diff options
585 files changed, 44365 insertions, 19173 deletions
diff --git a/libs/gui/CleanSpec.mk b/CleanSpec.mk index 5a5144c9df..6a9007c555 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 73360a3ff0..1a932c3d04 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -7,8 +7,10 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp libs/binder/ndk/ libs/graphicsenv/ libs/gui/ + libs/renderengine/ libs/ui/ libs/vr/ + services/bufferhub/ services/surfaceflinger/ services/vr/ diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 4459cef655..9b12a021f1 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -105,6 +105,8 @@ on late-init chmod 0666 /sys/kernel/tracing/events/mm_event/mm_event_record/enable chmod 0666 /sys/kernel/debug/tracing/events/lowmemorykiller/lowmemory_kill/enable chmod 0666 /sys/kernel/tracing/events/lowmemorykiller/lowmemory_kill/enable + chmod 0666 /sys/kernel/debug/tracing/events/oom/oom_score_adj_update/enable + chmod 0666 /sys/kernel/tracing/events/oom/oom_score_adj_update/enable # disk chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable diff --git a/cmds/atrace/atrace_userdebug.rc b/cmds/atrace/atrace_userdebug.rc index f4e5b9814c..5c28c9df81 100644 --- a/cmds/atrace/atrace_userdebug.rc +++ b/cmds/atrace/atrace_userdebug.rc @@ -9,8 +9,8 @@ on post-fs 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 75855cfee1..ded0ed35cb 100644 --- a/cmds/bugreportz/bugreportz.cpp +++ b/cmds/bugreportz/bugreportz.cpp @@ -55,7 +55,7 @@ int bugreportz(int s, bool show_progress) { errno = ETIMEDOUT; } printf("FAIL:Bugreport read terminated abnormally (%s)\n", strerror(errno)); - break; + return EXIT_FAILURE; } // Writes line by line. @@ -71,8 +71,5 @@ int bugreportz(int s, bool show_progress) { // 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 304e4b3dc3..7af289b2be 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 a3ae1ffa4d..74a95b0b57 100644 --- a/cmds/bugreportz/main.cpp +++ b/cmds/bugreportz/main.cpp @@ -82,7 +82,7 @@ int main(int argc, char* argv[]) { 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 @@ int main(int argc, char* argv[]) { 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 59a0047acf..516e3d8b73 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1436,6 +1436,12 @@ static void dumpstate() { 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"}); } /* Dumps state for the default case. Returns true if everything went fine. */ diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 8fbea8a163..4811927106 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -65,7 +65,7 @@ static void usage() { " -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 8f60881bfe..3ada15398c 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -371,8 +371,8 @@ TEST_F(DumpsysTest, PassAllFlagsToNormalServices) { 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/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 2439dff54e..81055d854e 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -94,15 +94,6 @@ static constexpr int kVerityPageSize = 4096; 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"; @@ -554,6 +545,35 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st remove_path_xattr(path, kXattrInodeCodeCache); } } + + auto extPath = findDataMediaPath(uuid, userId); + if (flags & FLAG_CLEAR_CACHE_ONLY) { + // Clear only cached data from shared storage + path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + } else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) { + // No code cache on shared storage + } else { + // Clear everything on shared storage + path = StringPrintf("%s/Android/sandbox/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); + if (delete_dir_contents(path, true) != 0) { + res = error("Failed to delete contents of " + path); + } + } } if (flags & FLAG_STORAGE_DE) { std::string suffix = ""; @@ -622,6 +642,24 @@ binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std:: if (delete_dir_contents_and_dir(path) != 0) { res = error("Failed to delete " + path); } + + auto extPath = findDataMediaPath(uuid, userId); + path = StringPrintf("%s/Android/sandbox/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete " + path); + } + path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete " + path); + } + path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete " + path); + } + path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); + if (delete_dir_contents_and_dir(path, true) != 0) { + res = error("Failed to delete " + path); + } } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); @@ -1413,6 +1451,8 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri } ATRACE_BEGIN("external"); + auto sandboxPath = create_data_media_package_path(uuid_, userId, "sandbox", pkgname); + calculate_tree_size(sandboxPath, &extStats.dataSize); auto extPath = create_data_media_package_path(uuid_, userId, "data", pkgname); collectManualStats(extPath, &extStats); auto mediaPath = create_data_media_package_path(uuid_, userId, "media", pkgname); diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 91e20b75ae..89b08e5ba8 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -102,4 +102,17 @@ interface IInstalld { boolean prepareAppProfile(@utf8InCpp String packageName, int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); + + const int FLAG_STORAGE_DE = 0x1; + const int FLAG_STORAGE_CE = 0x2; + + const int FLAG_CLEAR_CACHE_ONLY = 0x10; + const int FLAG_CLEAR_CODE_CACHE_ONLY = 0x20; + + const int FLAG_FREE_CACHE_V2 = 0x100; + const int FLAG_FREE_CACHE_V2_DEFY_QUOTA = 0x200; + const int FLAG_FREE_CACHE_NOOP = 0x400; + + const int FLAG_USE_QUOTA = 0x1000; + const int FLAG_FORCE = 0x2000; } diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index 2d58515b11..db0907017c 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -40,8 +40,8 @@ constexpr int64_t kMbInBytes = 1024 * kKbInBytes; 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 a5af5d740e..9dad99563d 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -37,7 +37,7 @@ namespace installd { 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/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index d776682d74..79cd6b5e1f 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -274,6 +274,7 @@ int svcmgr_handler(struct binder_state *bs, // Note that we ignore the strict_policy and don't propagate it // further (since we do no outbound RPCs anyway). strict_policy = bio_get_uint32(msg); + bio_get_uint32(msg); // Ignore worksource header. s = bio_get_string16(msg, &len); if (s == NULL) { return -1; diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index 4d93cb4c73..152ac28ba4 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -13,5 +13,6 @@ service servicemanager /system/bin/servicemanager onrestart restart cameraserver onrestart restart keystore onrestart restart gatekeeperd + onrestart restart thermalservice writepid /dev/cpuset/system-background/tasks shutdown critical diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto index 0bc08a91ab..c70bc3e5c1 100644 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ b/cmds/surfacereplayer/proto/src/trace.proto @@ -30,14 +30,13 @@ message Transaction { 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 @@ message SurfaceChange { OpaqueFlagChange opaque_flag = 13; SecureFlagChange secure_flag = 14; DeferredTransactionChange deferred_transaction = 15; + CornerRadiusChange corner_radius = 16; } } @@ -63,6 +63,10 @@ message AlphaChange { required float alpha = 1; } +message CornerRadiusChange { + required float corner_radius = 1; +} + message LayerChange { required uint32 layer = 1; } @@ -71,10 +75,6 @@ message CropChange { 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 VSyncEvent { 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 4140f40888..384f21ff17 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -385,12 +385,12 @@ status_t Replayer::doSurfaceTransaction( 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 +489,14 @@ void Replayer::setCrop(SurfaceComposerClient::Transaction& t, 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 +567,7 @@ void Replayer::setDeferredTransaction(SurfaceComposerClient::Transaction& t, 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, diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h index 295403eace..120dd9babd 100644 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ b/cmds/surfacereplayer/replayer/Replayer.h @@ -92,8 +92,8 @@ class Replayer { 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, diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py index a892e46816..d63d97f6b4 100644 --- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py +++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py @@ -172,7 +172,7 @@ def surface_delete(increment): 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 0000000000..7fa0bf9768 --- /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.hardware.biometrics.fingerprint.xml b/data/etc/android.hardware.biometrics.fingerprint.xml new file mode 100644 index 0000000000..e5af541e77 --- /dev/null +++ b/data/etc/android.hardware.biometrics.fingerprint.xml @@ -0,0 +1,21 @@ +<?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 fingerprint sensor. --> +<permissions> + <feature name="android.hardware.biometrics.fingerprint" /> +</permissions> diff --git a/data/etc/android.software.ipsec_tunnels.xml b/data/etc/android.software.ipsec_tunnels.xml new file mode 100644 index 0000000000..f7ffc02edd --- /dev/null +++ b/data/etc/android.software.ipsec_tunnels.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- + This is the feature indicating that the device has support for multinetworking-capable IPsec + tunnels +--> + +<permissions> + <feature name="android.software.ipsec_tunnels" /> +</permissions> diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 561f5ba9a6..d6021c007b 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -36,12 +36,15 @@ <feature name="android.hardware.type.automotive" /> <!-- basic system services --> - <feature name="android.software.app_widgets" /> <feature name="android.software.connectionservice" /> <feature name="android.software.voice_recognizers" notLowRam="true" /> <feature name="android.software.backup" /> <feature name="android.software.home_screen" /> + <feature name="android.software.input_methods" /> <feature name="android.software.print" /> + <feature name="android.software.companion_device_setup" /> + <feature name="android.software.autofill" /> + <feature name="android.software.cant_save_state" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> diff --git a/headers/media_plugin/media/drm/DrmAPI.h b/headers/media_plugin/media/drm/DrmAPI.h index c44a1f6152..aa8bd3d06c 100644 --- a/headers/media_plugin/media/drm/DrmAPI.h +++ b/headers/media_plugin/media/drm/DrmAPI.h @@ -167,6 +167,25 @@ namespace android { 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 inactive, the keys have been marked for + // release using getKeyRequest() with kKeyType_Release but the + // key response has not been received. + kOfflineLicenseStateInactive + }; + DrmPlugin() {} virtual ~DrmPlugin() {} diff --git a/headers/media_plugin/media/hardware/HardwareAPI.h b/headers/media_plugin/media/hardware/HardwareAPI.h index 6c1ba3de00..ae0220a5eb 100644 --- a/headers/media_plugin/media/hardware/HardwareAPI.h +++ b/headers/media_plugin/media/hardware/HardwareAPI.h @@ -425,7 +425,7 @@ struct DescribeColorAspectsParams { // 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 @@ struct DescribeHDRStaticInfoParams { 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 dc25deddbb..152015b67f 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_AMRDTXMODETYPE i, const char *def = 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 @@ inline static const char *asString(OMX_INDEXEXTTYPE i, const char *def = "??") { // 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"; diff --git a/headers/media_plugin/media/openmax/OMX_AudioExt.h b/headers/media_plugin/media/openmax/OMX_AudioExt.h index 8409553697..477faedef8 100644 --- a/headers/media_plugin/media/openmax/OMX_AudioExt.h +++ b/headers/media_plugin/media/openmax/OMX_AudioExt.h @@ -48,6 +48,7 @@ typedef enum OMX_AUDIO_CODINGEXTTYPE { 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 @@ typedef struct OMX_AUDIO_PARAM_ANDROID_EAC3TYPE { 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 @@ typedef struct OMX_AUDIO_PARAM_ANDROID_PROFILETYPE { 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 bb974b3f88..9ff934e4a0 100644 --- a/headers/media_plugin/media/openmax/OMX_Core.h +++ b/headers/media_plugin/media/openmax/OMX_Core.h @@ -542,6 +542,20 @@ typedef enum OMX_EVENTTYPE * 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 716d959979..479e9b8714 100644 --- a/headers/media_plugin/media/openmax/OMX_IndexExt.h +++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h @@ -64,6 +64,8 @@ typedef enum OMX_INDEXEXTTYPE { 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_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h index bbf157b889..435fcc8cca 100644 --- a/headers/media_plugin/media/openmax/OMX_VideoExt.h +++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h @@ -164,6 +164,8 @@ typedef enum OMX_VIDEO_VP9PROFILETYPE { // 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 @@ typedef enum OMX_VIDEO_HEVCPROFILETYPE { 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/input.h b/include/android/input.h index 681090188f..cfade6c806 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -83,7 +83,7 @@ enum { }; /** - * 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 59d67f35f2..cfd2b403b5 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -769,7 +769,13 @@ enum { /** 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, + /** 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/system_fonts.h b/include/android/system_fonts.h new file mode 100644 index 0000000000..38f036e4c0 --- /dev/null +++ b/include/android/system_fonts.h @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file system_fonts.h + * @brief Provides the system font configurations. + * + * These APIs provides the list of system installed font files with additional metadata about the + * font. + * + * The ASystemFontIterator_open method will give you an iterator which can iterate all system + * installed font files as shown in the following example. + * + * \code{.cpp} + * ASystemFontIterator* iterator = ASystemFontIterator_open(); + * ASystemFont* font = NULL; + * + * while ((font = ASystemFontIterator_next(iterator)) != nullptr) { + * // Look if the font is your desired one. + * if (ASystemFont_getWeight(font) == 400 && !ASystemFont_isItalic(font) + * && ASystemFont_getLocale(font) == NULL) { + * break; + * } + * ASystemFont_close(font); + * } + * ASystemFontIterator_close(iterator); + * + * int fd = open(ASystemFont_getFontFilePath(font), O_RDONLY); + * int collectionIndex = ASystemFont_getCollectionINdex(font); + * std::vector<std::pair<uint32_t, float>> variationSettings; + * for (size_t i = 0; i < ASystemFont_getAxisCount(font); ++i) { + * variationSettings.push_back(std::make_pair( + * ASystemFont_getAxisTag(font, i), + * ASystemFont_getAxisValue(font, i))); + * } + * ASystemFont_close(font); + * + * // Use this font for your text rendering engine. + * + * \endcode + * + * Available since API level 29. + */ + +#ifndef ANDROID_SYSTEM_FONTS_H +#define ANDROID_SYSTEM_FONTS_H + +#include <stdbool.h> +#include <stddef.h> +#include <sys/cdefs.h> + +/****************************************************************** + * + * IMPORTANT NOTICE: + * + * This file is part of Android's set of stable system headers + * exposed by the Android NDK (Native Development Kit). + * + * Third-party source AND binary code relies on the definitions + * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES. + * + * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES) + * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS + * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY + * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES + */ + +__BEGIN_DECLS + +#if __ANDROID_API__ >= 29 + +enum { + /** The minimum value fot the font weight value. */ + ASYSTEM_FONT_WEIGHT_MIN = 0, + + /** A font weight value for the thin weight. */ + ASYSTEM_FONT_WEIGHT_THIN = 100, + + /** A font weight value for the extra-light weight. */ + ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT = 200, + + /** A font weight value for the light weight. */ + ASYSTEM_FONT_WEIGHT_LIGHT = 300, + + /** A font weight value for the normal weight. */ + ASYSTEM_FONT_WEIGHT_NORMAL = 400, + + /** A font weight value for the medium weight. */ + ASYSTEM_FONT_WEIGHT_MEDIUM = 500, + + /** A font weight value for the semi-bold weight. */ + ASYSTEM_FONT_WEIGHT_SEMI_BOLD = 600, + + /** A font weight value for the bold weight. */ + ASYSTEM_FONT_WEIGHT_BOLD = 700, + + /** A font weight value for the extra-bold weight. */ + ASYSTEM_FONT_WEIGHT_EXTRA_BOLD = 800, + + /** A font weight value for the black weight. */ + ASYSTEM_FONT_WEIGHT_BLACK = 900, + + /** The maximum value for the font weight value. */ + ASYSTEM_FONT_WEIGHT_MAX = 1000 +}; + +/** + * ASystemFontIterator provides access to the system font configuration. + * + * ASystemFontIterator is an iterator for all available system font settings. + * This iterator is not a thread-safe object. Do not pass this iterator to other threads. + */ +struct ASystemFontIterator; + +/** + * ASystemFont provides information of the single system font configuration. + */ +struct ASystemFont; + +/** + * Create a system font iterator. + * + * Use ASystemFont_close() to close the iterator. + * + * \return a pointer for a newly allocated iterator, nullptr on failure. + */ +ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29); + +/** + * Close an opened system font iterator, freeing any related resources. + * + * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed. + */ +void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29); + +/** + * Move to the next system font. + * + * \param iterator an iterator for the system fonts. Passing NULL is not allowed. + * \return a font. If no more font is available, returns nullptr. You need to release the returned + * font by ASystemFont_close when it is no longer needed. + */ +ASystemFont* _Nullable ASystemFontIterator_next(ASystemFontIterator* _Nonnull iterator) __INTRODUCED_IN(29); + +/** + * Close an ASystemFont returned by ASystemFontIterator_next. + * + * \param font a font returned by ASystemFontIterator_next or ASystemFont_matchFamilyStyleCharacter. + * Do nothing if NULL is passed. + */ +void ASystemFont_close(ASystemFont* _Nullable font) __INTRODUCED_IN(29); + + +/** + * Select the best font from given parameters. + * + * Only generic font families are supported. + * For more information about generic font families, read [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families) + * + * Even if no font can render the given text, this function will return a non-null result for + * drawing Tofu character. + * + * Examples: + * \code{.cpp} + * // Simple font query for the ASCII character. + * std::vector<uint16_t> text = { 'A' }; + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "en-US", text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a valid font file. + * + * // Querying font for CJK characters + * std::vector<uint16_t> text = { 0x9AA8 }; + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "zh-CN,ja-JP", text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Simplified Chinese font. + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "ja-JP,zh-CN", text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Japanese font. + * + * // Querying font for text/color emoji + * std::vector<uint16_t> text = { 0xD83D, 0xDC68, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 }; + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "en-US", text.data(), text.length(), &runLength); + * // runLength will be 8 and the font will points a color emoji font. + * + * // Mixture of multiple script of characters. + * // 0x05D0 is a Hebrew character and 0x0E01 is a Thai character. + * std::vector<uint16_t> text = { 0x05D0, 0x0E01 }; + * ASystemFont font = ASystemFont_matchFamilyStyleCharacter( + * "sans", 400, false, "en-US", text.data(), text.length(), &runLength); + * // runLength will be 1 and the font will points a Hebrew font. + * \endcode + * + * \param familyName a null character terminated font family name + * \param weight a font weight value. Only from 0 to 1000 value is valid + * \param italic true if italic, otherwise false. + * \param languageTags a null character terminated comma separated IETF BCP47 compliant language + * tags. + * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string. + * \param textLength a length of the given text buffer. This must not be zero. + * \param runLengthOut if not null, the font run length will be filled. + * \return a font to be used for given text and params. You need to release the returned font by + * ASystemFont_close when it is no longer needed. + */ +ASystemFont* _Nonnull ASystemFont_matchFamilyStyleCharacter( + const char* _Nonnull familyName, + uint16_t weight, + bool italic, + const char* _Nonnull languageTags, + const uint16_t* _Nonnull text, + uint32_t textLength, + uint32_t* _Nullable runLengthOut) __INTRODUCED_IN(29); + +/** + * Return an absolute path to the current font file. + * + * Here is a list of font formats returned by this method: + * <ul> + * <li>OpenType</li> + * <li>OpenType Font Collection</li> + * <li>TrueType</li> + * <li>TrueType Collection</li> + * </ul> + * The file extension could be one of *.otf, *.ttf, *.otc or *.ttc. + * + * The font file returned is guaranteed to be opend with O_RDONLY. + * Note that the returned pointer is valid until ASystemFont_close() is called for the given font. + * + * \param font a font object. Passing NULL is not allowed. + * \return a string of the font file path. + */ +const char* _Nonnull ASystemFont_getFontFilePath(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a weight value associated with the current font. + * + * The weight values are positive and less than or equal to 1000. + * Here are pairs of the common names and their values. + * <p> + * <table> + * <tr> + * <th align="center">Value</th> + * <th align="center">Name</th> + * <th align="center">NDK Definition</th> + * </tr> + * <tr> + * <td align="center">100</td> + * <td align="center">Thin</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_THIN}</td> + * </tr> + * <tr> + * <td align="center">200</td> + * <td align="center">Extra Light (Ultra Light)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_LIGHT}</td> + * </tr> + * <tr> + * <td align="center">300</td> + * <td align="center">Light</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_LIGHT}</td> + * </tr> + * <tr> + * <td align="center">400</td> + * <td align="center">Normal (Regular)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_NORMAL}</td> + * </tr> + * <tr> + * <td align="center">500</td> + * <td align="center">Medium</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_MEDIUM}</td> + * </tr> + * <tr> + * <td align="center">600</td> + * <td align="center">Semi Bold (Demi Bold)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_SEMI_BOLD}</td> + * </tr> + * <tr> + * <td align="center">700</td> + * <td align="center">Bold</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_BOLD}</td> + * </tr> + * <tr> + * <td align="center">800</td> + * <td align="center">Extra Bold (Ultra Bold)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_EXTRA_BOLD}</td> + * </tr> + * <tr> + * <td align="center">900</td> + * <td align="center">Black (Heavy)</td> + * <td align="center">{@link ASYSTEM_FONT_WEIGHT_BLACK}</td> + * </tr> + * </table> + * </p> + * Note that the weight value may fall in between above values, e.g. 250 weight. + * + * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass) + * + * \param font a font object. Passing NULL is not allowed. + * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned. + */ +uint16_t ASystemFont_getWeight(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return true if the current font is italic, otherwise returns false. + * + * \param font a font object. Passing NULL is not allowed. + * \return true if italic, otherwise false. + */ +bool ASystemFont_isItalic(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a IETF BCP47 compliant language tag associated with the current font. + * + * For information about IETF BCP47, read [Locale.forLanguageTag(java.lang.String)](https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)") + * + * Note that the returned pointer is valid until ASystemFont_close() is called. + * + * \param font a font object. Passing NULL is not allowed. + * \return a IETF BCP47 compliant langauge tag or nullptr if not available. + */ +const char* _Nullable ASystemFont_getLocale(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a font collection index value associated with the current font. + * + * In case the target font file is a font collection (e.g. .ttc or .otc), this + * returns a non-negative value as an font offset in the collection. This + * always returns 0 if the target font file is a regular font. + * + * \param font a font object. Passing NULL is not allowed. + * \return a font collection index. + */ +size_t ASystemFont_getCollectionIndex(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + +/** + * Return a count of font variation settings associated with the current font + * + * The font variation settings are provided as multiple tag-values pairs. + * + * For example, bold italic font may have following font variation settings: + * 'wght' 700, 'slnt' -12 + * In this case, ASystemFont_getAxisCount returns 2 and ASystemFont_getAxisTag + * and ASystemFont_getAxisValue will return following values. + * \code{.cpp} + * ASystemFont* font = ASystemFontIterator_next(ite); + * + * // Returns the number of axes + * ASystemFont_getAxisCount(font); // Returns 2 + * + * // Returns the tag-value pair for the first axis. + * ASystemFont_getAxisTag(font, 0); // Returns 'wght'(0x77676874) + * ASystemFont_getAxisValue(font, 0); // Returns 700.0 + * + * // Returns the tag-value pair for the second axis. + * ASystemFont_getAxisTag(font, 1); // Returns 'slnt'(0x736c6e74) + * ASystemFont_getAxisValue(font, 1); // Returns -12.0 + * \endcode + * + * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) + * + * \param font a font object. Passing NULL is not allowed. + * \return a number of font variation settings. + */ +size_t ASystemFont_getAxisCount(const ASystemFont* _Nonnull font) __INTRODUCED_IN(29); + + +/** + * Return an OpenType axis tag associated with the current font. + * + * See ASystemFont_getAxisCount for more details. + * + * \param font a font object. Passing NULL is not allowed. + * \param axisIndex an index to the font variation settings. Passing value larger than or + * equal to {@link ASystemFont_getAxisCount} is not allowed. + * \return an OpenType axis tag value for the given font variation setting. + */ +uint32_t ASystemFont_getAxisTag(const ASystemFont* _Nonnull font, uint32_t axisIndex) + __INTRODUCED_IN(29); + +/** + * Return an OpenType axis value associated with the current font. + * + * See ASystemFont_getAxisCount for more details. + * + * \param font a font object. Passing NULL is not allowed. + * \param axisIndex an index to the font variation settings. Passing value larger than or + * equal to {@link ASYstemFont_getAxisCount} is not allwed. + * \return a float value for the given font variation setting. + */ +float ASystemFont_getAxisValue(const ASystemFont* _Nonnull font, uint32_t axisIndex) + __INTRODUCED_IN(29); + +#endif // __ANDROID_API__ >= 29 + +__END_DECLS + +#endif // ANDROID_SYSTEM_FONTS_H diff --git a/include/android/trace.h b/include/android/trace.h index aa24995cae..bb7ff28f79 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 @@ void ATrace_endSection() __INTRODUCED_IN(23); #endif /* __ANDROID_API__ >= 23 */ +#if __ANDROID_API__ >= __ANDROID_API_Q__ + +/** + * Writes a trace message to indicate that a given section of code has + * begun. Must be followed by a call to {@link ATrace_endAsyncSection} with the same + * methodName and cookie. Unlike {@link ATrace_beginSection} and {@link ATrace_endSection}, + * asynchronous events do not need to be nested. The name and cookie used to + * begin an event must be used to end it. + * + * \param sectionName The method name to appear in the trace. + * \param cookie Unique identifier for distinguishing simultaneous events + */ +void ATrace_beginAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29); + +/** + * Writes a trace message to indicate that the current method has ended. + * Must be called exactly once for each call to {@link ATrace_beginAsyncSection} + * using the same name and cookie. + * + * \param methodName The method name to appear in the trace. + * \param cookie Unique identifier for distinguishing simultaneous events + */ +void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCED_IN(29); + +/** + * Writes trace message to indicate the value of a given counter. + * + * \param counterName The counter name to appear in the trace. + * \param counterValue The counter value. + */ +void ATrace_setCounter(const char* counterName, int64_t counterValue) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ + #ifdef __cplusplus }; #endif diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 86da4d38e8..fa456bb213 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 @@ struct DisplayViewport { 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 @@ struct DisplayViewport { && 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 @@ struct DisplayViewport { 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 11bb7215d6..610834d23e 100644 --- a/include/input/IInputFlinger.h +++ b/include/input/IInputFlinger.h @@ -22,6 +22,9 @@ #include <binder/IInterface.h> +#include <utils/Vector.h> +#include <input/InputWindow.h> + namespace android { /* @@ -31,6 +34,11 @@ namespace android { class IInputFlinger : public IInterface { public: DECLARE_META_INTERFACE(InputFlinger) + + virtual void setInputWindows(const Vector<InputWindowInfo>& inputHandles) = 0; + + virtual void registerInputChannel(const sp<InputChannel>& channel) = 0; + virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0; }; @@ -40,7 +48,9 @@ public: 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 }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/include/input/Input.h b/include/input/Input.h index 756b337bc8..037270c096 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 @@ enum { */ 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, }; @@ -237,7 +244,12 @@ struct PointerCoords { 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 +314,18 @@ public: 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 +357,11 @@ public: 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, @@ -556,6 +575,7 @@ public: void initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, @@ -580,7 +600,7 @@ public: 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. diff --git a/services/inputflinger/InputApplication.h b/include/input/InputApplication.h index 724fc2c4f4..71a8f20468 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 @@ namespace android { * Describes the properties of an application that can receive input. */ struct InputApplicationInfo { + sp<IBinder> token; std::string name; nsecs_t dispatchingTimeout; + + status_t write(Parcel& output) const; + static InputApplicationInfo read(const Parcel& from); }; @@ -52,6 +61,10 @@ public: return mInfo ? mInfo->dispatchingTimeout : defaultValue; } + inline sp<IBinder> getApplicationToken() const { + return mInfo ? mInfo->token : nullptr; + } + /** * Requests that the state of this object be updated to reflect * the most current available information about the application. diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 1ea69d352d..34d164c280 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -31,9 +31,9 @@ struct InputDeviceIdentifier { } // 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,7 +45,7 @@ struct InputDeviceIdentifier { // 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 @@ -73,16 +73,16 @@ public: }; 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; } @@ -121,7 +121,7 @@ private: int32_t mGeneration; int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; - String8 mAlias; + std::string mAlias; bool mIsExternal; bool mHasMic; uint32_t mSources; @@ -149,7 +149,7 @@ enum InputDeviceConfigurationFileType { * * Returns an empty string if not found. */ -extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( +extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type); @@ -162,8 +162,8 @@ extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( * * 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); } // namespace android diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 4b33a96adf..59d16d15af 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -325,8 +325,11 @@ static const InputEventLabel KEYCODES[] = { 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 @@ static const InputEventLabel AXES[] = { // 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 @@ static const InputEventLabel LEDS[] = { 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 @@ static const InputEventLabel FLAGS[] = { DEFINE_FLAG(FUNCTION), DEFINE_FLAG(GESTURE), - { NULL, 0 } + { nullptr, 0 } }; static int lookupValueByLabel(const char* literal, const InputEventLabel *list) { @@ -424,7 +427,7 @@ static const char* lookupLabelByValue(int value, const InputEventLabel* list) { } list++; } - return NULL; + return nullptr; } static inline int32_t getKeyCodeByLabel(const char* label) { @@ -435,7 +438,7 @@ static inline const char* getLabelByKeyCode(int32_t keyCode) { 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 1ea2c2cc07..dddbfb04da 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,12 +40,20 @@ #include <utils/BitSet.h> namespace android { +class Parcel; /* * Intermediate representation used to send input events and related signals. * * Note that this structure is used for IPCs so its layout must be identical * on 64 and 32 bit processes. This is tested in StructLayout_test.cpp. + * + * Since the struct must be aligned to an 8-byte boundary, there could be uninitialized bytes + * in-between the defined fields. This padding data should be explicitly accounted for by adding + * "empty" fields into the struct. This data is memset to zero before sending the struct across + * the socket. Adding the explicit fields ensures that the memset is not optimized away by the + * compiler. When a new field is added to the struct, the corresponding change + * in StructLayout_test should be made. */ struct InputMessage { enum { @@ -61,6 +74,7 @@ struct InputMessage { union Body { struct Key { uint32_t seq; + uint32_t empty1; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -71,6 +85,7 @@ struct InputMessage { int32_t scanCode; int32_t metaState; int32_t repeatCount; + uint32_t empty2; nsecs_t downTime __attribute__((aligned(8))); inline size_t size() const { @@ -80,6 +95,7 @@ struct InputMessage { struct Motion { uint32_t seq; + uint32_t empty1; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -90,12 +106,14 @@ struct InputMessage { int32_t metaState; int32_t buttonState; int32_t edgeFlags; + uint32_t empty2; nsecs_t downTime __attribute__((aligned(8))); float xOffset; float yOffset; float xPrecision; float yPrecision; uint32_t pointerCount; + uint32_t empty3; // Note that PointerCoords requires 8 byte alignment. struct Pointer { PointerProperties properties; @@ -126,6 +144,7 @@ struct InputMessage { bool isValid(size_t actualSize) const; size_t size() const; + void getSanitizedCopy(InputMessage* msg) const; }; /* @@ -141,6 +160,7 @@ protected: virtual ~InputChannel(); public: + InputChannel() = default; InputChannel(const std::string& name, int fd); /* Creates a pair of input channels. @@ -181,9 +201,19 @@ public: /* 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; }; /* @@ -212,6 +242,7 @@ public: uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -305,7 +336,7 @@ public: * 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 @@ -460,10 +491,9 @@ private: 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 index 5a48375910..2b8cc57c05 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, @@ -113,17 +116,42 @@ struct InputWindowInfo { INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002, 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,7 @@ struct InputWindowInfo { int32_t ownerUid; int32_t inputFeatures; int32_t displayId; + InputApplicationInfo applicationInfo; void addTouchableRegion(const Rect& region); @@ -151,6 +180,9 @@ struct InputWindowInfo { bool supportsSplitTouch() const; bool overlaps(const InputWindowInfo* other) const; + + status_t write(Parcel& output) const; + static InputWindowInfo read(const Parcel& from); }; @@ -162,22 +194,23 @@ struct InputWindowInfo { */ 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 +225,21 @@ public: 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 5e542b87ec..a1a32a63de 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 @@ public: }; /* 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 @@ private: 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 1e8de7173b..73815fe8b4 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -62,7 +62,7 @@ struct AxisInfo { */ 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; diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index d4903e98df..8b66f693cc 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -21,7 +21,6 @@ #include <input/InputDevice.h> #include <input/InputEventLabels.h> #include <utils/Errors.h> -#include <utils/String8.h> #include <utils/PropertyMap.h> namespace android { @@ -43,10 +42,10 @@ class KeyCharacterMap; */ class KeyMap { public: - String8 keyLayoutFile; + std::string keyLayoutFile; sp<KeyLayoutMap> keyLayoutMap; - String8 keyCharacterMapFile; + std::string keyCharacterMapFile; sp<KeyCharacterMap> keyCharacterMap; KeyMap(); @@ -56,11 +55,11 @@ public: 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 +67,12 @@ public: } 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 0000000000..d68f27431a --- /dev/null +++ b/include/input/TouchVideoFrame.h @@ -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. + */ + +#ifndef _LIBINPUT_TOUCHVIDEOFRAME_H +#define _LIBINPUT_TOUCHVIDEOFRAME_H + +#include <stdint.h> +#include <sys/time.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 width, uint32_t height, std::vector<int16_t> data, + const struct timeval& timestamp) : + mWidth(width), mHeight(height), mData(std::move(data)), mTimestamp(timestamp) { + } + + /** + * Width of the frame + */ + uint32_t getWidth() const { return mWidth; } + /** + * Height of the frame + */ + uint32_t getHeight() const { return mHeight; } + /** + * 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 { return mData; } + /** + * Time at which the heatmap was taken. + */ + const struct timeval& getTimestamp() const { return mTimestamp; } + +private: + uint32_t mWidth; + uint32_t mHeight; + std::vector<int16_t> mData; + struct timeval mTimestamp; +}; + +} // namespace android + +#endif // _LIBINPUT_TOUCHVIDEOFRAME_H diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index ffa1614b55..727865a933 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -63,7 +63,7 @@ public: // 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 e245ead682..24e0e0ed9e 100644 --- a/include/input/VirtualKeyMap.h +++ b/include/input/VirtualKeyMap.h @@ -23,7 +23,6 @@ #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Tokenizer.h> -#include <utils/String8.h> #include <utils/Unicode.h> namespace android { @@ -50,7 +49,7 @@ class VirtualKeyMap { public: ~VirtualKeyMap(); - static status_t load(const String8& filename, VirtualKeyMap** outMap); + static status_t load(const std::string& filename, VirtualKeyMap** outMap); inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const { return mVirtualKeys; diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp index 28d0e4f9c3..49a94146db 100644 --- a/libs/binder/ActivityManager.cpp +++ b/libs/binder/ActivityManager.cpp @@ -89,6 +89,15 @@ bool ActivityManager::isUidActive(const uid_t uid, const String16& callingPackag 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 da10687476..aedf6b0d18 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -107,6 +107,7 @@ cc_library_shared { "-Wall", "-Wextra", "-Werror", + "-Wzero-as-null-pointer-constant", ], product_variables: { binder32bit: { diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index 428db4d579..377f604d44 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 @@ public: if (reply.readExceptionCode() != 0) return false; return reply.readInt32() == 1; } + + virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) + { + Parcel data, reply; + data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor()); + data.writeInt32(uid); + data.writeString16(callingPackage); + remote()->transact(GET_UID_PROCESS_STATE_TRANSACTION, data, &reply); + // fail on exception + if (reply.readExceptionCode() != 0) { + return ActivityManager::PROCESS_STATE_UNKNOWN; + } + return reply.readInt32(); + } }; // ------------------------------------------------------------------------------------ diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index 307bc28c0e..caf2318281 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -86,7 +86,7 @@ public: 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 @@ private: 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 @@ sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const 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 @@ status_t BnMemory::onTransact( 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 @@ void BpMemoryHeap::assertReallyMapped() const 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 @@ void BpMemoryHeap::assertReallyMapped() const 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 @@ uint32_t BpMemoryHeap::getFlags() const { return mFlags; } -uint32_t BpMemoryHeap::getOffset() const { +off_t BpMemoryHeap::getOffset() const { assertMapped(); return mOffset; } @@ -394,9 +402,9 @@ status_t BnMemoryHeap::onTransact( 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 f052bcb982..8df83f1afe 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -109,6 +109,8 @@ static const char *kCommandStrings[] = { "BC_DEAD_BINDER_DONE" }; +static const int64_t kWorkSourcePropagatedBitIndex = 32; + static const char* getReturnString(uint32_t cmd) { size_t idx = cmd & 0xff; @@ -383,6 +385,48 @@ int32_t IPCThreadState::getStrictModePolicy() const 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; @@ -736,6 +780,8 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), + mWorkSource(kUnsetWorkSource), + mPropagateWorkSource(false), mStrictModePolicy(0), mLastTransactionBinderFlags(0) { @@ -1098,6 +1144,13 @@ status_t IPCThreadState::executeCommand(int32_t cmd) 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; mCallingUid = tr.sender_euid; @@ -1150,6 +1203,8 @@ status_t IPCThreadState::executeCommand(int32_t cmd) mCallingUid = origUid; mStrictModePolicy = origStrictModePolicy; mLastTransactionBinderFlags = origTransactionBinderFlags; + mWorkSource = origWorkSource; + mPropagateWorkSource = origPropagateWorkSet; IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp index 697e948a6d..82f9047595 100644 --- a/libs/binder/IUidObserver.cpp +++ b/libs/binder/IUidObserver.cpp @@ -55,6 +55,16 @@ public: 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 @@ status_t BnUidObserver::onTransact( 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 9850ad9624..4c300b47c6 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -76,7 +76,7 @@ MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) } } -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 @@ MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t off 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 @@ status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const cha 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 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) 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 @@ void MemoryHeapBase::dispose() 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 @@ const char* MemoryHeapBase::getDevice() const { 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 b2db945af0..d285030c1b 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -74,7 +74,7 @@ static size_t pad_size(size_t s) { } // Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER -#define STRICT_MODE_PENALTY_GATHER (0x40 << 16) +#define STRICT_MODE_PENALTY_GATHER (1 << 31) // XXX This can be made public if we want to provide // support for typed data. @@ -599,8 +599,10 @@ bool Parcel::hasFileDescriptors() const // 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); + writeInt32(threadState->shouldPropagateWorkSource() ? + threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); // currently the interface identification token is just its name as a string return writeString16(interface); } @@ -613,6 +615,7 @@ bool Parcel::checkInterface(IBinder* binder) const bool Parcel::enforceInterface(const String16& interface, IPCThreadState* threadState) const { + // StrictModePolicy. int32_t strictPolicy = readInt32(); if (threadState == nullptr) { threadState = IPCThreadState::self(); @@ -627,6 +630,10 @@ bool Parcel::enforceInterface(const String16& interface, } else { threadState->setStrictModePolicy(strictPolicy); } + // WorkSource. + int32_t workSource = readInt32(); + threadState->setCallingWorkSourceUidWithoutPropagation(workSource); + // Interface descriptor. const String16 str(readString16()); if (str == interface) { return true; @@ -871,6 +878,16 @@ status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& v 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); @@ -1732,6 +1749,14 @@ status_t Parcel::readInt64Vector(std::vector<int64_t>* val) const { 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); } @@ -2329,6 +2354,15 @@ status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const 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; diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h index b8db09145f..26dafd0a12 100644 --- a/libs/binder/include/binder/ActivityManager.h +++ b/libs/binder/include/binder/ActivityManager.h @@ -31,6 +31,8 @@ class ActivityManager 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,27 @@ public: }; 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 = 3, + PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 4, + PROCESS_STATE_IMPORTANT_FOREGROUND = 5, + PROCESS_STATE_IMPORTANT_BACKGROUND = 6, + PROCESS_STATE_TRANSIENT_BACKGROUND = 7, + PROCESS_STATE_BACKUP = 8, + PROCESS_STATE_SERVICE = 9, + PROCESS_STATE_RECEIVER = 10, + PROCESS_STATE_TOP_SLEEPING = 11, + PROCESS_STATE_HEAVY_WEIGHT = 12, + PROCESS_STATE_HOME = 13, + PROCESS_STATE_LAST_ACTIVITY = 14, + PROCESS_STATE_CACHED_ACTIVITY = 15, + PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 16, + PROCESS_STATE_CACHED_RECENT = 17, + PROCESS_STATE_CACHED_EMPTY = 18, + PROCESS_STATE_NONEXISTENT = 19, }; ActivityManager(); @@ -53,8 +74,10 @@ public: 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 c5b57c7edf..37237dfd48 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -95,6 +95,20 @@ public: OP_USE_FINGERPRINT = 55, OP_BODY_SENSORS = 56, OP_AUDIO_ACCESSIBILITY_VOLUME = 64, + OP_READ_PHONE_NUMBERS = 65, + OP_REQUEST_INSTALL_PACKAGES = 66, + OP_PICTURE_IN_PICTURE = 67, + OP_INSTANT_APP_START_FOREGROUND = 68, + OP_ANSWER_PHONE_CALLS = 69, + OP_RUN_ANY_IN_BACKGROUND = 70, + OP_CHANGE_WIFI_STATE = 71, + OP_REQUEST_DELETE_PACKAGES = 72, + OP_BIND_ACCESSIBILITY_SERVICE = 73, + OP_ACCEPT_HANDOVER = 74, + OP_MANAGE_IPSEC_TUNNELS = 75, + OP_START_FOREGROUND = 76, + OP_BLUETOOTH_SCAN = 77, + OP_USE_BIOMETRIC = 78, }; AppOpsManager(); diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h index f34969be51..6abc071c45 100644 --- a/libs/binder/include/binder/IActivityManager.h +++ b/libs/binder/include/binder/IActivityManager.h @@ -38,12 +38,14 @@ public: const String16& callingPackage) = 0; virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0; virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0; + virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0; enum { OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_UID_OBSERVER_TRANSACTION, UNREGISTER_UID_OBSERVER_TRANSACTION, - IS_UID_ACTIVE_TRANSACTION + IS_UID_ACTIVE_TRANSACTION, + GET_UID_PROCESS_STATE_TRANSACTION }; }; diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 14edcbe72a..1674516ca2 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -47,7 +47,7 @@ class IShellCallback; * (method calls, property get and set) is down through a low-level * protocol implemented on top of the transact() API. */ -class IBinder : public virtual RefBase +class [[clang::lto_visibility_public]] IBinder : public virtual RefBase { public: enum { diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h index db9f53a797..071946f50c 100644 --- a/libs/binder/include/binder/IMemory.h +++ b/libs/binder/include/binder/IMemory.h @@ -43,7 +43,7 @@ public: 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 40b51adb06..26e8c0b599 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -47,6 +47,19 @@ public: 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; @@ -118,6 +131,13 @@ public: // 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(); @@ -155,6 +175,11 @@ private: status_t mLastError; pid_t mCallingPid; 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 e5d8ea67de..aeea1a2676 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -88,7 +88,7 @@ status_t getService(const String16& name, sp<INTERFACE>* outService) 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 9937ad6a29..a1f530dc71 100644 --- a/libs/binder/include/binder/IUidObserver.h +++ b/libs/binder/include/binder/IUidObserver.h @@ -34,11 +34,13 @@ public: 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 5e0a382a7e..2f5039dcc9 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -42,7 +42,7 @@ public: * 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 @@ public: 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 @@ public: 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 @@ private: 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 c9c273acd8..cd151ee509 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -139,6 +139,8 @@ public: status_t writeInt32Vector(const std::vector<int32_t>& val); status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val); status_t writeInt64Vector(const std::vector<int64_t>& val); + status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val); + status_t writeUint64Vector(const std::vector<uint64_t>& val); status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val); status_t writeFloatVector(const std::vector<float>& val); status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val); @@ -313,6 +315,8 @@ public: 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; diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 2dd86ba7e1..ae04b0f5a1 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -71,6 +71,8 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, + BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, + BINDER_LIB_TEST_ECHO_VECTOR, }; pid_t start_server_process(int arg2, bool usePoll = false) @@ -179,6 +181,7 @@ class BinderLibTest : public ::testing::Test { public: virtual void SetUp() { m_server = static_cast<BinderLibTestEnv *>(binder_env)->getServer(); + IPCThreadState::self()->restoreCallingWorkSource(0); } virtual void TearDown() { } @@ -938,6 +941,141 @@ TEST_F(BinderLibTest, OnewayQueueing) EXPECT_EQ(NO_ERROR, ret); } +TEST_F(BinderLibTest, WorkSourceUnsetByDefault) +{ + status_t ret; + Parcel data, reply; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceSet) +{ + status_t ret; + Parcel data, reply; + IPCThreadState::self()->clearCallingWorkSource(); + int64_t previousWorkSource = IPCThreadState::self()->setCallingWorkSourceUid(100); + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(100, reply.readInt32()); + EXPECT_EQ(-1, previousWorkSource); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceSetWithoutPropagation) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUidWithoutPropagation(100); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); + + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceCleared) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + int64_t token = IPCThreadState::self()->clearCallingWorkSource(); + int32_t previousWorkSource = (int32_t)token; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + EXPECT_EQ(-1, reply.readInt32()); + EXPECT_EQ(100, previousWorkSource); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, WorkSourceRestored) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + int64_t token = IPCThreadState::self()->clearCallingWorkSource(); + IPCThreadState::self()->restoreCallingWorkSource(token); + + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + EXPECT_EQ(100, reply.readInt32()); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); + EXPECT_EQ(NO_ERROR, ret); +} + +TEST_F(BinderLibTest, PropagateFlagSet) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->clearPropagateWorkSource(); + IPCThreadState::self()->setCallingWorkSourceUid(100); + EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, PropagateFlagCleared) +{ + status_t ret; + Parcel data, reply; + + IPCThreadState::self()->setCallingWorkSourceUid(100); + IPCThreadState::self()->clearPropagateWorkSource(); + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, PropagateFlagRestored) +{ + status_t ret; + Parcel data, reply; + + int token = IPCThreadState::self()->setCallingWorkSourceUid(100); + IPCThreadState::self()->restoreCallingWorkSource(token); + + EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); +} + +TEST_F(BinderLibTest, WorkSourcePropagatedForAllFollowingBinderCalls) +{ + IPCThreadState::self()->setCallingWorkSourceUid(100); + + Parcel data, reply; + status_t ret; + data.writeInterfaceToken(binderLibTestServiceName); + ret = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data, &reply); + + Parcel data2, reply2; + status_t ret2; + data2.writeInterfaceToken(binderLibTestServiceName); + ret2 = m_server->transact(BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, data2, &reply2); + EXPECT_EQ(100, reply2.readInt32()); + EXPECT_EQ(NO_ERROR, ret2); +} + +TEST_F(BinderLibTest, VectorSent) { + Parcel data, reply; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + std::vector<uint64_t> const testValue = { std::numeric_limits<uint64_t>::max(), 0, 200 }; + data.writeUint64Vector(testValue); + + status_t ret = server->transact(BINDER_LIB_TEST_ECHO_VECTOR, data, &reply); + EXPECT_EQ(NO_ERROR, ret); + std::vector<uint64_t> readValue; + ret = reply.readUint64Vector(&readValue); + EXPECT_EQ(readValue, testValue); +} + class BinderLibTestService : public BBinder { public: @@ -1236,6 +1374,19 @@ class BinderLibTestService : public BBinder } 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/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 35296a96c1..04884bb3ec 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -46,6 +46,7 @@ static const char* hal_interfaces_to_dump[] { "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.drm@1.0::IDrmFactory", + "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", "android.hardware.health@2.0::IHealth", "android.hardware.media.omx@1.0::IOmx", diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index 4da30e9980..280c14a3ea 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -22,6 +22,8 @@ cc_library_shared { cflags: ["-Wall", "-Werror"], shared_libs: [ + "libbase", + "libcutils", "liblog", ], diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index a8ef7a051d..9dc7431714 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -18,51 +18,330 @@ #define LOG_TAG "GraphicsEnv" #include <graphicsenv/GraphicsEnv.h> -#include <mutex> +#include <dlfcn.h> +#include <unistd.h> +#include <android-base/file.h> +#include <android-base/properties.h> +#include <android-base/strings.h> #include <android/dlext.h> +#include <cutils/properties.h> #include <log/log.h> +#include <sys/prctl.h> + +#include <memory> +#include <mutex> +#include <string> + +#include <dlfcn.h> // TODO(b/37049319) Get this from a header once one exists extern "C" { - android_namespace_t* android_get_exported_namespace(const char*); - android_namespace_t* android_create_namespace(const char* name, - const char* ld_library_path, - const char* default_library_path, - uint64_t type, - const char* permitted_when_isolated_path, - android_namespace_t* parent); +android_namespace_t* android_get_exported_namespace(const char*); +android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, + const char* default_library_path, uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent); +bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to, + const char* shared_libs_sonames); - enum { - ANDROID_NAMESPACE_TYPE_ISOLATED = 1, - ANDROID_NAMESPACE_TYPE_SHARED = 2, - }; +enum { + ANDROID_NAMESPACE_TYPE_ISOLATED = 1, + ANDROID_NAMESPACE_TYPE_SHARED = 2, +}; } +// TODO(ianelliott@): Get the following from an ANGLE header: +#define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting +// Version-2 API: +typedef bool (*fpANGLEGetFeatureSupportUtilAPIVersion)(unsigned int* versionToUse); +typedef bool (*fpANGLEAndroidParseRulesString)(const char* rulesString, void** rulesHandle, + int* rulesVersion); +typedef bool (*fpANGLEGetSystemInfo)(void** handle); +typedef bool (*fpANGLEAddDeviceInfoToSystemInfo)(const char* deviceMfr, const char* deviceModel, + void* handle); +typedef bool (*fpANGLEShouldBeUsedForApplication)(void* rulesHandle, int rulesVersion, + void* systemInfoHandle, const char* appName); +typedef bool (*fpANGLEFreeRulesHandle)(void* handle); +typedef bool (*fpANGLEFreeSystemInfoHandle)(void* handle); + namespace android { +enum NativeLibrary { + LLNDK = 0, + VNDKSP = 1, +}; + +static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt", + "/etc/vndksp.libraries.txt"}; + +static std::string vndkVersionStr() { +#ifdef __BIONIC__ + std::string version = android::base::GetProperty("ro.vndk.version", ""); + if (version != "" && version != "current") { + return "." + version; + } +#endif + return ""; +} + +static void insertVndkVersionStr(std::string* fileName) { + LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr"); + size_t insertPos = fileName->find_last_of("."); + if (insertPos == std::string::npos) { + insertPos = fileName->length(); + } + fileName->insert(insertPos, vndkVersionStr()); +} + +static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) { + // Read list of public native libraries from the config file. + std::string fileContent; + if (!base::ReadFileToString(configFile, &fileContent)) { + return false; + } + + std::vector<std::string> lines = base::Split(fileContent, "\n"); + + for (auto& line : lines) { + auto trimmedLine = base::Trim(line); + if (!trimmedLine.empty()) { + soNames->push_back(trimmedLine); + } + } + + return true; +} + +static const std::string getSystemNativeLibraries(NativeLibrary type) { + static const char* androidRootEnv = getenv("ANDROID_ROOT"); + static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system"; + + std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type]; + + insertVndkVersionStr(&nativeLibrariesSystemConfig); + + std::vector<std::string> soNames; + if (!readConfig(nativeLibrariesSystemConfig, &soNames)) { + ALOGE("Failed to retrieve library names from %s", nativeLibrariesSystemConfig.c_str()); + return ""; + } + + return base::Join(soNames, ':'); +} + /*static*/ GraphicsEnv& GraphicsEnv::getInstance() { static GraphicsEnv env; return env; } +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::setDriverPath(const std::string path) { if (!mDriverPath.empty()) { - ALOGV("ignoring attempt to change driver path from '%s' to '%s'", - mDriverPath.c_str(), path.c_str()); + ALOGV("ignoring attempt to change driver path from '%s' to '%s'", mDriverPath.c_str(), + path.c_str()); return; } ALOGV("setting driver path to '%s'", path.c_str()); mDriverPath = path; } +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()) { + ALOGE("App name is empty. setAngleInfo() must be called first to enable ANGLE."); + return false; + } + + return mUseAngle; +} + +void GraphicsEnv::updateUseAngle() { + mUseAngle = false; + + 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 = true; + } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { + ALOGV("User set \"Developer Options\" to force the use of Native"); + mUseAngle = false; + } 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: + + // Check if ANGLE is enabled. Workaround for several bugs: + // b/119305693 b/119322355 b/119305887 + // Something is not working correctly in the feature library + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.angle.enable", prop, "0"); + void* featureSo = nullptr; + if (atoi(prop)) { + featureSo = loadLibrary("feature_support"); + } + if (featureSo) { + ALOGV("loaded ANGLE's opt-in/out logic from namespace"); + mUseAngle = checkAngleRules(featureSo); + 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) { + 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; mAppNamespace = appNamespace; } else { ALOGV("Vulkan layer search path already set, not clobbering with '%s' for namespace %p'", - layerPaths.c_str(), appNamespace); + layerPaths.c_str(), appNamespace); } } @@ -70,40 +349,87 @@ NativeLoaderNamespace* GraphicsEnv::getAppNamespace() { return mAppNamespace; } -const std::string GraphicsEnv::getLayerPaths(){ +std::string& GraphicsEnv::getAngleAppName() { + return mAngleAppName; +} + +const std::string& GraphicsEnv::getLayerPaths() { return mLayerPaths; } -const std::string GraphicsEnv::getDebugLayers() { +const std::string& GraphicsEnv::getDebugLayers() { return mDebugLayers; } +const std::string& GraphicsEnv::getDebugLayersGLES() { + return mDebugLayersGLES; +} + void GraphicsEnv::setDebugLayers(const std::string layers) { mDebugLayers = layers; } +void GraphicsEnv::setDebugLayersGLES(const std::string layers) { + mDebugLayersGLES = layers; +} + android_namespace_t* GraphicsEnv::getDriverNamespace() { static std::once_flag once; std::call_once(once, [this]() { - if (mDriverPath.empty()) - return; - // If the sphal namespace isn't configured for a device, don't support updatable drivers. - // We need a parent namespace to inherit the default search path from. - auto sphalNamespace = android_get_exported_namespace("sphal"); - if (!sphalNamespace) return; + if (mDriverPath.empty()) return; + + auto vndkNamespace = android_get_exported_namespace("vndk"); + if (!vndkNamespace) return; + mDriverNamespace = android_create_namespace("gfx driver", - nullptr, // ld_library_path + mDriverPath.c_str(), // ld_library_path mDriverPath.c_str(), // default_library_path - ANDROID_NAMESPACE_TYPE_SHARED | - ANDROID_NAMESPACE_TYPE_ISOLATED, + ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, // permitted_when_isolated_path - sphalNamespace); + nullptr); + + const std::string llndkLibraries = getSystemNativeLibraries(NativeLibrary::LLNDK); + if (llndkLibraries.empty()) { + mDriverNamespace = nullptr; + return; + } + if (!android_link_namespaces(mDriverNamespace, nullptr, llndkLibraries.c_str())) { + ALOGE("Failed to link default namespace[%s]", dlerror()); + mDriverNamespace = nullptr; + return; + } + + const std::string vndkspLibraries = getSystemNativeLibraries(NativeLibrary::VNDKSP); + if (vndkspLibraries.empty()) { + mDriverNamespace = nullptr; + return; + } + if (!android_link_namespaces(mDriverNamespace, vndkNamespace, vndkspLibraries.c_str())) { + ALOGE("Failed to link vndk namespace[%s]", dlerror()); + mDriverNamespace = nullptr; + return; + } }); + return mDriverNamespace; } -} // namespace android +android_namespace_t* GraphicsEnv::getAngleNamespace() { + static std::once_flag once; + std::call_once(once, [this]() { + if (mAnglePath.empty()) return; + + 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); + if (!mAngleNamespace) ALOGD("Could not create ANGLE namespace from default"); + }); -extern "C" android_namespace_t* android_getDriverNamespace() { - return android::GraphicsEnv::getInstance().getDriverNamespace(); + return mAngleNamespace; } + +} // namespace android diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 17e8f6ba47..37c24ef173 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -18,6 +18,7 @@ #define ANDROID_UI_GRAPHICS_ENV_H 1 #include <string> +#include <vector> struct android_namespace_t; @@ -29,6 +30,8 @@ class GraphicsEnv { 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 @@ -37,35 +40,48 @@ public: void setDriverPath(const std::string path); android_namespace_t* getDriverNamespace(); + 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: + void* loadLibrary(std::string name); + bool checkAngleRules(void* so); + void updateUseAngle(); + GraphicsEnv() = default; std::string mDriverPath; + std::string mAnglePath; + std::string mAngleAppName; + std::string mAngleDeveloperOptIn; + std::vector<char> mRulesBuffer; + bool mUseAngle; std::string mDebugLayers; + std::string mDebugLayersGLES; std::string mLayerPaths; 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/gui/Android.bp b/libs/gui/Android.bp index b29c1d5157..d1c732bc1f 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -58,7 +58,7 @@ cc_library_shared { "-Wno-weak-vtables", // Allow four-character integer literals - "-Wno-four-char-constants", + "-Wno-four-char-constants", // Allow documentation warnings "-Wno-documentation", @@ -106,6 +106,7 @@ cc_library_shared { "IProducerListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", + "ITransactionCompletedListener.cpp", "LayerDebugInfo.cpp", "LayerState.cpp", "OccupancyTracker.cpp", @@ -116,14 +117,16 @@ cc_library_shared { "SyncFeatures.cpp", "view/Surface.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", - "bufferqueue/1.0/H2BGraphicBufferProducer.cpp" + "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", ], shared_libs: [ "android.hardware.graphics.common@1.1", + "libbase", "libsync", "libbinder", - "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. + "libbufferhub", + "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. "libpdx_default_transport", "libcutils", "libEGL", @@ -132,6 +135,7 @@ cc_library_shared { "libutils", "libnativewindow", "liblog", + "libinput", "libhidlbase", "libhidltransport", "android.hidl.token@1.0-utils", @@ -143,14 +147,16 @@ cc_library_shared { // bufferhub is not used when building libgui for vendors target: { vendor: { - cflags: ["-DNO_BUFFERHUB"], + cflags: ["-DNO_BUFFERHUB", "-DNO_INPUT"], exclude_srcs: [ "BufferHubConsumer.cpp", "BufferHubProducer.cpp", ], exclude_shared_libs: [ + "libbufferhub", "libbufferhubqueue", "libpdx_default_transport", + "libinput" ], }, }, diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp index ae5cca2d20..16952a6625 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 { @@ -224,23 +225,172 @@ status_t BufferHubProducer::dequeueBuffer(int* out_slot, sp<Fence>* out_fence, u return ret; } -status_t BufferHubProducer::detachBuffer(int /* slot */) { - ALOGE("BufferHubProducer::detachBuffer not implemented."); - return INVALID_OPERATION; +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::detachNextBuffer(sp<GraphicBuffer>* /* out_buffer */, - sp<Fence>* /* out_fence */) { - ALOGE("BufferHubProducer::detachNextBuffer not implemented."); +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<BufferProducer> buffer_producer = queue_->GetBuffer(slot); + if (buffer_producer == nullptr || buffer_producer->buffer() == nullptr) { + ALOGE("detachBuffer: Invalid BufferProducer at slot %zu.", slot); + return BAD_VALUE; + } + sp<GraphicBuffer> graphic_buffer = buffer_producer->buffer()->buffer(); + if (graphic_buffer == nullptr) { + ALOGE("detachBuffer: Invalid GraphicBuffer at slot %zu.", slot); + return BAD_VALUE; + } + + // Remove the BufferProducer 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 = buffer_producer->Detach(); + if (!status_or_handle.ok()) { + ALOGE("detachBuffer: Failed to detach from a BufferProducer 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::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::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 BufferProducer 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 BufferProducer 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<BufferProducer> buffer_producer = status_or_buffer.take(); + if (buffer_producer == 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].mBufferProducer == nullptr) { + ALOGE("detachNextBuffer: BufferProducer at slot %zu is null.", slot); + return BAD_VALUE; + } + if (buffers_[slot].mBufferProducer->id() != buffer_producer->id()) { + ALOGE("detachNextBuffer: BufferProducer at slot %zu has mismatched id, actual: " + "%d, expected: %d.", + slot, buffers_[slot].mBufferProducer->id(), buffer_producer->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 = buffer_producer->buffer()->buffer(); + buffers_[slot].mGraphicBuffer = buffer_producer->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) { + // 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; } @@ -370,7 +520,7 @@ status_t BufferHubProducer::cancelBuffer(int slot, const sp<Fence>& fence) { } auto buffer_producer = buffers_[slot].mBufferProducer; - queue_->Enqueue(buffer_producer, size_t(slot), 0ULL); + queue_->Enqueue(buffer_producer, size_t(slot), 0U); buffers_[slot].mBufferState.cancel(); buffers_[slot].mFence = fence; ALOGV("cancelBuffer: slot %d", slot); @@ -654,26 +804,28 @@ status_t BufferHubProducer::AllocateBuffer(uint32_t width, uint32_t height, uint 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].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].mBufferState.reset(); - buffers_[slot].mRequestBufferCalled = false; buffers_[slot].mBufferProducer = nullptr; + buffers_[slot].mBufferState.reset(); 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 f50379b3ed..5beba02e63 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -39,8 +39,8 @@ static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { } 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::getPodSize() const { 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::getFlattenedSize() const { 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 @@ status_t BufferItem::flatten( 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 89bc0c4c2d..f50bc203e8 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -107,7 +107,7 @@ status_t BufferItemConsumer::releaseBuffer(const BufferItem &item, 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 a8da1347cb..5fb3f0b80f 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -38,7 +38,7 @@ BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} void BufferQueue::ProxyConsumerListener::onDisconnect() { sp<ConsumerListener> listener(mConsumerListener.promote()); - if (listener != NULL) { + if (listener != nullptr) { listener->onDisconnect(); } } @@ -46,7 +46,7 @@ void BufferQueue::ProxyConsumerListener::onDisconnect() { 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::onFrameAvailable( 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::ProxyConsumerListener::addAndGetFrameTimestamps( 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 @@ void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, #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 @@ void BufferQueue::createBufferHubQueue(sp<IGraphicBufferProducer>* outProducer, 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 d70e1422b0..3837c3e11a 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -255,7 +255,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer // on the consumer side if (outBuffer->mAcquireCalled) { - outBuffer->mGraphicBuffer = NULL; + outBuffer->mGraphicBuffer = nullptr; } mCore->mQueue.erase(front); @@ -272,7 +272,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, VALIDATE_CONSISTENCY(); } - if (listener != NULL) { + if (listener != nullptr) { for (int i = 0; i < numDroppedBuffers; ++i) { listener->onBufferReleased(); } @@ -321,10 +321,10 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, const sp<android::GraphicBuffer>& buffer) { ATRACE_CALL(); - if (outSlot == NULL) { + if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; - } else if (buffer == NULL) { + } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } @@ -413,7 +413,7 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, ATRACE_BUFFER_INDEX(slot); if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS || - releaseFence == NULL) { + releaseFence == nullptr) { BQ_LOGE("releaseBuffer: slot %d out of range or fence %p NULL", slot, releaseFence.get()); return BAD_VALUE; @@ -465,7 +465,7 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBufferReleased(); } @@ -476,7 +476,7 @@ status_t BufferQueueConsumer::connect( const sp<IConsumerListener>& consumerListener, bool controlledByApp) { ATRACE_CALL(); - if (consumerListener == NULL) { + if (consumerListener == nullptr) { BQ_LOGE("connect: consumerListener may not be NULL"); return BAD_VALUE; } @@ -504,13 +504,13 @@ status_t BufferQueueConsumer::disconnect() { Mutex::Autolock lock(mCore->mMutex); - if (mCore->mConsumerListener == NULL) { + if (mCore->mConsumerListener == nullptr) { BQ_LOGE("disconnect: no consumer is connected"); return BAD_VALUE; } mCore->mIsAbandoned = true; - mCore->mConsumerListener = NULL; + mCore->mConsumerListener = nullptr; mCore->mQueue.clear(); mCore->freeAllBuffersLocked(); mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; @@ -521,7 +521,7 @@ status_t BufferQueueConsumer::disconnect() { status_t BufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { ATRACE_CALL(); - if (outSlotMask == NULL) { + if (outSlotMask == nullptr) { BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL"); return BAD_VALUE; } @@ -673,7 +673,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( } } // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -772,7 +772,7 @@ status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResul if (uid != shellUid) { #endif 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 bb703da3dd..960b1948c2 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -349,7 +349,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mUnusedSlots but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer != NULL) { + if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mUnusedSluts but has an active buffer", slot); usleep(PAUSE_TIME); @@ -371,7 +371,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mFreeSlots but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer != NULL) { + if (mSlots[slot].mGraphicBuffer != nullptr) { BQ_LOGE("Slot %d is in mFreeSlots but has a buffer", slot); usleep(PAUSE_TIME); @@ -394,7 +394,7 @@ void BufferQueueCore::validateConsistencyLocked() const { BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE", slot); usleep(PAUSE_TIME); } - if (mSlots[slot].mGraphicBuffer == NULL) { + if (mSlots[slot].mGraphicBuffer == nullptr) { BQ_LOGE("Slot %d is in mFreeBuffers but has no buffer", slot); usleep(PAUSE_TIME); } @@ -418,7 +418,7 @@ void BufferQueueCore::validateConsistencyLocked() const { 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 c96a2dd6a3..5e250a4185 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -166,7 +166,7 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -221,7 +221,7 @@ status_t BufferQueueProducer::setAsyncMode(bool async) { } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } return NO_ERROR; @@ -449,11 +449,11 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou 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 +471,7 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64, mCore->mBufferAge); - if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { + if (CC_UNLIKELY(mSlots[found].mFence == nullptr)) { BQ_LOGE("dequeueBuffer: about to return a NULL fence - " "slot=%d w=%d h=%d format=%u", found, buffer->width, buffer->height, buffer->format); @@ -612,7 +612,7 @@ status_t BufferQueueProducer::detachBuffer(int slot) { listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -623,10 +623,10 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { ATRACE_CALL(); - if (outBuffer == NULL) { + if (outBuffer == nullptr) { BQ_LOGE("detachNextBuffer: outBuffer must not be NULL"); return BAD_VALUE; - } else if (outFence == NULL) { + } else if (outFence == nullptr) { BQ_LOGE("detachNextBuffer: outFence must not be NULL"); return BAD_VALUE; } @@ -670,7 +670,7 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); } @@ -681,10 +681,10 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, const sp<android::GraphicBuffer>& buffer) { ATRACE_CALL(); - if (outSlot == NULL) { + if (outSlot == nullptr) { BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; - } else if (buffer == NULL) { + } else if (buffer == nullptr) { BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } @@ -766,7 +766,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, const Region& surfaceDamage = input.getSurfaceDamage(); const HdrMetadata& hdrMetadata = input.getHdrMetadata(); - if (acquireFence == NULL) { + if (acquireFence == nullptr) { BQ_LOGE("queueBuffer: fence is NULL"); return BAD_VALUE; } @@ -972,9 +972,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, mCallbackCondition.wait(mCallbackMutex); } - if (frameAvailableListener != NULL) { + if (frameAvailableListener != nullptr) { frameAvailableListener->onFrameAvailable(item); - } else if (frameReplacedListener != NULL) { + } else if (frameReplacedListener != nullptr) { frameReplacedListener->onFrameReplaced(item); } @@ -1039,7 +1039,7 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; - } else if (fence == NULL) { + } else if (fence == nullptr) { BQ_LOGE("cancelBuffer: fence is NULL"); return BAD_VALUE; } @@ -1069,7 +1069,7 @@ int BufferQueueProducer::query(int what, int *outValue) { ATRACE_CALL(); Mutex::Autolock lock(mCore->mMutex); - if (outValue == NULL) { + if (outValue == nullptr) { BQ_LOGE("query: outValue was NULL"); return BAD_VALUE; } @@ -1145,12 +1145,12 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, 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 +1188,10 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, 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) { @@ -1268,7 +1268,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { mCore->freeAllBuffersLocked(); // Remove our death notification callback if we have one - if (mCore->mLinkedToDeath != NULL) { + if (mCore->mLinkedToDeath != nullptr) { sp<IBinder> token = IInterface::asBinder(mCore->mLinkedToDeath); // This can fail if we're here because of the death @@ -1278,8 +1278,8 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { } mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; - mCore->mLinkedToDeath = NULL; - mCore->mConnectedProducerListener = NULL; + mCore->mLinkedToDeath = nullptr; + mCore->mConnectedProducerListener = nullptr; mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API; mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); @@ -1302,7 +1302,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { } // Autolock scope // Call back without lock held - if (listener != NULL) { + if (listener != nullptr) { listener->onBuffersReleased(); listener->onDisconnect(); } @@ -1318,7 +1318,7 @@ status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) listener = mCore->mConsumerListener; } // Autolock scope - if (listener != NULL) { + if (listener != nullptr) { listener->onSidebandStreamChanged(); } return NO_ERROR; @@ -1536,7 +1536,7 @@ void BufferQueueProducer::addAndGetFrameTimestamps( Mutex::Autolock lock(mCore->mMutex); listener = mCore->mConsumerListener; } - if (listener != NULL) { + if (listener != nullptr) { listener->addAndGetFrameTimestamps(newTimestamps, outDelta); } } diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index f9e292e199..abd9921fa9 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -96,7 +96,7 @@ void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { 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 @@ void ConsumerBase::onFrameAvailable(const BufferItem& item) { listener = mFrameAvailableListener.promote(); } - if (listener != NULL) { + if (listener != nullptr) { CB_LOGV("actually calling onFrameAvailable"); listener->onFrameAvailable(item); } @@ -125,7 +125,7 @@ void ConsumerBase::onFrameReplaced(const BufferItem &item) { listener = mFrameAvailableListener.promote(); } - if (listener != NULL) { + if (listener != nullptr) { CB_LOGV("actually calling onFrameReplaced"); listener->onFrameReplaced(item); } @@ -352,8 +352,8 @@ status_t ConsumerBase::acquireBufferLocked(BufferItem *item, 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 @@ bool ConsumerBase::stillTracking(int slot, 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 1757ec1cd3..f5cf1c4d5a 100644 --- a/libs/gui/DisplayEventReceiver.cpp +++ b/libs/gui/DisplayEventReceiver.cpp @@ -34,9 +34,9 @@ namespace android { 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 @@ DisplayEventReceiver::~DisplayEventReceiver() { } 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 @@ status_t DisplayEventReceiver::setVsyncRate(uint32_t count) { 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::setVsyncRate(uint32_t count) { } 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 85ae433e42..96e9a85fd2 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 @@ void FrameEvents::checkFencesForCompletion() { 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 @@ void FrameEvents::dump(String8& outString) const 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 @@ static bool FrameNumberLessThan( 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 885efec9b9..faf02f3b53 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 @@ static const mat4 mtxIdentity; 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 @@ static bool hasEglProtectedContent() { 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 @@ status_t GLConsumer::releaseTexImage() { return err; } - if (mReleasedTexImage == NULL) { + if (mReleasedTexImage == nullptr) { mReleasedTexImage = new EglImage(getDebugTexImageBuffer()); } @@ -321,7 +296,7 @@ status_t GLConsumer::releaseTexImage() { 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 @@ status_t GLConsumer::acquireBufferLocked(BufferItem *item, // 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 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, // 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 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, } 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 @@ status_t GLConsumer::bindTextureImageLocked() { 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 @@ status_t GLConsumer::bindTextureImageLocked() { // 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 @@ status_t GLConsumer::attachToContext(uint32_t tex) { 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 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { 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 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { // 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 @@ void GLConsumer::setFilteringEnabled(bool enabled) { bool needsRecompute = mFilteringEnabled != enabled; mFilteringEnabled = enabled; - if (needsRecompute && mCurrentTextureImage==NULL) { + if (needsRecompute && mCurrentTextureImage==nullptr) { GLC_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL"); } - if (needsRecompute && mCurrentTextureImage != NULL) { + if (needsRecompute && mCurrentTextureImage != nullptr) { computeCurrentTransformMatrixLocked(); } } @@ -769,8 +741,7 @@ void GLConsumer::computeCurrentTransformMatrixLocked() { GLC_LOGD("computeCurrentTransformMatrixLocked: " "mCurrentTextureImage is NULL"); } - computeTransformMatrix(mCurrentTransformMatrix, buf, - isEglImageCroppable(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop, + computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform, mFilteringEnabled); } @@ -863,10 +834,10 @@ void GLConsumer::computeTransformMatrix(float outTransform[16], xform = crop * xform; } - // SurfaceFlinger expects the top of its window textures to be at a Y - // coordinate of 0, so GLConsumer must behave the same way. We don't - // want to expose this to applications, however, so we must add an - // additional vertical flip to the transform after all the other transforms. + // GLConsumer uses the GL convention where (0, 0) is the bottom-left + // corner and (1, 1) is the top-right corner. Add an additional vertical + // flip after all other transforms to map from GL convention to buffer + // queue memory layout, where (0, 0) is the top-left corner. xform = mtxFlipV * xform; memcpy(outTransform, xform.asArray(), sizeof(xform)); @@ -938,7 +909,7 @@ sp<GraphicBuffer> GLConsumer::getCurrentBuffer(int* outSlot) const { } return (mCurrentTextureImage == nullptr) ? - NULL : mCurrentTextureImage->graphicBuffer(); + nullptr : mCurrentTextureImage->graphicBuffer(); } Rect GLConsumer::getCurrentCrop() const { @@ -1063,8 +1034,7 @@ void GLConsumer::dumpLocked(String8& result, const char* prefix) const GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), - mEglDisplay(EGL_NO_DISPLAY), - mCropRect(Rect::EMPTY_RECT) { + mEglDisplay(EGL_NO_DISPLAY) { } GLConsumer::EglImage::~EglImage() { @@ -1077,13 +1047,11 @@ GLConsumer::EglImage::~EglImage() { } status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, - const Rect& cropRect, bool forceCreation) { // If there's an image and it's no longer valid, destroy it. bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; bool displayInvalid = mEglDisplay != eglDisplay; - bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect; - if (haveImage && (displayInvalid || cropInvalid || forceCreation)) { + if (haveImage && (displayInvalid || forceCreation)) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { ALOGE("createIfNeeded: eglDestroyImageKHR failed"); } @@ -1095,14 +1063,12 @@ status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, // If there's no image, create one. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = eglDisplay; - mCropRect = cropRect; - mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect); + mEglImage = createImage(mEglDisplay, mGraphicBuffer); } // Fail if we can't create a valid image. if (mEglImage == EGL_NO_IMAGE_KHR) { mEglDisplay = EGL_NO_DISPLAY; - mCropRect.makeInvalid(); const sp<GraphicBuffer>& buffer = mGraphicBuffer; ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", buffer->getWidth(), buffer->getHeight(), buffer->getStride(), @@ -1119,38 +1085,19 @@ void GLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) { } EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy, - const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) { + const sp<GraphicBuffer>& graphicBuffer) { EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer()); const bool createProtectedImage = (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent(); EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, - EGL_IMAGE_CROP_LEFT_ANDROID, crop.left, - EGL_IMAGE_CROP_TOP_ANDROID, crop.top, - EGL_IMAGE_CROP_RIGHT_ANDROID, crop.right, - EGL_IMAGE_CROP_BOTTOM_ANDROID, crop.bottom, + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, createProtectedImage ? EGL_TRUE : EGL_NONE, EGL_NONE, }; - if (!crop.isValid()) { - // No crop rect to set, so leave the crop out of the attrib array. Make - // sure to propagate the protected content attrs if they are set. - attrs[2] = attrs[10]; - attrs[3] = attrs[11]; - attrs[4] = EGL_NONE; - } else if (!isEglImageCroppable(crop)) { - // The crop rect is not at the origin, so we can't set the crop on the - // EGLImage because that's not allowed by the EGL_ANDROID_image_crop - // extension. In the future we can add a layered extension that - // removes this restriction if there is hardware that can support it. - attrs[2] = attrs[10]; - attrs[3] = attrs[11]; - attrs[4] = EGL_NONE; - } - eglInitialize(dpy, 0, 0); + eglInitialize(dpy, nullptr, nullptr); EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); if (image == EGL_NO_IMAGE_KHR) { diff --git a/libs/gui/GuiConfig.cpp b/libs/gui/GuiConfig.cpp index bc0c83c557..3ec20ee0c0 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 b715e431d5..add3ef0458 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 @@ size_t HdrMetadata::getFlattenedSize() const { if (validTypes & CTA861_3) { size += sizeof(cta8613); } + if (validTypes & HDR10PLUS) { + size += sizeof(size_t); + size += hdr10plus.size(); + } return size; } @@ -41,6 +46,12 @@ status_t HdrMetadata::flatten(void* buffer, size_t size) const { 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 @@ status_t HdrMetadata::unflatten(void const* buffer, size_t size) { } 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 @@ bool HdrMetadata::operator==(const HdrMetadata& rhs) const { } } + 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 0b3796056d..68a6b1fe38 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -190,10 +190,10 @@ public: 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 +301,7 @@ public: 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 { @@ -738,8 +738,8 @@ status_t BnGraphicBufferProducer::onTransact( int bufferIdx = data.readInt32(); sp<GraphicBuffer> buffer; int result = requestBuffer(bufferIdx, &buffer); - reply->writeInt32(buffer != 0); - if (buffer != 0) { + reply->writeInt32(buffer != nullptr); + if (buffer != nullptr) { reply->write(*buffer); } reply->writeInt32(result); @@ -797,12 +797,12 @@ status_t BnGraphicBufferProducer::onTransact( int32_t result = detachNextBuffer(&buffer, &fence); reply->writeInt32(result); if (result == NO_ERROR) { - reply->writeInt32(buffer != NULL); - if (buffer != NULL) { + reply->writeInt32(buffer != nullptr); + if (buffer != nullptr) { reply->write(*buffer); } - reply->writeInt32(fence != NULL); - if (fence != NULL) { + reply->writeInt32(fence != nullptr); + if (fence != nullptr) { reply->write(*fence); } } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index d2d27e8239..2d6be26903 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -103,59 +103,64 @@ public: } 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, + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); + data.writeInt32(static_cast<int32_t>(reqDataspace)); + data.writeInt32(static_cast<int32_t>(reqPixelFormat)); data.write(sourceCrop); data.writeUint32(reqWidth); data.writeUint32(reqHeight); - data.writeInt32(minLayerZ); - data.writeInt32(maxLayerZ); data.writeInt32(static_cast<int32_t>(useIdentityTransform)); data.writeInt32(static_cast<int32_t>(rotation)); - status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); - - if (err != NO_ERROR) { - return err; + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to transact: %d", result); + return result; } - - err = reply.readInt32(); - if (err != NO_ERROR) { - return err; + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureScreen failed to readInt32: %d", result); + return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); - return err; + + return result; } virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, + sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale, bool childrenOnly) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(layerHandleBinder); + data.writeInt32(static_cast<int32_t>(reqDataspace)); + data.writeInt32(static_cast<int32_t>(reqPixelFormat)); data.write(sourceCrop); data.writeFloat(frameScale); data.writeBool(childrenOnly); - status_t err = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); - - if (err != NO_ERROR) { - return err; + status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); + if (result != NO_ERROR) { + ALOGE("captureLayers failed to transact: %d", result); + return result; } - - err = reply.readInt32(); - if (err != NO_ERROR) { - return err; + result = reply.readInt32(); + if (result != NO_ERROR) { + ALOGE("captureLayers failed to readInt32: %d", result); + return result; } *outBuffer = new GraphicBuffer(); reply.read(**outBuffer); - return err; + return result; } virtual bool authenticateSurfaceTexture( @@ -332,50 +337,38 @@ public: return result; } - virtual status_t getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { + virtual int getActiveConfig(const sp<IBinder>& display) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + data.writeStrongBinder(display); + remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply); + return reply.readInt32(); + } + + virtual status_t setActiveConfig(const sp<IBinder>& display, int id) + { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to writeInterfaceToken: %d", result); + ALOGE("setActiveConfig failed to writeInterfaceToken: %d", result); return result; } result = data.writeStrongBinder(display); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to writeStrongBinder: %d", result); + ALOGE("setActiveConfig failed to writeStrongBinder: %d", result); return result; } - result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_VIEWPORT, data, &reply); + result = data.writeInt32(id); if (result != NO_ERROR) { - ALOGE("getDisplayViewport failed to transact: %d", result); + ALOGE("setActiveConfig failed to writeInt32: %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; - } + result = remote()->transact(BnSurfaceComposer::SET_ACTIVE_CONFIG, data, &reply); + if (result != NO_ERROR) { + ALOGE("setActiveConfig failed to transact: %d", result); + return result; } - return result; - } - - virtual int getActiveConfig(const sp<IBinder>& display) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_ACTIVE_CONFIG, data, &reply); - return reply.readInt32(); - } - - 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); return reply.readInt32(); } @@ -457,8 +450,16 @@ public: virtual status_t clearAnimationFrameStats() { Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("clearAnimationFrameStats failed to writeInterfaceToken: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::CLEAR_ANIMATION_FRAME_STATS, data, &reply); + if (result != NO_ERROR) { + ALOGE("clearAnimationFrameStats failed to transact: %d", result); + return result; + } return reply.readInt32(); } @@ -563,6 +564,132 @@ public: 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; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -598,10 +725,10 @@ status_t BnSurfaceComposer::onTransact( 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; } @@ -634,18 +761,18 @@ status_t BnSurfaceComposer::onTransact( 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(); - status_t res = captureScreen(display, &outBuffer, sourceCrop, reqWidth, reqHeight, - minLayerZ, maxLayerZ, useIdentityTransform, + status_t res = captureScreen(display, &outBuffer, reqDataspace, reqPixelFormat, + sourceCrop, reqWidth, reqHeight, useIdentityTransform, static_cast<ISurfaceComposer::Rotation>(rotation)); reply->writeInt32(res); if (res == NO_ERROR) { @@ -656,14 +783,16 @@ status_t BnSurfaceComposer::onTransact( case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> layerHandleBinder = data.readStrongBinder(); + ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); + ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); sp<GraphicBuffer> outBuffer; Rect sourceCrop(Rect::EMPTY_RECT); data.read(sourceCrop); float frameScale = data.readFloat(); bool childrenOnly = data.readBool(); - status_t res = captureLayers(layerHandleBinder, &outBuffer, sourceCrop, frameScale, - childrenOnly); + status_t res = captureLayers(layerHandleBinder, &outBuffer, reqDataspace, + reqPixelFormat, sourceCrop, frameScale, childrenOnly); reply->writeInt32(res); if (res == NO_ERROR) { reply->write(*outBuffer); @@ -752,26 +881,6 @@ status_t BnSurfaceComposer::onTransact( } 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(); @@ -906,6 +1015,115 @@ status_t BnSurfaceComposer::onTransact( } 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 NO_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 result; + } + 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; + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp new file mode 100644 index 0000000000..1be55e6f8a --- /dev/null +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -0,0 +1,183 @@ +/* + * 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; + } + return output->writeBool(releasePreviousBuffer); +} + +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; + } + return input->readBool(&releasePreviousBuffer); +} + +status_t TransactionStats::writeToParcel(Parcel* output) const { + status_t err = output->writeInt64(latchTime); + if (err != NO_ERROR) { + return err; + } + if (presentFence) { + err = output->writeBool(true); + if (err != NO_ERROR) { + return err; + } + err = output->write(*presentFence); + } else { + err = output->writeBool(false); + } + if (err != NO_ERROR) { + return err; + } + return output->writeParcelableVector(surfaceStats); +} + +status_t TransactionStats::readFromParcel(const Parcel* input) { + status_t err = input->readInt64(&latchTime); + if (err != NO_ERROR) { + return err; + } + bool hasFence = false; + err = input->readBool(&hasFence); + if (err != NO_ERROR) { + return err; + } + if (hasFence) { + presentFence = new Fence(); + err = input->read(*presentFence); + if (err != NO_ERROR) { + return err; + } + } + return input->readParcelableVector(&surfaceStats); +} + +status_t ListenerStats::writeToParcel(Parcel* output) const { + status_t err = output->writeInt32(static_cast<int32_t>(transactionStats.size())); + if (err != NO_ERROR) { + return err; + } + + for (const auto& [callbackIds, stats] : transactionStats) { + err = output->writeParcelable(stats); + if (err != NO_ERROR) { + return err; + } + err = output->writeInt64Vector(callbackIds); + if (err != NO_ERROR) { + return err; + } + } + return NO_ERROR; +} + +status_t ListenerStats::readFromParcel(const Parcel* input) { + int32_t transactionStats_size = input->readInt32(); + + for (int i = 0; i < transactionStats_size; i++) { + TransactionStats stats; + std::vector<CallbackId> callbackIds; + + status_t err = input->readParcelable(&stats); + if (err != NO_ERROR) { + return err; + } + err = input->readInt64Vector(&callbackIds); + if (err != NO_ERROR) { + return err; + } + + transactionStats.emplace(callbackIds, stats); + } + return NO_ERROR; +} + +ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbackIds) { + ListenerStats listenerStats; + listenerStats.listener = listener; + TransactionStats transactionStats; + listenerStats.transactionStats.emplace(std::piecewise_construct, + std::forward_as_tuple(callbackIds.begin(), + callbackIds.end()), + std::forward_as_tuple(transactionStats)); + return listenerStats; +} + +class BpTransactionCompletedListener : public SafeBpInterface<ITransactionCompletedListener> { +public: + explicit BpTransactionCompletedListener(const sp<IBinder>& impl) + : SafeBpInterface<ITransactionCompletedListener>(impl, "BpTransactionCompletedListener") { + } + + ~BpTransactionCompletedListener() override; + + void onTransactionCompleted(ListenerStats stats) override { + callRemoteAsync<decltype(&ITransactionCompletedListener:: + onTransactionCompleted)>(Tag::ON_TRANSACTION_COMPLETED, + stats); + } +}; + +// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see +// clang warning -Wweak-vtables) +BpTransactionCompletedListener::~BpTransactionCompletedListener() = default; + +IMPLEMENT_META_INTERFACE(TransactionCompletedListener, "android.gui.ITransactionComposerListener"); + +status_t BnTransactionCompletedListener::onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) { + if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) { + return BBinder::onTransact(code, data, reply, flags); + } + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::ON_TRANSACTION_COMPLETED: + return callLocalAsync(data, reply, + &ITransactionCompletedListener::onTransactionCompleted); + } +} + +}; // namespace android diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp index d3dc16d30e..cdde9a2308 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 @@ status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { 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 @@ status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { 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 @@ status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { } 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/LayerState.cpp b/libs/gui/LayerState.cpp index 01acc2de20..35ce6e393a 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 @@ namespace android { 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,66 @@ status_t layer_state_t::write(Parcel& output) const output.writeUint32(mask); *reinterpret_cast<layer_state_t::matrix22_t *>( output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix; - output.write(crop); - output.write(finalCrop); - output.writeStrongBinder(barrierHandle); + output.write(crop_legacy); + output.writeStrongBinder(barrierHandle_legacy); output.writeStrongBinder(reparentHandle); - output.writeUint64(frameNumber); + output.writeUint64(frameNumber_legacy); output.writeInt32(overrideScalingMode); - output.writeStrongBinder(IInterface::asBinder(barrierGbp)); + output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy)); output.writeStrongBinder(relativeLayerHandle); output.writeStrongBinder(parentHandleForChild); output.writeFloat(color.r); output.writeFloat(color.g); output.writeFloat(color.b); +#ifndef NO_INPUT + inputInfo.write(output); +#endif output.write(transparentRegion); + output.writeUint32(transform); + output.writeBool(transformToDisplayInverse); + output.write(crop); + output.write(frame); + if (buffer) { + output.writeBool(true); + output.write(*buffer); + } else { + output.writeBool(false); + } + if (acquireFence) { + output.writeBool(true); + output.write(*acquireFence); + } else { + output.writeBool(false); + } + output.writeUint32(static_cast<uint32_t>(dataspace)); + output.write(hdrMetadata); + output.write(surfaceDamageRegion); + output.writeInt32(api); + if (sidebandStream) { + output.writeBool(true); + output.writeNativeHandle(sidebandStream->handle()); + } else { + output.writeBool(false); + } + + memcpy(output.writeInplace(16 * sizeof(float)), + colorTransform.asArray(), 16 * sizeof(float)); + output.writeFloat(cornerRadius); + + if (output.writeVectorSize(listenerCallbacks) == NO_ERROR) { + for (const auto& [listener, callbackIds] : listenerCallbacks) { + output.writeStrongBinder(IInterface::asBinder(listener)); + output.writeInt64Vector(callbackIds); + } + } + 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 +116,54 @@ status_t layer_state_t::read(const Parcel& input) } else { return BAD_VALUE; } - input.read(crop); - input.read(finalCrop); - barrierHandle = input.readStrongBinder(); + input.read(crop_legacy); + barrierHandle_legacy = input.readStrongBinder(); reparentHandle = input.readStrongBinder(); - frameNumber = input.readUint64(); + frameNumber_legacy = input.readUint64(); overrideScalingMode = input.readInt32(); - barrierGbp = - interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); + barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); relativeLayerHandle = input.readStrongBinder(); parentHandleForChild = input.readStrongBinder(); color.r = input.readFloat(); color.g = input.readFloat(); color.b = input.readFloat(); + +#ifndef NO_INPUT + inputInfo = InputWindowInfo::read(input); +#endif + input.read(transparentRegion); + transform = input.readUint32(); + transformToDisplayInverse = input.readBool(); + input.read(crop); + input.read(frame); + buffer = new GraphicBuffer(); + if (input.readBool()) { + input.read(*buffer); + } + acquireFence = new Fence(); + if (input.readBool()) { + input.read(*acquireFence); + } + dataspace = static_cast<ui::Dataspace>(input.readUint32()); + input.read(hdrMetadata); + input.read(surfaceDamageRegion); + api = input.readInt32(); + if (input.readBool()) { + sidebandStream = NativeHandle::create(input.readNativeHandle(), true); + } + + colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); + cornerRadius = input.readFloat(); + + int32_t listenersSize = input.readInt32(); + for (int32_t i = 0; i < listenersSize; i++) { + auto listener = interface_cast<ITransactionCompletedListener>(input.readStrongBinder()); + std::vector<CallbackId> callbackIds; + input.readInt64Vector(&callbackIds); + listenerCallbacks.emplace_back(listener, callbackIds); + } + return NO_ERROR; } @@ -194,19 +272,19 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eLayerStackChanged; layerStack = other.layerStack; } - if (other.what & eCropChanged) { - what |= eCropChanged; - crop = other.crop; + if (other.what & eCropChanged_legacy) { + what |= eCropChanged_legacy; + crop_legacy = other.crop_legacy; } - if (other.what & eDeferTransaction) { - what |= eDeferTransaction; - barrierHandle = other.barrierHandle; - barrierGbp = other.barrierGbp; - frameNumber = other.frameNumber; + if (other.what & eCornerRadiusChanged) { + what |= eCornerRadiusChanged; + cornerRadius = other.cornerRadius; } - if (other.what & eFinalCropChanged) { - what |= eFinalCropChanged; - finalCrop = other.finalCrop; + if (other.what & eDeferTransaction_legacy) { + what |= eDeferTransaction_legacy; + barrierHandle_legacy = other.barrierHandle_legacy; + barrierGbp_legacy = other.barrierGbp_legacy; + frameNumber_legacy = other.frameNumber_legacy; } if (other.what & eOverrideScalingModeChanged) { what |= eOverrideScalingModeChanged; @@ -234,6 +312,71 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eDestroySurface) { what |= eDestroySurface; } + if (other.what & eTransformChanged) { + what |= eTransformChanged; + transform = other.transform; + } + if (other.what & eTransformToDisplayInverseChanged) { + what |= eTransformToDisplayInverseChanged; + transformToDisplayInverse = other.transformToDisplayInverse; + } + if (other.what & eCropChanged) { + what |= eCropChanged; + crop = other.crop; + } + if (other.what & eFrameChanged) { + what |= eFrameChanged; + frame = other.frame; + } + if (other.what & eBufferChanged) { + what |= eBufferChanged; + buffer = other.buffer; + } + if (other.what & eAcquireFenceChanged) { + what |= eAcquireFenceChanged; + acquireFence = other.acquireFence; + } + if (other.what & eDataspaceChanged) { + what |= eDataspaceChanged; + dataspace = other.dataspace; + } + if (other.what & eHdrMetadataChanged) { + what |= eHdrMetadataChanged; + hdrMetadata = other.hdrMetadata; + } + if (other.what & eSurfaceDamageRegionChanged) { + what |= eSurfaceDamageRegionChanged; + surfaceDamageRegion = other.surfaceDamageRegion; + } + if (other.what & eApiChanged) { + what |= eApiChanged; + api = other.api; + } + if (other.what & eSidebandStreamChanged) { + what |= eSidebandStreamChanged; + sidebandStream = other.sidebandStream; + } + if (other.what & eColorTransformChanged) { + what |= eColorTransformChanged; + colorTransform = other.colorTransform; + } + if (other.what & eListenerCallbacksChanged) { + what |= eListenerCallbacksChanged; + listenerCallbacks = other.listenerCallbacks; + } + +#ifndef NO_INPUT + if (other.what & eInputInfoChanged) { + what |= eInputInfoChanged; + inputInfo = other.inputInfo; + } +#endif + + if ((other.what & what) != other.what) { + ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " + "other.what=0x%" PRIu64 " what=0x%" PRIu64, + other.what, what); + } } }; // namespace android diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp index 52c906775e..2f8e104ea0 100644 --- a/libs/gui/StreamSplitter.cpp +++ b/libs/gui/StreamSplitter.cpp @@ -38,11 +38,11 @@ namespace android { 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 @@ StreamSplitter::~StreamSplitter() { 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 2de14c8846..00e23f0df1 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -156,7 +156,7 @@ status_t Surface::getDisplayRefreshCycleDuration(nsecs_t* outRefreshDuration) { ATRACE_CALL(); DisplayStatInfo stats; - status_t result = composerService()->getDisplayStats(NULL, &stats); + status_t result = composerService()->getDisplayStats(nullptr, &stats); if (result != NO_ERROR) { return result; } @@ -501,7 +501,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { 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 +541,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { 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 (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { freeAllBuffers(); @@ -619,7 +619,7 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, 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; } @@ -965,6 +965,9 @@ int Surface::perform(int operation, va_list args) 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 +1123,12 @@ int Surface::dispatchSetBuffersCta8613Metadata(va_list args) { return setBuffersCta8613Metadata(metadata); } +int Surface::dispatchSetBuffersHdr10PlusMetadata(va_list args) { + const size_t size = va_arg(args, size_t); + const uint8_t* metadata = va_arg(args, const uint8_t*); + return setBuffersHdr10PlusMetadata(size, metadata); +} + int Surface::dispatchSetSurfaceDamage(va_list args) { android_native_rect_t* rects = va_arg(args, android_native_rect_t*); size_t numRects = va_arg(args, size_t); @@ -1268,7 +1277,7 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, ATRACE_CALL(); ALOGV("Surface::detachNextBuffer"); - if (outBuffer == NULL || outFence == NULL) { + if (outBuffer == nullptr || outFence == nullptr) { return BAD_VALUE; } @@ -1277,8 +1286,8 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, 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 +1295,19 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, } *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 +1358,7 @@ int Surface::setCrop(Rect const* rect) ATRACE_CALL(); Rect realRect(Rect::EMPTY_RECT); - if (rect == NULL || rect->isEmpty()) { + if (rect == nullptr || rect->isEmpty()) { realRect.clear(); } else { realRect = *rect; @@ -1568,6 +1577,19 @@ int Surface::setBuffersCta8613Metadata(const android_cta861_3_metadata* metadata 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 +1598,7 @@ Dataspace Surface::getBuffersDataSpace() { void Surface::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { - mSlots[i].buffer = 0; + mSlots[i].buffer = nullptr; } } @@ -1616,12 +1638,12 @@ static status_t copyBlt( // 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 +1691,7 @@ static status_t copyBlt( 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 +1723,7 @@ status_t Surface::lock( // 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 +1785,7 @@ status_t Surface::lock( status_t Surface::unlockAndPost() { - if (mLockedBuffer == 0) { + if (mLockedBuffer == nullptr) { ALOGE("Surface::unlockAndPost failed, no locked buffer"); return INVALID_OPERATION; } @@ -1777,7 +1799,7 @@ status_t Surface::unlockAndPost() 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 f3c6fd2f87..95862199eb 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,6 +42,10 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#ifndef NO_INPUT +#include <input/InputWindow.h> +#endif + #include <private/gui/ComposerService.h> namespace android { @@ -60,7 +66,7 @@ void ComposerService::connectLocked() { 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 +87,9 @@ void ComposerService::connectLocked() { /*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,8 +98,63 @@ void ComposerService::connectLocked() { void ComposerService::composerServiceDied() { Mutex::Autolock _l(mLock); - mComposerService = NULL; - mDeathObserver = NULL; + mComposerService = nullptr; + mDeathObserver = nullptr; +} + +// --------------------------------------------------------------------------- + +// 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::addCallback(const TransactionCompletedCallback& callback) { + std::lock_guard<std::mutex> lock(mMutex); + startListeningLocked(); + + CallbackId callbackId = getNextIdLocked(); + mCallbacks.emplace(callbackId, callback); + return callbackId; +} + +void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { + std::lock_guard lock(mMutex); + + for (const auto& [callbackIds, transactionStats] : listenerStats.transactionStats) { + for (auto callbackId : callbackIds) { + const auto& callback = mCallbacks[callbackId]; + if (!callback) { + ALOGE("cannot call null callback function, skipping"); + continue; + } + callback(transactionStats); + mCallbacks.erase(callbackId); + } + } } // --------------------------------------------------------------------------- @@ -127,6 +188,17 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr } 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(); + return *this; } @@ -137,6 +209,32 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + // For every listener with registered callbacks + for (const auto& [listener, callbackInfo] : mListenerCallbacks) { + auto& [callbackIds, surfaceControls] = callbackInfo; + if (callbackIds.empty()) { + continue; + } + + // If the listener does not have any SurfaceControls set on this Transaction, send the + // callback now + if (surfaceControls.empty()) { + listener->onTransactionCompleted(ListenerStats::createEmpty(listener, callbackIds)); + } + + // If the listener has any SurfaceControls set on this Transaction update the surface state + for (const auto& surfaceControl : surfaceControls) { + layer_state_t* s = getLayerState(surfaceControl); + if (!s) { + ALOGE("failed to get layer state"); + continue; + } + s->what |= layer_state_t::eListenerCallbacksChanged; + s->listenerCallbacks.emplace_back(listener, std::move(callbackIds)); + } + } + mListenerCallbacks.clear(); + Vector<ComposerState> composerStates; Vector<DisplayState> displayStates; uint32_t flags = 0; @@ -206,6 +304,11 @@ layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<Surfac return &(mComposerStates[sc].state); } +void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( + const sp<SurfaceControl>& sc) { + mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls.insert(sc); +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition( const sp<SurfaceControl>& sc, float x, float y) { layer_state_t* s = getLayerState(sc); @@ -216,6 +319,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosit s->what |= layer_state_t::ePositionChanged; s->x = x; s->y = y; + + registerSurfaceControlForCallback(sc); return *this; } @@ -240,9 +345,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSize( s->w = w; s->h = h; - // Resizing a surface makes the transaction synchronous. - mForceSynchronous = true; - + registerSurfaceControlForCallback(sc); return *this; } @@ -255,6 +358,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer } s->what |= layer_state_t::eLayerChanged; s->z = z; + + registerSurfaceControlForCallback(sc); return *this; } @@ -267,6 +372,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelat s->what |= layer_state_t::eRelativeLayerChanged; s->relativeLayerHandle = relativeTo; s->z = z; + + registerSurfaceControlForCallback(sc); return *this; } @@ -286,6 +393,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; + + registerSurfaceControlForCallback(sc); return *this; } @@ -299,6 +408,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrans } s->what |= layer_state_t::eTransparentRegionChanged; s->transparentRegion = transparentRegion; + + registerSurfaceControlForCallback(sc); return *this; } @@ -311,6 +422,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha } s->what |= layer_state_t::eAlphaChanged; s->alpha = alpha; + + registerSurfaceControlForCallback(sc); return *this; } @@ -323,6 +436,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer } s->what |= layer_state_t::eLayerStackChanged; s->layerStack = layerStack; + + registerSurfaceControlForCallback(sc); return *this; } @@ -341,57 +456,68 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMatri 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 +531,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent } s->what |= layer_state_t::eReparentChildren; s->reparentHandle = newParentHandle; + + registerSurfaceControlForCallback(sc); return *this; } @@ -418,6 +546,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent } s->what |= layer_state_t::eReparent; s->parentHandleForChild = newParentHandle; + + registerSurfaceControlForCallback(sc); return *this; } @@ -431,6 +561,177 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColor } s->what |= layer_state_t::eColorChanged; s->color = color; + + 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); + 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::addTransactionCompletedCallback( + TransactionCompletedCallbackTakesContext callback, void* callbackContext) { + auto listener = TransactionCompletedListener::getInstance(); + + auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1); + + CallbackId callbackId = listener->addCallback(callbackWithContext); + + mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace( + callbackId); return *this; } @@ -441,6 +742,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachCh mStatus = BAD_INDEX; } s->what |= layer_state_t::eDetachChildren; + + registerSurfaceControlForCallback(sc); return *this; } @@ -468,6 +771,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverr s->what |= layer_state_t::eOverrideScalingModeChanged; s->overrideScalingMode = overrideScalingMode; + + registerSurfaceControlForCallback(sc); return *this; } @@ -479,8 +784,25 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeome return *this; } s->what |= layer_state_t::eGeometryAppliesWithResize; + + registerSurfaceControlForCallback(sc); + return *this; +} + +#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->inputInfo = info; + s->what |= layer_state_t::eInputInfoChanged; return *this; } +#endif SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::destroySurface( const sp<SurfaceControl>& sc) { @@ -493,6 +815,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::destroyS return *this; } +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; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -571,12 +907,12 @@ SurfaceComposerClient::SurfaceComposerClient(const sp<ISurfaceComposerClient>& c void SurfaceComposerClient::onFirstRef() { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - if (sf != 0 && mStatus == NO_INIT) { + if (sf != nullptr && mStatus == NO_INIT) { auto rootProducer = mParent.promote(); sp<ISurfaceComposerClient> conn; conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) : sf->createConnection(); - if (conn != 0) { + if (conn != nullptr) { mClient = conn; mStatus = NO_ERROR; } @@ -606,7 +942,7 @@ void SurfaceComposerClient::dispose() { // 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(); } @@ -718,10 +1054,6 @@ status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, 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); } @@ -749,6 +1081,14 @@ void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token, 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); +} + status_t SurfaceComposerClient::clearAnimationFrameStats() { return ComposerService::getComposerService()->clearAnimationFrameStats(); } @@ -763,16 +1103,39 @@ status_t SurfaceComposerClient::getHdrCapabilities(const sp<IBinder>& display, 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 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) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureScreen(display, outBuffer, sourceCrop, reqWidth, reqHeight, minLayerZ, - maxLayerZ, useIdentityTransform, + if (s == nullptr) return NO_INIT; + status_t ret = s->captureScreen(display, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop, + reqWidth, reqHeight, useIdentityTransform, static_cast<ISurfaceComposer::Rotation>(rotation)); if (ret != NO_ERROR) { return ret; @@ -780,21 +1143,25 @@ status_t ScreenshotClient::capture(const sp<IBinder>& display, Rect sourceCrop, return ret; } -status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, +status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale, - false /* childrenOnly */); + if (s == nullptr) return NO_INIT; + status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, + sourceCrop, frameScale, false /* childrenOnly */); return ret; } -status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, +status_t ScreenshotClient::captureChildLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == NULL) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, sourceCrop, frameScale, - true /* childrenOnly */); + if (s == nullptr) return NO_INIT; + status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, + sourceCrop, frameScale, true /* childrenOnly */); return ret; } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 5eafbb3555..3a6dfdada5 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -86,7 +86,7 @@ void SurfaceControl::clear() } void SurfaceControl::disconnect() { - if (mGraphicBufferProducer != NULL) { + if (mGraphicBufferProducer != nullptr) { mGraphicBufferProducer->disconnect( BufferQueueCore::CURRENTLY_CONNECTED_API); } @@ -95,28 +95,28 @@ void SurfaceControl::disconnect() { 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 +128,7 @@ status_t SurfaceControl::writeSurfaceToParcel( 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 +146,7 @@ sp<Surface> SurfaceControl::generateSurfaceLocked() const sp<Surface> SurfaceControl::getSurface() const { Mutex::Autolock _l(mLock); - if (mSurfaceData == 0) { + if (mSurfaceData == nullptr) { return generateSurfaceLocked(); } return mSurfaceData; diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp index afa15c5cda..fcae05c8ad 100644 --- a/libs/gui/SyncFeatures.cpp +++ b/libs/gui/SyncFeatures.cpp @@ -41,7 +41,7 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), // This can only be called after EGL has been initialized; otherwise the // check below will abort. const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - LOG_ALWAYS_FATAL_IF(exts == NULL, "eglQueryStringImplementationANDROID failed"); + LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { // This makes GLConsumer use the EGL_ANDROID_native_fence_sync // extension to create Android native fences to signal when all diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp index b1e44bb8ad..e64ba9bcdd 100644 --- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp @@ -100,7 +100,12 @@ inline int native_handle_read_fd(native_handle_t const* nh, int index = 0) { */ // 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 @@ inline status_t toStatusT(Return<Status> const& t) { */ // 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/include/gui/BufferHubProducer.h b/libs/gui/include/gui/BufferHubProducer.h index 23c9909826..f7af19b20e 100644 --- a/libs/gui/include/gui/BufferHubProducer.h +++ b/libs/gui/include/gui/BufferHubProducer.h @@ -165,6 +165,10 @@ private: // 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_; diff --git a/libs/gui/include/gui/CpuConsumer.h b/libs/gui/include/gui/CpuConsumer.h index d375611e5b..806fbe8aa0 100644 --- a/libs/gui/include/gui/CpuConsumer.h +++ b/libs/gui/include/gui/CpuConsumer.h @@ -70,7 +70,7 @@ class CpuConsumer : public ConsumerBase uint32_t chromaStep; LockedBuffer() : - data(NULL), + data(nullptr), width(0), height(0), format(PIXEL_FORMAT_NONE), @@ -82,8 +82,8 @@ class CpuConsumer : public ConsumerBase 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/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h index e06e40f2a6..df02494bf4 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h @@ -30,7 +30,6 @@ namespace android { struct FrameEvents; class FrameEventHistoryDelta; -class String8; // Identifiers for all the events that may be recorded or reported. @@ -72,7 +71,7 @@ struct FrameEvents { 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 @@ public: 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 71ed3bf239..46a99e124c 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -140,7 +140,8 @@ public: // 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 @@ private: // 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 @@ private: 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 @@ private: // 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 b020ed9b6a..7aa54321fd 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 9800602d6c..1e9c3e7102 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 @@ namespace android { 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 887654e05b..8ff8d81cf6 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -345,7 +345,7 @@ public: *outScalingMode = scalingMode; *outTransform = transform; *outFence = fence; - if (outStickyTransform != NULL) { + if (outStickyTransform != nullptr) { *outStickyTransform = stickyTransform; } if (outGetFrameTimestamps) { diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 99a3a75502..3052c0b312 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -27,10 +27,11 @@ #include <binder/IInterface.h> +#include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> -#include <ui/PixelFormat.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> +#include <ui/PixelFormat.h> #include <vector> @@ -157,9 +158,6 @@ public: 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; @@ -174,21 +172,86 @@ public: virtual status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode) = 0; - /* Capture the specified screen. requires READ_FRAME_BUFFER permission - * This function will fail if there is a secure window on screen. + /** + * 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. + * The subregion can be optionally rotated. It will also be scaled to + * match the size of the output buffer. + * + * reqDataspace and reqPixelFormat specify the data space and pixel format + * of the buffer. The caller should pick the data space and pixel format + * that it can consume. + * + * At the moment, sourceCrop is ignored and is always set to the visible + * region (projected display viewport) of the screen. + * + * reqWidth and reqHeight specifies the size of the buffer. When either + * of them is 0, they are set to the size of the logical display viewport. + * + * When useIdentityTransform is true, layer transformations are disabled. + * + * rotation specifies the rotation of the source crop (and the pixels in + * it) around its center. */ virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, Rotation rotation = eRotateNone) = 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. + * + * 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, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + bool useIdentityTransform, Rotation rotation = eRotateNone) { + return captureScreen(display, outBuffer, ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, + sourceCrop, reqWidth, reqHeight, useIdentityTransform, rotation); + } /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. + * + * reqDataspace and reqPixelFormat specify the data space and pixel format + * of the buffer. The caller should pick the data space and pixel format + * that it can consume. */ virtual status_t captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, + sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale = 1.0, bool childrenOnly = false) = 0; + /** + * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format, + * potentially ignoring the root node. + */ + status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, + const Rect& sourceCrop, float frameScale = 1.0, + bool childrenOnly = false) { + return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, sourceCrop, frameScale, childrenOnly); + } + /* Clears the frame statistics for animations. * * Requires the ACCESS_SURFACE_FLINGER permission. @@ -217,18 +280,55 @@ public: * 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; }; // ---------------------------------------------------------------------------- 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, @@ -239,7 +339,7 @@ public: 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, @@ -254,7 +354,11 @@ public: 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, }; 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 b4fd956b0f..82b01b8cf0 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -40,8 +40,9 @@ public: eProtectedByDRM = 0x00001000, eCursorWindow = 0x00002000, - eFXSurfaceNormal = 0x00000000, + eFXSurfaceBufferQueue = 0x00000000, eFXSurfaceColor = 0x00020000, + eFXSurfaceBufferState = 0x00040000, eFXSurfaceContainer = 0x00080000, eFXSurfaceMask = 0x000F0000, }; diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h new file mode 100644 index 0000000000..8acfa7a8f6 --- /dev/null +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -0,0 +1,116 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <binder/SafeInterface.h> + +#include <ui/Fence.h> +#include <utils/Timers.h> + +#include <cstdint> +#include <unordered_map> +#include <unordered_set> + +namespace android { + +class ITransactionCompletedListener; + +using CallbackId = int64_t; + +struct CallbackIdsHash { + // CallbackId vectors have several properties that let us get away with this simple hash. + // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is + // empty we can still hash 0. + // 2) CallbackId vectors for the same listener either are identical or contain none of the + // same members. It is sufficient to just check the first CallbackId in the vectors. If + // they match, they are the same. If they do not match, they are not the same. + std::size_t operator()(const std::vector<CallbackId> callbackIds) const { + return std::hash<CallbackId>{}((callbackIds.size() == 0) ? 0 : callbackIds.front()); + } +}; + +class SurfaceStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + SurfaceStats() = default; + SurfaceStats(const sp<IBinder>& sc, nsecs_t time, bool releasePrevBuffer) + : surfaceControl(sc), acquireTime(time), releasePreviousBuffer(releasePrevBuffer) {} + + sp<IBinder> surfaceControl; + nsecs_t acquireTime = -1; + bool releasePreviousBuffer = false; +}; + +class TransactionStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + nsecs_t latchTime = -1; + sp<Fence> presentFence = nullptr; + std::vector<SurfaceStats> surfaceStats; +}; + +class ListenerStats : public Parcelable { +public: + status_t writeToParcel(Parcel* output) const override; + status_t readFromParcel(const Parcel* input) override; + + static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbackIds); + + sp<ITransactionCompletedListener> listener; + std::unordered_map<std::vector<CallbackId>, TransactionStats, CallbackIdsHash> transactionStats; +}; + +class ITransactionCompletedListener : public IInterface { +public: + DECLARE_META_INTERFACE(TransactionCompletedListener) + + virtual void onTransactionCompleted(ListenerStats stats) = 0; +}; + +class BnTransactionCompletedListener : public SafeBnInterface<ITransactionCompletedListener> { +public: + BnTransactionCompletedListener() + : SafeBnInterface<ITransactionCompletedListener>("BnTransactionCompletedListener") {} + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; +}; + +class ListenerCallbacks { +public: + ListenerCallbacks(const sp<ITransactionCompletedListener>& listener, + const std::unordered_set<CallbackId>& callbacks) + : transactionCompletedListener(listener), + callbackIds(callbacks.begin(), callbacks.end()) {} + + ListenerCallbacks(const sp<ITransactionCompletedListener>& listener, + const std::vector<CallbackId>& ids) + : transactionCompletedListener(listener), callbackIds(ids) {} + + sp<ITransactionCompletedListener> transactionCompletedListener; + std::vector<CallbackId> callbackIds; +}; + +} // namespace android diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h index 92bd8c5b28..66a7b4dc06 100644 --- a/libs/gui/include/gui/LayerDebugInfo.h +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -52,7 +52,6 @@ public: 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/LayerState.h b/libs/gui/include/gui/LayerState.h index 788962e490..02c6be25c7 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -22,10 +22,18 @@ #include <utils/Errors.h> -#include <ui/Region.h> -#include <ui/Rect.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/ITransactionCompletedListener.h> +#include <math/mat4.h> + +#ifndef NO_INPUT +#include <input/InputWindow.h> +#endif + #include <math/vec3.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> namespace android { @@ -36,113 +44,159 @@ class ISurfaceComposerClient; * Used to communicate layer information between SurfaceFlinger and its clients. */ struct layer_state_t { - - enum { - eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java - eLayerOpaque = 0x02, // SURFACE_OPAQUE - eLayerSecure = 0x80, // SECURE + eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java + eLayerOpaque = 0x02, // SURFACE_OPAQUE + eLayerSecure = 0x80, // SECURE }; enum { - ePositionChanged = 0x00000001, - eLayerChanged = 0x00000002, - eSizeChanged = 0x00000004, - eAlphaChanged = 0x00000008, - eMatrixChanged = 0x00000010, - eTransparentRegionChanged = 0x00000020, - eFlagsChanged = 0x00000040, - eLayerStackChanged = 0x00000080, - eCropChanged = 0x00000100, - eDeferTransaction = 0x00000200, - eFinalCropChanged = 0x00000400, - eOverrideScalingModeChanged = 0x00000800, - eGeometryAppliesWithResize = 0x00001000, - eReparentChildren = 0x00002000, - eDetachChildren = 0x00004000, - eRelativeLayerChanged = 0x00008000, - eReparent = 0x00010000, - eColorChanged = 0x00020000, - eDestroySurface = 0x00040000 + ePositionChanged = 0x00000001, + eLayerChanged = 0x00000002, + eSizeChanged = 0x00000004, + eAlphaChanged = 0x00000008, + eMatrixChanged = 0x00000010, + eTransparentRegionChanged = 0x00000020, + eFlagsChanged = 0x00000040, + eLayerStackChanged = 0x00000080, + eCropChanged_legacy = 0x00000100, + eDeferTransaction_legacy = 0x00000200, + eOverrideScalingModeChanged = 0x00000400, + eGeometryAppliesWithResize = 0x00000800, + eReparentChildren = 0x00001000, + eDetachChildren = 0x00002000, + eRelativeLayerChanged = 0x00004000, + eReparent = 0x00008000, + eColorChanged = 0x00010000, + eDestroySurface = 0x00020000, + eTransformChanged = 0x00040000, + eTransformToDisplayInverseChanged = 0x00080000, + eCropChanged = 0x00100000, + eBufferChanged = 0x00200000, + eAcquireFenceChanged = 0x00400000, + eDataspaceChanged = 0x00800000, + eHdrMetadataChanged = 0x01000000, + eSurfaceDamageRegionChanged = 0x02000000, + eApiChanged = 0x04000000, + eSidebandStreamChanged = 0x08000000, + eColorTransformChanged = 0x10000000, + eListenerCallbacksChanged = 0x20000000, + eInputInfoChanged = 0x40000000, + eCornerRadiusChanged = 0x80000000, + eFrameChanged = 0x1'00000000, }; 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()) { 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); - - 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; - - sp<IGraphicBufferProducer> barrierGbp; - - sp<IBinder> relativeLayerHandle; - - sp<IBinder> parentHandleForChild; - - half3 color; - - // non POD must be last. see write/read - Region transparentRegion; + 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; + 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_legacy; + + sp<IBinder> relativeLayerHandle; + + sp<IBinder> parentHandleForChild; + + half3 color; + + // non POD must be last. see write/read + Region transparentRegion; + + uint32_t transform; + bool transformToDisplayInverse; + Rect crop; + Rect frame; + sp<GraphicBuffer> buffer; + sp<Fence> acquireFence; + ui::Dataspace dataspace; + HdrMetadata hdrMetadata; + Region surfaceDamageRegion; + int32_t api; + sp<NativeHandle> sidebandStream; + mat4 colorTransform; + + std::vector<ListenerCallbacks> listenerCallbacks; +#ifndef NO_INPUT + InputWindowInfo inputInfo; +#endif }; 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(); @@ -152,29 +206,40 @@ struct DisplayState { sp<IBinder> token; sp<IGraphicBufferProducer> surface; uint32_t layerStack; + + // These states define how layers are projected onto the physical display. + // + // Layers are first clipped to `viewport'. They are then translated and + // scaled from `viewport' to `frame'. Finally, they are rotated according + // to `orientation', `width', and `height'. + // + // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20, + // 10, 420, 210), and the size of the display is WxH. When orientation is + // 0, layers will be scaled by a factor of 2 and translated by (20, 10). + // When orientation is 1, layers will be additionally rotated by 90 + // degrees around the origin clockwise and translated by (W, 0). uint32_t orientation; Rect viewport; Rect frame; + uint32_t width, height; + status_t write(Parcel& output) const; status_t read(const Parcel& input); }; -static inline -int compare_type(const ComposerState& lhs, const ComposerState& rhs) { +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 9aeafae198..248e105d04 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -80,7 +80,7 @@ public: /* 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 @@ private: int dispatchSetBuffersDataSpace(va_list args); int dispatchSetBuffersSmpte2086Metadata(va_list args); int dispatchSetBuffersCta8613Metadata(va_list args); + int dispatchSetBuffersHdr10PlusMetadata(va_list args); int dispatchSetSurfaceDamage(va_list args); int dispatchSetSharedBufferMode(va_list args); int dispatchSetAutoRefresh(va_list args); @@ -249,6 +250,7 @@ protected: 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 ad8a8b09d0..8e3ba78108 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,16 @@ #include <utils/SortedVector.h> #include <utils/threads.h> +#include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> #include <gui/CpuConsumer.h> +#include <gui/ITransactionCompletedListener.h> +#include <gui/LayerState.h> #include <gui/SurfaceControl.h> #include <math/vec3.h> -#include <gui/LayerState.h> namespace android { @@ -49,6 +53,37 @@ class Region; // --------------------------------------------------------------------------- +using TransactionCompletedCallbackTakesContext = + std::function<void(void* /*context*/, const TransactionStats&)>; +using TransactionCompletedCallback = std::function<void(const TransactionStats&)>; + +class TransactionCompletedListener : public BnTransactionCompletedListener { + TransactionCompletedListener(); + + CallbackId getNextIdLocked() REQUIRES(mMutex); + + std::mutex mMutex; + + bool mListening GUARDED_BY(mMutex) = false; + + CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1; + + std::map<CallbackId, TransactionCompletedCallback> mCallbacks GUARDED_BY(mMutex); + +public: + static sp<TransactionCompletedListener> getInstance(); + static sp<ITransactionCompletedListener> getIInstance(); + + void startListeningLocked() REQUIRES(mMutex); + + CallbackId addCallback(const TransactionCompletedCallback& callback); + + // Overrides BnTransactionCompletedListener's onTransactionCompleted + void onTransactionCompleted(ListenerStats stats) override; +}; + +// --------------------------------------------------------------------------- + class SurfaceComposerClient : public RefBase { friend class Composer; @@ -69,7 +104,7 @@ public: // 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 +114,6 @@ public: 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); @@ -104,6 +136,16 @@ public: /* 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); + // ------------------------------------------------------------------------ // surface creation / destruction @@ -151,9 +193,27 @@ public: } }; + 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; @@ -164,6 +224,8 @@ public: layer_state_t* getLayerState(const sp<SurfaceControl>& sc); DisplayState& getDisplayState(const sp<IBinder>& token); + void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc); + public: Transaction() = default; virtual ~Transaction() = default; @@ -203,22 +265,21 @@ public: 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); // 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 +292,24 @@ public: Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color); + 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& 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& 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,8 +333,16 @@ public: // freezing the total geometry of a surface until a resize is completed. Transaction& setGeometryAppliesWithResize(const sp<SurfaceControl>& sc); +#ifndef NO_INPUT + Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info); +#endif + Transaction& destroySurface(const sp<SurfaceControl>& sc); + // 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); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -297,6 +384,16 @@ public: 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); + private: virtual void onFirstRef(); @@ -312,17 +409,21 @@ class ScreenshotClient { public: // if cropping isn't required, callers may pass in a default Rect, e.g.: // capture(display, producer, Rect(), reqWidth, ...); - static status_t capture(const sp<IBinder>& display, Rect sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, int32_t minLayerZ, int32_t maxLayerZ, - bool useIdentityTransform, uint32_t rotation, - sp<GraphicBuffer>* outBuffer); - static status_t captureLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, float frameScale, - sp<GraphicBuffer>* outBuffer); - static status_t captureChildLayers(const sp<IBinder>& layerHandle, Rect sourceCrop, + static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, sp<GraphicBuffer>* outBuffer); + static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + float frameScale, sp<GraphicBuffer>* outBuffer); + static status_t captureChildLayers(const sp<IBinder>& layerHandle, + const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, float frameScale, sp<GraphicBuffer>* outBuffer); }; // --------------------------------------------------------------------------- + }; // 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 bd987dd638..ccb30fa8e7 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -48,11 +48,11 @@ public: 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( diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 01e90e0eb8..f020a4067d 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -16,6 +16,8 @@ cc_test { "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", + "EndToEndNativeInputTest.cpp", + "DisplayedContentSampling_test.cpp", "FillBuffer.cpp", "GLTest.cpp", "IGraphicBufferProducer_test.cpp", @@ -35,6 +37,7 @@ cc_test { shared_libs: [ "android.hardware.configstore@1.0", "android.hardware.configstore-utils", + "libbase", "liblog", "libEGL", "libGLESv1_CM", @@ -44,15 +47,19 @@ cc_test { "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 +68,6 @@ cc_test { "-Werror", ], - test_per_src: true, srcs: [ "SurfaceParcelable_test.cpp", ], diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 9a208593ab..119e888edb 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -61,7 +61,7 @@ protected: } 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 @@ protected: 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 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { 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 @@ TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { 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 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { 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 36be7d9368..00e32d9124 100644 --- a/libs/gui/tests/CpuConsumer_test.cpp +++ b/libs/gui/tests/CpuConsumer_test.cpp @@ -484,12 +484,12 @@ void produceOneFrame(const sp<ANativeWindow>& anw, 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 @@ TEST_P(CpuConsumerTest, FromCpuSingle) { 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 @@ TEST_P(CpuConsumerTest, FromCpuManyInQueue) { 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 @@ TEST_P(CpuConsumerTest, FromCpuLockMax) { 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 @@ TEST_P(CpuConsumerTest, FromCpuLockMax) { 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 0000000000..5443812bff --- /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->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); + 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 0000000000..60542bd47c --- /dev/null +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -0,0 +1,443 @@ +/* + * 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()); + } + + ~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()); + + DisplayInfo info; + auto display = mComposerClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); + SurfaceComposerClient::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); + } +} + +TEST_F(InputSurfacesTest, can_receive_input) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + injectTap(101, 101); + + EXPECT_TRUE(surface->consumeEvent() != nullptr); +} + +TEST_F(InputSurfacesTest, input_respects_positioning) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + + std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); + surface2->showAt(200, 200); + + injectTap(201, 201); + surface2->expectTap(1, 1); + + injectTap(101, 101); + surface->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.setPosition(sc, 100, 100); + }); + surface->doTransaction([](auto &t, auto &sc) { + t.setPosition(sc, 200, 200); + }); + + injectTap(101, 101); + surface2->expectTap(1, 1); + + injectTap(201, 201); + surface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_layering) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); + + surface->showAt(10, 10); + surface2->showAt(10, 10); + + surface->doTransaction([](auto &t, auto &sc) { + t.setLayer(sc, LAYER_BASE + 1); + }); + + injectTap(11, 11); + surface->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.setLayer(sc, LAYER_BASE + 1); + }); + + injectTap(11, 11); + surface2->expectTap(1, 1); + + surface2->doTransaction([](auto &t, auto &sc) { + t.hide(sc); + }); + + injectTap(11, 11); + surface->expectTap(1, 1); +} + +// Surface Insets are set to offset the client content and draw a border around the client surface +// (such as shadows in dialogs). Inputs sent to the client are offset such that 0,0 is the start +// of the client content. +TEST_F(InputSurfacesTest, input_respects_surface_insets) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + bgSurface->showAt(100, 100); + + fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->showAt(100, 100); + + injectTap(106, 106); + fgSurface->expectTap(1, 1); + + injectTap(101, 101); + bgSurface->expectTap(1, 1); +} + +// Ensure a surface whose insets are cropped, handles the touch offset correctly. ref:b/120413463 +TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { + std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100); + parentSurface->showAt(100, 100); + + childSurface->mInputInfo.surfaceInset = 10; + childSurface->showAt(100, 100); + + childSurface->doTransaction([&](auto &t, auto &sc) { + t.setPosition(sc, -5, -5); + t.reparent(sc, parentSurface->mSurfaceControl->getHandle()); + }); + + injectTap(106, 106); + childSurface->expectTap(1, 1); + + injectTap(101, 101); + parentSurface->expectTap(1, 1); +} + +// Ensure we ignore transparent region when getting screen bounds when positioning input frame. +TEST_F(InputSurfacesTest, input_ignores_transparent_region) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->doTransaction([](auto &t, auto &sc) { + Region transparentRegion(Rect(0, 0, 10, 10)); + t.setTransparentRegionHint(sc, transparentRegion); + }); + surface->showAt(100, 100); + injectTap(101, 101); + surface->expectTap(1, 1); +} + +// Ensure we send the input to the right surface when the surface visibility changes due to the +// first buffer being submitted. ref: b/120839715 +TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); + + bgSurface->showAt(10, 10); + bufferSurface->showAt(10, 10); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); + + postBuffer(bufferSurface->mSurfaceControl); + injectTap(11, 11); + bufferSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> bufferSurface = + InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); + postBuffer(bufferSurface->mSurfaceControl); + + bgSurface->showAt(10, 10); + bufferSurface->showAt(10, 10); + + injectTap(11, 11); + bufferSurface->expectTap(1, 1); + + bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + + bgSurface->showAt(10, 10); + fgSurface->showAt(10, 10); + + injectTap(11, 11); + fgSurface->expectTap(1, 1); + + fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} + +TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { + std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); + std::unique_ptr<InputSurface> containerSurface = + InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); + + bgSurface->showAt(10, 10); + containerSurface->showAt(10, 10); + + injectTap(11, 11); + containerSurface->expectTap(1, 1); + + containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); + + injectTap(11, 11); + bgSurface->expectTap(1, 1); +} +} +} diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp index ccd674fcb8..b60995a624 100644 --- a/libs/gui/tests/FillBuffer.cpp +++ b/libs/gui/tests/FillBuffer.cpp @@ -93,11 +93,11 @@ void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { 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 a91552f7fe..a1405fcb11 100644 --- a/libs/gui/tests/GLTest.cpp +++ b/libs/gui/tests/GLTest.cpp @@ -50,7 +50,7 @@ void GLTest::SetUp() { 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 @@ void GLTest::SetUp() { 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 @@ void GLTest::TearDown() { sleep(mDisplaySecs); } - if (mComposerClient != NULL) { + if (mComposerClient != nullptr) { mComposerClient->dispose(); } if (mEglContext != EGL_NO_CONTEXT) { @@ -171,7 +171,7 @@ EGLint GLTest::getSurfaceHeight() { 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 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, 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 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, 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 @@ void GLTest::loadShader(GLenum shaderType, const char* pSource, } 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 @@ void GLTest::createProgram(const char* pVertexSource, 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 f0d27a8a34..f290b3c68d 100644 --- a/libs/gui/tests/GLTest.h +++ b/libs/gui/tests/GLTest.h @@ -39,7 +39,7 @@ protected: 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 a35cf11174..aef7aed52c 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -228,9 +228,9 @@ protected: 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 @@ TEST_P(IGraphicBufferProducerTest, ConnectFirst_ReturnsError) { 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 @@ TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) { // 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 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // Fence was NULL { - sp<Fence> nullFence = NULL; + sp<Fence> nullFence = nullptr; IGraphicBufferProducer::QueueBufferInput input = QueueBufferInputBuilder().setFence(nullFence).build(); @@ -695,10 +695,7 @@ TEST_P(IGraphicBufferProducerTest, 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 @@ TEST_P(IGraphicBufferProducerTest, 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 @@ TEST_P(IGraphicBufferProducerTest, 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 3a25ac59ca..7d3d4aa412 100644 --- a/libs/gui/tests/MultiTextureConsumer_test.cpp +++ b/libs/gui/tests/MultiTextureConsumer_test.cpp @@ -47,7 +47,7 @@ protected: GLTest::TearDown(); } virtual EGLint const* getContextAttribs() { - return NULL; + return nullptr; } virtual EGLint const* getConfigAttribs() { static EGLint sDefaultConfigAttribs[] = { @@ -105,7 +105,7 @@ TEST_F(MultiTextureConsumerTest, EGLImageTargetWorks) { 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/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index d5b2f004ed..65e09f2540 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 @@ protected: mEglDisplay(EGL_NO_DISPLAY), mEglSurface(EGL_NO_SURFACE), mEglContext(EGL_NO_CONTEXT), - mEglConfig(NULL) { + mEglConfig(nullptr) { } virtual void SetUp() { @@ -82,7 +81,7 @@ protected: 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 @@ protected: 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 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { 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 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { 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, EglCreateWindowSurfaceSucceeds) { 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, GetTransformMatrixSucceedsAfterFreeingBuffers) } 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 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi 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 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi 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 @@ protected: 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 @@ protected: 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 7f1ae84c48..70f988de11 100644 --- a/libs/gui/tests/SurfaceTextureFBO.h +++ b/libs/gui/tests/SurfaceTextureFBO.h @@ -34,7 +34,7 @@ protected: 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 0134273a07..f34561f668 100644 --- a/libs/gui/tests/SurfaceTextureFBO_test.cpp +++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp @@ -39,12 +39,12 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { 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 @@ TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { 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 2ce20eb2b1..03975b1261 100644 --- a/libs/gui/tests/SurfaceTextureGLThreadToGL.h +++ b/libs/gui/tests/SurfaceTextureGLThreadToGL.h @@ -158,7 +158,7 @@ protected: } virtual void TearDown() { - if (mProducerThread != NULL) { + if (mProducerThread != nullptr) { mProducerThread->requestExitAndWait(); } mProducerThread.clear(); @@ -167,7 +167,7 @@ protected: } 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 5d43a48898..3a87c12cf6 100644 --- a/libs/gui/tests/SurfaceTextureGLToGL.h +++ b/libs/gui/tests/SurfaceTextureGLToGL.h @@ -38,7 +38,7 @@ protected: 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 56392867ea..e2b4f3d035 100644 --- a/libs/gui/tests/SurfaceTextureGL_test.cpp +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -40,12 +40,12 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { 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 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { 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 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { 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 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } @@ -248,7 +248,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { 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 @@ TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) @@ -641,7 +641,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { &anb) != NO_ERROR) { return false; } - if (anb == NULL) { + if (anb == nullptr) { return false; } if (mANW->queueBuffer(mANW.get(), anb, -1) @@ -654,7 +654,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { &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 6e196bfac8..67afbd6a52 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -72,7 +72,7 @@ protected: 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 @@ protected: .apply()); mSurface = mSurfaceControl->getSurface(); - ASSERT_TRUE(mSurface != NULL); + ASSERT_TRUE(mSurface != nullptr); } virtual void TearDown() { @@ -134,8 +134,9 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { sp<IBinder> display(sf->getBuiltInDisplay( ISurfaceComposer::eDisplayIdMain)); sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(), - 64, 64, 0, 0x7fffffff, false)); + ASSERT_EQ(NO_ERROR, + sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); @@ -145,7 +146,7 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), GRALLOC_USAGE_PROTECTED)); ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); - ANativeWindowBuffer* buf = 0; + ANativeWindowBuffer* buf = nullptr; status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf); if (err) { @@ -165,8 +166,9 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) { &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } - ASSERT_EQ(NO_ERROR, sf->captureScreen(display, &outBuffer, Rect(), - 64, 64, 0, 0x7fffffff, false)); + ASSERT_EQ(NO_ERROR, + sf->captureScreen(display, &outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { @@ -205,7 +207,7 @@ TEST_F(SurfaceTest, QueryConsumerUsage) { } TEST_F(SurfaceTest, QueryDefaultBuffersDataSpace) { - const android_dataspace TEST_DATASPACE = HAL_DATASPACE_SRGB; + const android_dataspace TEST_DATASPACE = HAL_DATASPACE_V0_SRGB; sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); @@ -364,10 +366,17 @@ TEST_F(SurfaceTest, SetHdrMetadata) { 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) { @@ -581,9 +590,6 @@ public: 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 { @@ -599,15 +605,19 @@ public: } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } - status_t captureScreen(const sp<IBinder>& /*display*/, - sp<GraphicBuffer>* /*outBuffer*/, - Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, - int32_t /*minLayerZ*/, int32_t /*maxLayerZ*/, - bool /*useIdentityTransform*/, - Rotation /*rotation*/) override { return NO_ERROR; } + status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/, + const ui::Dataspace /*reqDataspace*/, + const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/, + uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, + bool /*useIdentityTransform*/, Rotation /*rotation*/) override { + return NO_ERROR; + } virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/, - sp<GraphicBuffer>* /*outBuffer*/, const Rect& /*sourceCrop*/, - float /*frameScale*/, bool /*childrenOnly*/) override { + sp<GraphicBuffer>* /*outBuffer*/, + const ui::Dataspace /*reqDataspace*/, + const ui::PixelFormat /*reqPixelFormat*/, + const Rect& /*sourceCrop*/, float /*frameScale*/, + bool /*childrenOnly*/) override { return NO_ERROR; } status_t clearAnimationFrameStats() override { return NO_ERROR; } @@ -625,6 +635,30 @@ public: 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; + } + + virtual status_t getColorManagement(bool* /*outGetColorManagement*/) const { return NO_ERROR; } protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 2f399765a0..fc676f14e9 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -42,15 +42,18 @@ cc_library { target: { android: { srcs: [ - "IInputFlinger.cpp", "InputTransport.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", + "InputApplication.cpp", + "InputWindow.cpp", + "IInputFlinger.cpp" ], shared_libs: [ "libutils", "libbinder", + "libui" ], sanitize: { diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index 003e73dae6..139570a5fd 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,64 @@ public: explicit BpInputFlinger(const sp<IBinder>& impl) : BpInterface<IInputFlinger>(impl) { } - virtual status_t doSomething() { + virtual void setInputWindows(const Vector<InputWindowInfo>& inputInfo) { 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); + } + remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply, + IBinder::FLAG_ONEWAY); + } + + virtual void registerInputChannel(const sp<InputChannel>& channel) { + Parcel data, reply; + data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); + channel->write(data); + remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); + } + + virtual void unregisterInputChannel(const sp<InputChannel>& channel) { + Parcel data, reply; + data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); + channel->write(data); + remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); } }; IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger"); - status_t BnInputFlinger::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { - case DO_SOMETHING_TRANSACTION: { + case SET_INPUT_WINDOWS_TRANSACTION: { + CHECK_INTERFACE(IInputFlinger, data, reply); + size_t count = data.readUint32(); + if (count > data.dataSize()) { + return BAD_VALUE; + } + Vector<InputWindowInfo> handles; + handles.setCapacity(count); + for (size_t i = 0; i < count; i++) { + handles.add(InputWindowInfo(data)); + } + setInputWindows(handles); + 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); - reply->writeInt32(0); + sp<InputChannel> channel = new InputChannel(); + channel->read(data); + unregisterInputChannel(channel); break; } default: diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index a6246636a3..a558970b58 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -31,14 +31,16 @@ namespace android { // --- 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 +56,7 @@ int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { void KeyEvent::initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -62,7 +65,7 @@ void KeyEvent::initialize( 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 +131,24 @@ static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) } } -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,6 +227,7 @@ void PointerProperties::copyFrom(const PointerProperties& other) { void MotionEvent::initialize( int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t actionButton, int32_t flags, @@ -230,7 +243,7 @@ void MotionEvent::initialize( size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { - InputEvent::initialize(deviceId, source); + InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; mFlags = flags; @@ -250,7 +263,7 @@ void MotionEvent::initialize( } 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; @@ -341,15 +354,15 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { 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,6 +444,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mDeviceId = parcel->readInt32(); mSource = parcel->readInt32(); + mDisplayId = parcel->readInt32(); mAction = parcel->readInt32(); mActionButton = parcel->readInt32(); mFlags = parcel->readInt32(); @@ -480,6 +494,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mDeviceId); parcel->writeInt32(mSource); + parcel->writeInt32(mDisplayId); parcel->writeInt32(mAction); parcel->writeInt32(mActionButton); parcel->writeInt32(mFlags); diff --git a/services/inputflinger/InputApplication.cpp b/libs/input/InputApplication.cpp index 9e90631840..7936f50d54 100644 --- a/services/inputflinger/InputApplication.cpp +++ b/libs/input/InputApplication.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "InputApplication" -#include "InputApplication.h" +#include <input/InputApplication.h> #include <android/log.h> @@ -25,7 +25,7 @@ namespace android { // --- InputApplicationHandle --- InputApplicationHandle::InputApplicationHandle() : - mInfo(NULL) { + mInfo(nullptr) { } InputApplicationHandle::~InputApplicationHandle() { @@ -35,8 +35,25 @@ InputApplicationHandle::~InputApplicationHandle() { void InputApplicationHandle::releaseInfo() { if (mInfo) { delete mInfo; - mInfo = NULL; + mInfo = nullptr; } } +InputApplicationInfo InputApplicationInfo::read(const Parcel& from) { + InputApplicationInfo ret; + ret.token = from.readStrongBinder(); + ret.name = from.readString8().c_str(); + ret.dispatchingTimeout = from.readInt64(); + + return ret; +} + +status_t InputApplicationInfo::write(Parcel& output) const { + output.writeStrongBinder(token); + output.writeString8(String8(name.c_str())); + output.writeInt64(dispatchingTimeout); + + return OK; +} + } // namespace android diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 4287abeb7d..778c4539fa 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,8 +44,8 @@ static bool isValidNameChar(char ch) { return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); } -static void appendInputDeviceConfigurationFileRelativePath(String8& path, - const String8& name, InputDeviceConfigurationFileType type) { +static void appendInputDeviceConfigurationFileRelativePath(std::string& path, + const std::string& name, InputDeviceConfigurationFileType type) { path.append(CONFIGURATION_FILE_DIR[type]); for (size_t i = 0; i < name.length(); i++) { char ch = name[i]; @@ -54,28 +57,28 @@ static void appendInputDeviceConfigurationFileRelativePath(String8& path, path.append(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; } } @@ -84,22 +87,25 @@ String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, 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 +115,17 @@ String8 getInputDeviceConfigurationFilePathByName( // 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 +135,16 @@ String8 getInputDeviceConfigurationFilePathByName( // 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 ""; } // --- 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 +160,7 @@ InputDeviceInfo::~InputDeviceInfo() { } 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; @@ -175,7 +185,7 @@ const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( return ⦥ } } - return NULL; + return nullptr; } void InputDeviceInfo::addSource(uint32_t source) { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index aa0bf17ca3..f33b210c4c 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -30,6 +30,7 @@ #include <cutils/properties.h> #include <log/log.h> +#include <binder/Parcel.h> #include <input/InputTransport.h> namespace android { @@ -96,19 +97,117 @@ size_t InputMessage::size() const { return sizeof(Header); } +/** + * There could be non-zero bytes in-between InputMessage fields. Force-initialize the entire + * memory to zero, then only copy the valid bytes on a per-field basis. + */ +void InputMessage::getSanitizedCopy(InputMessage* msg) const { + memset(msg, 0, sizeof(*msg)); + + // Write the header + msg->header.type = header.type; + + // Write the body + switch(header.type) { + case InputMessage::TYPE_KEY: { + // uint32_t seq + msg->body.key.seq = body.key.seq; + // nsecs_t eventTime + msg->body.key.eventTime = body.key.eventTime; + // int32_t deviceId + msg->body.key.deviceId = body.key.deviceId; + // int32_t source + msg->body.key.source = body.key.source; + // int32_t displayId + msg->body.key.displayId = body.key.displayId; + // int32_t action + msg->body.key.action = body.key.action; + // int32_t flags + msg->body.key.flags = body.key.flags; + // int32_t keyCode + msg->body.key.keyCode = body.key.keyCode; + // int32_t scanCode + msg->body.key.scanCode = body.key.scanCode; + // int32_t metaState + msg->body.key.metaState = body.key.metaState; + // int32_t repeatCount + msg->body.key.repeatCount = body.key.repeatCount; + // nsecs_t downTime + msg->body.key.downTime = body.key.downTime; + break; + } + case InputMessage::TYPE_MOTION: { + // uint32_t seq + msg->body.motion.seq = body.motion.seq; + // nsecs_t eventTime + msg->body.motion.eventTime = body.motion.eventTime; + // int32_t deviceId + msg->body.motion.deviceId = body.motion.deviceId; + // int32_t source + msg->body.motion.source = body.motion.source; + // int32_t displayId + msg->body.motion.displayId = body.motion.displayId; + // int32_t action + msg->body.motion.action = body.motion.action; + // int32_t actionButton + msg->body.motion.actionButton = body.motion.actionButton; + // int32_t flags + msg->body.motion.flags = body.motion.flags; + // int32_t metaState + msg->body.motion.metaState = body.motion.metaState; + // int32_t buttonState + msg->body.motion.buttonState = body.motion.buttonState; + // int32_t edgeFlags + msg->body.motion.edgeFlags = body.motion.edgeFlags; + // nsecs_t downTime + msg->body.motion.downTime = body.motion.downTime; + // float xOffset + msg->body.motion.xOffset = body.motion.xOffset; + // float yOffset + msg->body.motion.yOffset = body.motion.yOffset; + // float xPrecision + msg->body.motion.xPrecision = body.motion.xPrecision; + // float yPrecision + msg->body.motion.yPrecision = body.motion.yPrecision; + // uint32_t pointerCount + msg->body.motion.pointerCount = body.motion.pointerCount; + //struct Pointer pointers[MAX_POINTERS] + for (size_t i = 0; i < body.motion.pointerCount; i++) { + // PointerProperties properties + msg->body.motion.pointers[i].properties.id = body.motion.pointers[i].properties.id; + msg->body.motion.pointers[i].properties.toolType = + body.motion.pointers[i].properties.toolType, + // PointerCoords coords + msg->body.motion.pointers[i].coords.bits = body.motion.pointers[i].coords.bits; + const uint32_t count = BitSet64::count(body.motion.pointers[i].coords.bits); + memcpy(&msg->body.motion.pointers[i].coords.values[0], + &body.motion.pointers[i].coords.values[0], + count * (sizeof(body.motion.pointers[i].coords.values[0]))); + } + break; + } + case InputMessage::TYPE_FINISHED: { + msg->body.finished.seq = body.finished.seq; + msg->body.finished.handled = body.finished.handled; + break; + } + default: { + LOG_FATAL("Unexpected message type %i", header.type); + break; + } + } +} // --- 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() { @@ -120,6 +219,18 @@ InputChannel::~InputChannel() { ::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]; @@ -149,10 +260,12 @@ status_t InputChannel::openInputChannelPair(const std::string& name, } status_t InputChannel::sendMessage(const InputMessage* msg) { - size_t msgLength = msg->size(); + const size_t msgLength = msg->size(); + InputMessage cleanMsg; + msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { @@ -226,10 +339,51 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { 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) : @@ -243,6 +397,7 @@ status_t InputPublisher::publishKeyEvent( uint32_t seq, int32_t deviceId, int32_t source, + int32_t displayId, int32_t action, int32_t flags, int32_t keyCode, @@ -270,6 +425,7 @@ status_t InputPublisher::publishKeyEvent( 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; @@ -303,13 +459,15 @@ status_t InputPublisher::publishMotionEvent( const PointerCoords* pointerCoords) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " + "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32, mChannel->getName().c_str(), seq, - deviceId, source, action, actionButton, flags, edgeFlags, metaState, buttonState, - xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); + deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, + buttonState, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, + pointerCount); #endif if (!seq) { @@ -384,7 +542,7 @@ InputConsumer::~InputConsumer() { bool InputConsumer::isTouchResamplingEnabled() { char value[PROPERTY_VALUE_MAX]; - int length = property_get("ro.input.noresample", value, NULL); + int length = property_get("ro.input.noresample", value, nullptr); if (length > 0) { if (!strcmp("1", value)) { return false; @@ -398,16 +556,14 @@ bool InputConsumer::isTouchResamplingEnabled() { } 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. @@ -422,7 +578,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, 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", @@ -466,7 +622,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, // 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; @@ -500,7 +656,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, 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); @@ -518,14 +674,13 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, } 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; } @@ -539,11 +694,11 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, 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); } @@ -557,7 +712,7 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, } 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; @@ -572,7 +727,6 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, mSeqChains.push(seqChain); addSample(motionEvent, &msg); } else { - *displayId = msg.body.motion.displayId; initializeMotionEvent(motionEvent, &msg); } chain = msg.body.motion.seq; @@ -928,6 +1082,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) 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, @@ -950,6 +1105,7 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage 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, diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp new file mode 100644 index 0000000000..aa1371fdc9 --- /dev/null +++ b/libs/input/InputWindow.cpp @@ -0,0 +1,169 @@ +/* + * 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); + applicationInfo.write(output); + output.write(touchableRegion); + + 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.applicationInfo = InputApplicationInfo::read(from); + from.read(ret.touchableRegion); + + 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 cba1111606..e189d20e28 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -106,14 +106,14 @@ KeyCharacterMap::~KeyCharacterMap() { } } -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 @@ status_t KeyCharacterMap::load(const String8& filename, 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 @@ status_t KeyCharacterMap::load(Tokenizer* tokenizer, 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 @@ bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMeta // 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 @@ void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, 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 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { 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 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { 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 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { 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 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { } if (parcel->errorCheck()) { - return NULL; + return nullptr; } } return map; @@ -666,7 +666,7 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { 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 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { // --- 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::Key::~Key() { // --- 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 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { 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 @@ status_t KeyCharacterMap::Parser::finishKey(Key* key) { 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 @@ status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* o 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 @@ status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* o } 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 2b2f13e4c7..88cb0dbdb4 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -49,13 +49,13 @@ KeyLayoutMap::KeyLayoutMap() { 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,7 +117,7 @@ const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCod return &mKeysByScanCode.valueAt(index); } } - return NULL; + return nullptr; } status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index 11842ee7ff..0c22bfefed 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -45,22 +45,22 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, 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 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, } // 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 @@ bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, } 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 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, 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 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi 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 @@ bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, } } - 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/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index f834c559a4..42d774e73c 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -117,7 +117,7 @@ VelocityTracker::VelocityTracker(const char* strategy) : // 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 @@ VelocityTracker::~VelocityTracker() { 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 @@ VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { // 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 28ea7177cf..3ec53bf5a0 100644 --- a/libs/input/VirtualKeyMap.cpp +++ b/libs/input/VirtualKeyMap.cpp @@ -46,13 +46,13 @@ VirtualKeyMap::VirtualKeyMap() { VirtualKeyMap::~VirtualKeyMap() { } -status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { - *outMap = NULL; +status_t VirtualKeyMap::load(const std::string& filename, VirtualKeyMap** outMap) { + *outMap = nullptr; 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 virtual key map file %s.", status, filename.string()); + ALOGE("Error %d opening virtual key map file %s.", status, filename.c_str()); } else { VirtualKeyMap* map = new VirtualKeyMap(); if (!map) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index f06119f3f7..fdd945e90e 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -6,6 +6,7 @@ cc_test { "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", "VelocityTracker_test.cpp", + "InputWindow_test.cpp" ], cflags: [ "-Wall", @@ -34,4 +35,12 @@ cc_library_static { "-Wall", "-Werror", ], + shared_libs: [ + "libinput", + "libcutils", + "libutils", + "libbinder", + "libui", + "libbase", + ] } diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index fd3b7c8ee4..99f83ba7db 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -22,6 +22,9 @@ namespace android { +// Default display id. +static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; + class BaseTest : public testing::Test { protected: virtual void SetUp() { } @@ -178,13 +181,14 @@ TEST_F(KeyEventTest, Properties) { // 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 +201,11 @@ TEST_F(KeyEventTest, Properties) { // 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,7 +257,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { 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, @@ -301,6 +310,7 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { 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()); @@ -434,6 +444,11 @@ TEST_F(MotionEventTest, Properties) { 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,7 +572,7 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; - event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, + event.initialize(0, 0, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index c5322414fd..0788c89b2a 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -46,12 +46,12 @@ protected: 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 @@ TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { 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; - - status = mPublisher->publishKeyEvent(seq, deviceId, source, action, flags, + 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, 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 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { 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,23 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { 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 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++) { @@ -176,12 +176,11 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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,6 +189,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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()); diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp new file mode 100644 index 0000000000..5e5893fe4c --- /dev/null +++ b/libs/input/tests/InputWindow_test.cpp @@ -0,0 +1,96 @@ +/* + * 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) { + 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; + + 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); +} + +} // namespace test +} // namespace android diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index d19f3b8066..12a67828ac 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -65,6 +65,9 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80); CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88); + + CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); + CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } } // namespace android diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 9da2e2a315..af97c34055 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -27,6 +27,8 @@ using android::base::StringPrintf; 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 @@ -96,7 +98,7 @@ MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSample // First sample added separately with initialize coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y); - event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, + event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, positions[0].time, 1, properties, &coords); for (size_t i = 1; i < numSamples; i++) { diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 7e26b0b587..a19fe17d16 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -41,33 +41,11 @@ using namespace android; // ---------------------------------------------------------------------------- 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()) + "]")); @@ -122,19 +100,26 @@ int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, 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"); + "AHARDWAREBUFFER_USAGE_CPU_* flags are allowed"); return BAD_VALUE; } usage = AHardwareBuffer_convertToGrallocUsageBits(usage); - GraphicBuffer* gBuffer = AHardwareBuffer_to_GraphicBuffer(buffer); + 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())); + 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); + return gbuffer->lockAsync(usage, usage, bounds, outVirtualAddress, fence); } int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) { @@ -274,6 +259,25 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out return NO_ERROR; } +int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) { + if (!desc) return 0; + if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0; + + // Make a trial allocation. + // TODO(b/115660272): add implementation that uses a HAL query. + AHardwareBuffer_Desc trialDesc = *desc; + trialDesc.width = 4; + trialDesc.height = desc->format == AHARDWAREBUFFER_FORMAT_BLOB ? 1 : 4; + 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 +326,71 @@ int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, 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; + } + + 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 (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; + } -static inline bool containsBits(uint64_t mask, uint64_t bitsToCheck) { - return (mask & bitsToCheck) == bitsToCheck && bitsToCheck; + 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) { @@ -445,7 +506,7 @@ uint64_t AHardwareBuffer_convertToGrallocUsageBits(uint64_t usage) { "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"); diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index d74bdb3e99..d8478848cb 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: false, +} + ndk_library { name: "libnativewindow", symbol_file: "libnativewindow.map.txt", @@ -40,6 +47,7 @@ cc_library { cflags: [ "-Wall", "-Werror", + "-Wno-enum-compare", "-Wno-unused-function", ], @@ -66,6 +74,7 @@ cc_library { 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 71f563467d..bf688f8ef8 100644 --- a/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h +++ b/libs/nativewindow/include-private/private/android/AHardwareBufferHelpers.h @@ -28,10 +28,15 @@ #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); diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index 52b4582466..03545a68ca 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -194,6 +194,8 @@ enum AHardwareBuffer_UsageFlags { /// 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 +203,10 @@ enum AHardwareBuffer_UsageFlags { * 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 is protected from direct CPU access or being read by * non-secure hardware, such as video encoders. @@ -309,7 +312,7 @@ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer) __INTRODUCED_IN(26); /** * Acquire a reference on the given AHardwareBuffer object. - * + * * This prevents the object from being deleted until the last reference * is removed. */ @@ -417,6 +420,29 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out #endif // __ANDROID_API__ >= 26 +#if __ANDROID_API__ >= 29 + +/** + * Test whether the given format and usage flag combination is + * allocatable. + * + * If this function returns true, it means that a buffer with the given + * description can be allocated on this implementation, unless resource + * exhaustion occurs. If this function returns false, it means that the + * allocation of the given description will never succeed. + * + * The return value of this function may depend on all fields in the + * description, except stride, which is always ignored. For example, + * some implementations have implementation-defined limits on texture + * size and layer count. + * + * \return 1 if the format and usage flag combination is allocatable, + * 0 otherwise. + */ +int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_IN(29); + +#endif // __ANDROID_API__ >= 29 + __END_DECLS #endif // ANDROID_HARDWARE_BUFFER_H diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 197f73f3b1..61590e0196 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -202,7 +202,7 @@ enum { * 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 @@ enum { 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 @@ static inline int native_window_set_buffers_cta861_3_metadata( } /* + * 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/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 753954d97a..a796e97e29 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -5,6 +5,7 @@ LIBNATIVEWINDOW { AHardwareBuffer_createFromHandle; # vndk AHardwareBuffer_describe; AHardwareBuffer_getNativeHandle; # vndk + AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_recvHandleFromUnixSocket; AHardwareBuffer_release; diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp new file mode 100644 index 0000000000..d872f0264e --- /dev/null +++ b/libs/renderengine/Android.bp @@ -0,0 +1,81 @@ +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", + "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, + }, +} diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp new file mode 100644 index 0000000000..b9cea1071f --- /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 index 6a62b1d7dd..f5387f28ea 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 @@ Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t te 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 @@ Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t te 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 @@ size_t Mesh::getStride() const { return mStride; } -} /* namespace android */ +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp new file mode 100644 index 0000000000..6dd7283a15 --- /dev/null +++ b/libs/renderengine/RenderEngine.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/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) { + 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); + } + ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); + return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags); +} + +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 0000000000..995dba1422 --- /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 index 351430fa06..154cde80b9 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 @@ size_t Texture::getHeight() const { 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 0000000000..51cf1886d3 --- /dev/null +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -0,0 +1,1133 @@ +/* + * 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 <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <android-base/stringprintf.h> +#include <cutils/compiler.h> +#include <renderengine/Mesh.h> +#include <renderengine/Texture.h> +#include <renderengine/private/Description.h> +#include <ui/ColorSpace.h> +#include <ui/DebugUtils.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) { + // initialize EGL for the default display + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(display, nullptr, nullptr)) { + LOG_ALWAYS_FATAL("failed to initialize EGL"); + } + + GLExtensions& extensions = GLExtensions::getInstance(); + extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION), + eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS)); + + // The code assumes that ES2 or later is available if this extension is + // supported. + EGLConfig config = EGL_NO_CONFIG; + if (!extensions.hasNoConfigContext()) { + config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + + bool useContextPriority = extensions.hasContextPriority() && + (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT); + EGLContext protectedContext = EGL_NO_CONTEXT; + if (extensions.hasProtectedContent()) { + protectedContext = createEglContext(display, config, nullptr, useContextPriority, + Protection::PROTECTED); + ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context"); + } + + EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority, + Protection::UNPROTECTED); + + // if can't create a GL context, we can only abort. + LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); + + EGLSurface dummy = EGL_NO_SURFACE; + if (!extensions.hasSurfacelessContext()) { + dummy = createDummyEglPbufferSurface(display, config, hwcFormat, Protection::UNPROTECTED); + LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer"); + } + EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt); + LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current"); + extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), + glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); + + // In order to have protected contents in GPU composition, the OpenGL ES extension + // GL_EXT_protected_textures must be supported. If it's not supported, reset + // protected context to EGL_NO_CONTEXT to indicate that protected contents is not supported. + if (!extensions.hasProtectedTexture()) { + protectedContext = EGL_NO_CONTEXT; + } + + EGLSurface protectedDummy = EGL_NO_SURFACE; + if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) { + protectedDummy = + createDummyEglPbufferSurface(display, config, hwcFormat, Protection::PROTECTED); + ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer"); + } + + // now figure out what version of GL did we actually get + GlesVersion version = parseGlesVersion(extensions.getVersion()); + + // initialize the renderer while GL is current + std::unique_ptr<GLESRenderEngine> engine; + switch (version) { + case GLES_VERSION_1_0: + case GLES_VERSION_1_1: + LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run."); + break; + case GLES_VERSION_2_0: + case GLES_VERSION_3_0: + engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy, + protectedContext, protectedDummy); + 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) + : renderengine::impl::RenderEngine(featureFlags), + mEGLDisplay(display), + mEGLConfig(config), + mEGLContext(ctxt), + mDummySurface(dummy), + mProtectedEGLContext(protectedContext), + mProtectedDummySurface(protectedDummy), + mVpWidth(0), + mVpHeight(0), + 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; + } +} + +GLESRenderEngine::~GLESRenderEngine() { + 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); +} + +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() { + 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 GLESRenderEngine::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 GLESRenderEngine::waitFence(base::unique_fd fenceFd) { + if (!GLExtensions::getInstance().hasNativeFenceSync() || + !GLExtensions::getInstance().hasWaitSync()) { + return false; + } + + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); + return false; + } + + // fenceFd is now owned by EGLSync + (void)fenceFd.release(); + + // XXX: The spec draft is inconsistent as to whether this should return an + // EGLint or void. Ignore the return value for now, as it's not strictly + // needed. + eglWaitSyncKHR(mEGLDisplay, sync, 0); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (error != EGL_SUCCESS) { + ALOGE("failed to wait for EGL native fence sync: %#x", error); + return false; + } + + return true; +} + +void GLESRenderEngine::clearWithColor(float red, float green, float blue, float alpha) { + glClearColor(red, green, blue, alpha); + glClear(GL_COLOR_BUFFER_BIT); +} + +void GLESRenderEngine::fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) { + size_t c; + Rect const* r = region.getArray(&c); + Mesh mesh(Mesh::TRIANGLES, c * 6, 2); + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + for (size_t i = 0; i < c; i++, r++) { + position[i * 6 + 0].x = r->left; + position[i * 6 + 0].y = r->top; + position[i * 6 + 1].x = r->left; + position[i * 6 + 1].y = r->bottom; + position[i * 6 + 2].x = r->right; + position[i * 6 + 2].y = r->bottom; + position[i * 6 + 3].x = r->left; + position[i * 6 + 3].y = r->top; + position[i * 6 + 4].x = r->right; + position[i * 6 + 4].y = r->bottom; + position[i * 6 + 5].x = r->right; + position[i * 6 + 5].y = r->top; + } + setupFillWithColor(red, green, blue, alpha); + drawMesh(mesh); +} + +void GLESRenderEngine::setScissor(const Rect& region) { + // Invert y-coordinate to map to GL-space. + int32_t canvasHeight = mFboHeight; + int32_t glBottom = canvasHeight - region.bottom; + + glScissor(region.left, glBottom, region.getWidth(), region.getHeight()); + glEnable(GL_SCISSOR_TEST); +} + +void GLESRenderEngine::disableScissor() { + glDisable(GL_SCISSOR_TEST); +} + +void GLESRenderEngine::genTextures(size_t count, uint32_t* names) { + glGenTextures(count, names); +} + +void GLESRenderEngine::deleteTextures(size_t count, uint32_t const* names) { + glDeleteTextures(count, names); +} + +void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) { + const GLImage& glImage = static_cast<const GLImage&>(image); + const GLenum target = GL_TEXTURE_EXTERNAL_OES; + + glBindTexture(target, texName); + if (supportsProtectedContent()) { + glTexParameteri(target, GL_TEXTURE_PROTECTED_EXT, + glImage.isProtected() ? GL_TRUE : GL_FALSE); + } + if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) { + glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage())); + } +} + +status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) { + GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer); + EGLImageKHR eglImage = glFramebuffer->getEGLImage(); + uint32_t textureName = glFramebuffer->getTextureName(); + uint32_t framebufferName = glFramebuffer->getFramebufferName(); + + // Bind the texture and turn our EGLImage into a texture + glBindTexture(GL_TEXTURE_2D, textureName); + if (supportsProtectedContent()) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_PROTECTED_EXT, + mInProtectedContext ? GL_TRUE : GL_FALSE); + } + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage); + + // Bind the Framebuffer to render into + glBindFramebuffer(GL_FRAMEBUFFER, framebufferName); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0); + + mFboHeight = glFramebuffer->getBufferHeight(); + + uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d", + glStatus); + + return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE; +} + +void GLESRenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) { + mFboHeight = 0; + + // back to main framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void GLESRenderEngine::checkErrors() const { + do { + // there could be more than one error flag + GLenum error = glGetError(); + if (error == GL_NO_ERROR) break; + ALOGE("GL error 0x%04x", int(error)); + } while (true); +} + +bool GLESRenderEngine::supportsProtectedContent() const { + return mProtectedEGLContext != EGL_NO_CONTEXT; +} + +bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) { + if (useProtectedContext == mInProtectedContext) { + return true; + } + if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) { + return false; + } + const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface; + const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; + const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE; + if (success) { + mInProtectedContext = useProtectedContext; + } + return success; +} + +status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* const buffer, + base::unique_fd* drawFence) { + if (layers.empty()) { + ALOGV("Drawing empty layer stack"); + return NO_ERROR; + } + + BindNativeBufferAsFramebuffer fbo(*this, buffer); + + if (fbo.getStatus() != NO_ERROR) { + ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors(); + return fbo.getStatus(); + } + + setViewportAndProjection(display.physicalDisplay, display.clip); + + setOutputDataSpace(display.outputDataspace); + setDisplayMaxLuminance(display.maxLuminance); + + mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; + + Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2); + for (auto layer : layers) { + // for now, assume that all pixel sources are solid colors. + // TODO(alecmouri): support buffer sources + if (layer.source.buffer.buffer != nullptr) { + continue; + } + + setColorTransform(display.colorTransform * layer.colorTransform); + + mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; + + 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); + + half3 solidColor = layer.source.solidColor; + half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); + setupLayerBlending(/*premultipliedAlpha=*/true, /*opaque=*/false, /*disableTexture=*/true, + color, /*cornerRadius=*/0.0); + setSourceDataSpace(layer.sourceDataspace); + + drawMesh(mesh); + } + + *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->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) { + mVpWidth = viewport.getWidth(); + mVpHeight = viewport.getHeight(); + + // We pass the the top left corner instead of the bottom left corner, + // because since we're rendering off-screen first. + glViewport(viewport.left, viewport.top, mVpWidth, mVpHeight); + + mState.projectionMatrix = mat4::ortho(clip.left, clip.right, clip.top, clip.bottom, 0, 1); +} + +void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color, float cornerRadius) { + mState.isPremultipliedAlpha = premultipliedAlpha; + mState.isOpaque = opaque; + mState.color = color; + mState.cornerRadius = cornerRadius; + + if (disableTexture) { + mState.textureEnabled = false; + } + + if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) { + glEnable(GL_BLEND); + glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } +} + +void GLESRenderEngine::setSourceY410BT2020(bool enable) { + mState.isY410BT2020 = enable; +} + +void GLESRenderEngine::setSourceDataSpace(Dataspace source) { + mDataSpace = source; +} + +void GLESRenderEngine::setOutputDataSpace(Dataspace dataspace) { + mOutputDataSpace = dataspace; +} + +void GLESRenderEngine::setDisplayMaxLuminance(const float maxLuminance) { + mState.displayMaxLuminance = maxLuminance; +} + +void GLESRenderEngine::setupLayerTexturing(const Texture& texture) { + GLuint target = texture.getTextureTarget(); + glBindTexture(target, texture.getTextureName()); + GLenum filter = GL_NEAREST; + if (texture.getFiltering()) { + filter = GL_LINEAR; + } + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); + + mState.texture = texture; + mState.textureEnabled = true; +} + +void GLESRenderEngine::setupLayerBlackedOut() { + glBindTexture(GL_TEXTURE_2D, mProtectedTexName); + Texture texture(Texture::TEXTURE_2D, mProtectedTexName); + texture.setDimensions(1, 1); // FIXME: we should get that from somewhere + mState.texture = texture; + mState.textureEnabled = true; +} + +void GLESRenderEngine::setColorTransform(const mat4& colorTransform) { + mState.colorMatrix = colorTransform; +} + +void GLESRenderEngine::disableTexturing() { + mState.textureEnabled = false; +} + +void GLESRenderEngine::disableBlending() { + glDisable(GL_BLEND); +} + +void GLESRenderEngine::setupFillWithColor(float r, float g, float b, float a) { + mState.isPremultipliedAlpha = true; + mState.isOpaque = false; + mState.color = half4(r, g, b, a); + mState.textureEnabled = false; + glDisable(GL_BLEND); +} + +void GLESRenderEngine::setupCornerRadiusCropSize(float width, float height) { + mState.cropSize = half2(width, height); +} + +void GLESRenderEngine::drawMesh(const Mesh& mesh) { + ATRACE_CALL(); + if (mesh.getTexCoordsSize()) { + glEnableVertexAttribArray(Program::texCoords); + glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getTexCoords()); + } + + glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getPositions()); + + if (mState.cornerRadius > 0.0f) { + glEnableVertexAttribArray(Program::cropCoords); + glVertexAttribPointer(Program::cropCoords, mesh.getVertexSize(), GL_FLOAT, GL_FALSE, + mesh.getByteStride(), mesh.getCropCoords()); + } + + // By default, DISPLAY_P3 is the only supported wide color output. However, + // when HDR content is present, hardware composer may be able to handle + // BT2020 data space, in that case, the output data space is set to be + // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need + // to respect this and convert non-HDR content to HDR format. + if (mUseColorManagement) { + Description managedState = mState; + Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK); + Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); + Dataspace outputStandard = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK); + Dataspace outputTransfer = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); + bool needsXYZConversion = needsXYZTransformMatrix(); + + // NOTE: if the input standard of the input dataspace is not STANDARD_DCI_P3 or + // STANDARD_BT2020, it will be treated as STANDARD_BT709 + if (inputStandard != Dataspace::STANDARD_DCI_P3 && + inputStandard != Dataspace::STANDARD_BT2020) { + inputStandard = Dataspace::STANDARD_BT709; + } + + if (needsXYZConversion) { + // The supported input color spaces are standard RGB, Display P3 and BT2020. + switch (inputStandard) { + case Dataspace::STANDARD_DCI_P3: + managedState.inputTransformMatrix = mDisplayP3ToXyz; + break; + case Dataspace::STANDARD_BT2020: + managedState.inputTransformMatrix = mBt2020ToXyz; + break; + default: + managedState.inputTransformMatrix = mSrgbToXyz; + break; + } + + // The supported output color spaces are BT2020, Display P3 and standard RGB. + switch (outputStandard) { + case Dataspace::STANDARD_BT2020: + managedState.outputTransformMatrix = mXyzToBt2020; + break; + case Dataspace::STANDARD_DCI_P3: + managedState.outputTransformMatrix = mXyzToDisplayP3; + break; + default: + managedState.outputTransformMatrix = mXyzToSrgb; + break; + } + } else if (inputStandard != outputStandard) { + // At this point, the input data space and output data space could be both + // HDR data spaces, but they match each other, we do nothing in this case. + // In addition to the case above, the input data space could be + // - scRGB linear + // - scRGB non-linear + // - sRGB + // - Display P3 + // - BT2020 + // The output data spaces could be + // - sRGB + // - Display P3 + // - BT2020 + switch (outputStandard) { + case Dataspace::STANDARD_BT2020: + if (inputStandard == Dataspace::STANDARD_BT709) { + managedState.outputTransformMatrix = mSrgbToBt2020; + } else if (inputStandard == Dataspace::STANDARD_DCI_P3) { + managedState.outputTransformMatrix = mDisplayP3ToBt2020; + } + break; + case Dataspace::STANDARD_DCI_P3: + if (inputStandard == Dataspace::STANDARD_BT709) { + managedState.outputTransformMatrix = mSrgbToDisplayP3; + } else if (inputStandard == Dataspace::STANDARD_BT2020) { + managedState.outputTransformMatrix = mBt2020ToDisplayP3; + } + break; + default: + if (inputStandard == Dataspace::STANDARD_DCI_P3) { + managedState.outputTransformMatrix = mDisplayP3ToSrgb; + } else if (inputStandard == Dataspace::STANDARD_BT2020) { + managedState.outputTransformMatrix = mBt2020ToSrgb; + } + break; + } + } + + // we need to convert the RGB value to linear space and convert it back when: + // - there is a color matrix that is not an identity matrix, or + // - there is an output transform matrix that is not an identity matrix, or + // - the input transfer function doesn't match the output transfer function. + if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() || + inputTransfer != outputTransfer) { + managedState.inputTransferFunction = + Description::dataSpaceToTransferFunction(inputTransfer); + managedState.outputTransferFunction = + Description::dataSpaceToTransferFunction(outputTransfer); + } + + ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext + : mEGLContext, + managedState); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + + if (outputDebugPPMs) { + static uint64_t managedColorFrameCount = 0; + std::ostringstream out; + out << "/data/texture_out" << managedColorFrameCount++; + writePPM(out.str().c_str(), mVpWidth, mVpHeight); + } + } else { + ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext + : mEGLContext, + mState); + + glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount()); + } + + if (mesh.getTexCoordsSize()) { + glDisableVertexAttribArray(Program::texCoords); + } + + if (mState.cornerRadius > 0.0f) { + glDisableVertexAttribArray(Program::cropCoords); + } +} + +size_t GLESRenderEngine::getMaxTextureSize() const { + return mMaxTextureSize; +} + +size_t GLESRenderEngine::getMaxViewportDims() const { + return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1]; +} + +void GLESRenderEngine::dump(std::string& result) { + const GLExtensions& extensions = GLExtensions::getInstance(); + ProgramCache& cache = ProgramCache::getInstance(); + + StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); + StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); + StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), + extensions.getVersion()); + StringAppendF(&result, "%s\n", extensions.getExtensions()); + StringAppendF(&result, "RenderEngine is in protected context : %d\n", mInProtectedContext); + StringAppendF(&result, "RenderEngine program cache size for unprotected context: %zu\n", + cache.getSize(mEGLContext)); + StringAppendF(&result, "RenderEngine program cache size for protected context: %zu\n", + cache.getSize(mProtectedEGLContext)); + StringAppendF(&result, "RenderEngine last dataspace conversion: (%s) to (%s)\n", + dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(), + dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str()); +} + +GLESRenderEngine::GlesVersion GLESRenderEngine::parseGlesVersion(const char* str) { + int major, minor; + if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) { + if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) { + ALOGW("Unable to parse GL_VERSION string: \"%s\"", str); + return GLES_VERSION_1_0; + } + } + + if (major == 1 && minor == 0) return GLES_VERSION_1_0; + if (major == 1 && minor >= 1) return GLES_VERSION_1_1; + if (major == 2 && minor >= 0) return GLES_VERSION_2_0; + if (major == 3 && minor >= 0) return GLES_VERSION_3_0; + + ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor); + return GLES_VERSION_1_0; +} + +EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, bool useContextPriority, + Protection protection) { + EGLint renderableType = 0; + if (config == EGL_NO_CONFIG) { + renderableType = EGL_OPENGL_ES3_BIT; + } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { + LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); + } + EGLint contextClientVersion = 0; + if (renderableType & EGL_OPENGL_ES3_BIT) { + contextClientVersion = 3; + } else if (renderableType & EGL_OPENGL_ES2_BIT) { + contextClientVersion = 2; + } else if (renderableType & EGL_OPENGL_ES_BIT) { + contextClientVersion = 1; + } else { + LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); + } + + std::vector<EGLint> contextAttributes; + contextAttributes.reserve(7); + contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); + contextAttributes.push_back(contextClientVersion); + if (useContextPriority) { + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + } + if (protection == Protection::PROTECTED) { + contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT); + contextAttributes.push_back(EGL_TRUE); + } + contextAttributes.push_back(EGL_NONE); + + EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + + if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) { + // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus + // EGL_NO_CONTEXT so that we can abort. + if (config != EGL_NO_CONFIG) { + return context; + } + // If |config| is EGL_NO_CONFIG, we speculatively try to create GLES 3 context, so we should + // try to fall back to GLES 2. + contextAttributes[1] = 2; + context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + } + + return context; +} + +EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection) { + EGLConfig dummyConfig = config; + if (dummyConfig == EGL_NO_CONFIG) { + dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + std::vector<EGLint> attributes; + attributes.reserve(7); + attributes.push_back(EGL_WIDTH); + attributes.push_back(1); + attributes.push_back(EGL_HEIGHT); + attributes.push_back(1); + if (protection == Protection::PROTECTED) { + attributes.push_back(EGL_PROTECTED_CONTENT_EXT); + attributes.push_back(EGL_TRUE); + } + attributes.push_back(EGL_NONE); + + return eglCreatePbufferSurface(display, dummyConfig, attributes.data()); +} + +bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const { + const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK); + const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK); + return standard == Dataspace::STANDARD_BT2020 && + (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG); +} + +// For convenience, we want to convert the input color space to XYZ color space first, +// and then convert from XYZ color space to output color space when +// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or +// HDR content will be tone-mapped to SDR; Or, +// - there are HDR PQ and HLG contents presented at the same time, where we want to convert +// HLG content to PQ content. +// In either case above, we need to operate the Y value in XYZ color space. Thus, when either +// input data space or output data space is HDR data space, and the input transfer function +// doesn't match the output transfer function, we would enable an intermediate transfrom to +// XYZ color space. +bool GLESRenderEngine::needsXYZTransformMatrix() const { + const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace); + const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace); + const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK); + const Dataspace outputTransfer = + static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK); + + return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer; +} + +} // 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 0000000000..b6fff33061 --- /dev/null +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -0,0 +1,175 @@ +/* + * 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 <stdint.h> +#include <sys/types.h> + +#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); + 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); + ~GLESRenderEngine() override; + + std::unique_ptr<Framebuffer> createFramebuffer() override; + std::unique_ptr<Image> createImage() override; + + void primeCache() const override; + bool isCurrent() const override; + base::unique_fd flush() override; + bool finish() override; + bool waitFence(base::unique_fd fenceFd) override; + void clearWithColor(float red, float green, float blue, float alpha) override; + void fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) override; + void setScissor(const Rect& region) override; + void disableScissor() override; + void genTextures(size_t count, uint32_t* names) override; + void deleteTextures(size_t count, uint32_t const* names) override; + void bindExternalTextureImage(uint32_t texName, const Image& image) override; + status_t bindFrameBuffer(Framebuffer* framebuffer) override; + void unbindFrameBuffer(Framebuffer* framebuffer) override; + void checkErrors() const override; + + bool isProtected() const override { return mInProtectedContext; } + bool supportsProtectedContent() const override; + bool useProtectedContext(bool useProtectedContext) override; + status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* buffer, base::unique_fd* drawFence) override; + + // internal to RenderEngine + EGLDisplay getEGLDisplay() const { return mEGLDisplay; } + EGLConfig getEGLConfig() const { return mEGLConfig; } + +protected: + 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); + + // 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); + + 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; + int32_t mFboHeight = 0; + + // 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; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android + +#endif /* SF_GLESRENDERENGINE_H_ */ diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp index dc09a376bb..2924b0e8b3 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 { -// --------------------------------------------------------------------------- - -ANDROID_SINGLETON_STATIC_INSTANCE(GLExtensions) +namespace renderengine { +namespace gl { + +namespace { + +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); + } -SortedVector<String8> GLExtensions::parseExtensionString(char const* extensions) { - SortedVector<String8> list; + bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; } - 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); +private: + std::unordered_set<std::string> mExtensions; +}; - return list; -} +} // anonymous namespace void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version, GLubyte const* extensions) { @@ -48,12 +60,11 @@ void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* rende 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 @@ char const* GLExtensions::getExtensions() const { 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 @@ void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExt // // 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 @@ char const* GLExtensions::getEGLExtensions() const { 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 index 0d8c10b0ac..ef000090a8 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 @@ public: 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 0000000000..4a519bb422 --- /dev/null +++ b/libs/renderengine/gl/GLFramebuffer.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GLFramebuffer.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <nativebase/nativebase.h> +#include "GLESRenderEngine.h" + +namespace android { +namespace renderengine { +namespace gl { + +GLFramebuffer::GLFramebuffer(const GLESRenderEngine& engine) + : mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { + glGenTextures(1, &mTextureName); + glGenFramebuffers(1, &mFramebufferName); +} + +GLFramebuffer::~GLFramebuffer() { + glDeleteFramebuffers(1, &mFramebufferName); + glDeleteTextures(1, &mTextureName); + eglDestroyImageKHR(mEGLDisplay, mEGLImage); +} + +bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) { + if (mEGLImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mEGLDisplay, mEGLImage); + mEGLImage = EGL_NO_IMAGE_KHR; + mBufferWidth = 0; + mBufferHeight = 0; + } + + if (nativeBuffer) { + EGLint attributes[] = { + isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + isProtected ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + nativeBuffer, attributes); + if (mEGLImage == EGL_NO_IMAGE_KHR) { + return false; + } + mBufferWidth = nativeBuffer->width; + mBufferHeight = nativeBuffer->height; + } + return true; +} + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h new file mode 100644 index 0000000000..5043c590a9 --- /dev/null +++ b/libs/renderengine/gl/GLFramebuffer.h @@ -0,0 +1,56 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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(const GLESRenderEngine& engine); + ~GLFramebuffer() override; + + bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) override; + EGLImageKHR getEGLImage() const { return mEGLImage; } + uint32_t getTextureName() const { return mTextureName; } + uint32_t getFramebufferName() const { return mFramebufferName; } + int32_t getBufferHeight() const { return mBufferHeight; } + int32_t getBufferWidth() const { return mBufferWidth; } + +private: + EGLDisplay mEGLDisplay; + EGLImageKHR mEGLImage; + uint32_t mTextureName, mFramebufferName; + + int32_t mBufferHeight = 0; + int32_t mBufferWidth = 0; +}; + +} // namespace gl +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/Image.cpp b/libs/renderengine/gl/GLImage.cpp index 0d06422a41..587cb313c2 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,19 @@ * limitations under the License. */ -#include "Image.h" +#include "GLImage.h" #include <vector> #include <log/log.h> - +#include "GLESRenderEngine.h" #include "GLExtensions.h" -#include "RenderEngine.h" namespace android { -namespace RE { - -Image::~Image() = default; - -namespace impl { - -Image::Image(const RenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {} - -Image::~Image() { - setNativeWindowBuffer(nullptr, false, 0, 0); -} +namespace renderengine { +namespace gl { -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 +38,18 @@ static std::vector<EGLint> buildAttributeList(bool isProtected, int32_t cropWidt 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) { if (mEGLImage != EGL_NO_IMAGE_KHR) { if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) { ALOGE("failed to destroy image: %#x", eglGetError()); @@ -75,18 +58,19 @@ bool Image::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, } 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/services/surfaceflinger/RenderEngine/Image.h b/libs/renderengine/gl/GLImage.h index 1ae7e09cba..59d6ce3549 100644 --- a/services/surfaceflinger/RenderEngine/Image.h +++ b/libs/renderengine/gl/GLImage.h @@ -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. @@ -20,43 +20,35 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <android-base/macros.h> +#include <renderengine/Image.h> struct ANativeWindowBuffer; namespace android { -namespace RE { +namespace renderengine { +namespace gl { -class Image { -public: - virtual ~Image() = 0; - virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, - int32_t cropWidth, int32_t cropHeight) = 0; -}; - -namespace impl { +class GLESRenderEngine; -class RenderEngine; - -class Image : public RE::Image { +class GLImage : public renderengine::Image { public: - explicit Image(const RenderEngine& engine); - ~Image() override; + explicit GLImage(const GLESRenderEngine& engine); + ~GLImage() override; - Image(const Image&) = delete; - Image& operator=(const Image&) = delete; + bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override; - bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth, - int32_t cropHeight) override; + EGLImageKHR getEGLImage() const { return mEGLImage; } + bool isProtected() const { return mProtected; } private: - // methods internal to RenderEngine - friend class RenderEngine; - EGLSurface getEGLImage() const { return mEGLImage; } - EGLDisplay mEGLDisplay; EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR; + bool mProtected = false; + + DISALLOW_COPY_AND_ASSIGN(GLImage); }; -} // namespace impl -} // namespace RE +} // namespace gl +} // namespace renderengine } // namespace android diff --git a/services/surfaceflinger/RenderEngine/Program.cpp b/libs/renderengine/gl/Program.cpp index fe536f0c7c..fe9d909923 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 @@ Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const c glAttachShader(programId, fragmentId); glBindAttribLocation(programId, position, "position"); glBindAttribLocation(programId, texCoords, "texCoords"); + glBindAttribLocation(programId, cropCoords, "cropCoords"); glLinkProgram(programId); GLint status; @@ -65,6 +67,8 @@ Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const c 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(const ProgramCache::Key& /*needs*/, const char* vertex, const c } } -Program::~Program() {} - bool Program::isValid() const { return mInitialized; } @@ -111,45 +113,41 @@ GLuint Program::buildShader(const char* source, GLenum type) { 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 index ae796c5852..bc9cf08b8b 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 @@ public: private: GLuint buildShader(const char* source, GLenum type); - String8& dumpShader(String8& result, GLenum type); // whether the initialization succeeded bool mInitialized; @@ -84,8 +94,16 @@ private: /* 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 index 2073b05453..49bdd2a319 100644 --- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp +++ b/libs/renderengine/gl/ProgramCache.cpp @@ -16,18 +16,21 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#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 "Description.h" #include "Program.h" -#include "ProgramCache.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 @@ Formatter& dedent(Formatter& f) { return f; } -// ----------------------------------------------------------------------------------------------- - -ANDROID_SINGLETON_STATIC_INSTANCE(ProgramCache) - -ProgramCache::ProgramCache() {} - -ProgramCache::~ProgramCache() {} - -void ProgramCache::primeCache(bool hasWideColor) { +void ProgramCache::primeCache(EGLContext context, bool useColorManagement) { + auto& cache = mCaches[context]; uint32_t shaderCount = 0; - uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK; + uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK + | Key::ROUNDED_CORNERS_MASK; // Prime the cache for all combinations of the above masks, // leaving off the experimental color matrix mask options. @@ -96,16 +93,14 @@ void ProgramCache::primeCache(bool hasWideColor) { if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) { continue; } - Program* program = mCache.valueFor(shaderKey); - if (program == nullptr) { - program = generateProgram(shaderKey); - mCache.add(shaderKey, program); + if (cache.count(shaderKey) == 0) { + cache.emplace(shaderKey, generateProgram(shaderKey)); shaderCount++; } } // Prime for sRGB->P3 conversion - if (hasWideColor) { + if (useColorManagement) { Key shaderKey; shaderKey.set(Key::BLEND_MASK | Key::TEXTURE_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::INPUT_TF_MASK | Key::OUTPUT_TF_MASK, @@ -115,10 +110,8 @@ void ProgramCache::primeCache(bool hasWideColor) { shaderKey.set(Key::OPACITY_MASK, (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT); shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE); - Program* program = mCache.valueFor(shaderKey); - if (program == nullptr) { - program = generateProgram(shaderKey); - mCache.add(shaderKey, program); + if (cache.count(shaderKey) == 0) { + cache.emplace(shaderKey, generateProgram(shaderKey)); shaderCount++; } } @@ -132,31 +125,35 @@ void ProgramCache::primeCache(bool hasWideColor) { ProgramCache::Key ProgramCache::computeKey(const Description& description) { Key needs; needs.set(Key::TEXTURE_MASK, - !description.mTextureEnabled + !description.textureEnabled ? Key::TEXTURE_OFF - : description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES + : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES ? Key::TEXTURE_EXT - : description.mTexture.getTextureTarget() == GL_TEXTURE_2D + : description.texture.getTextureTarget() == GL_TEXTURE_2D ? Key::TEXTURE_2D : Key::TEXTURE_OFF) - .set(Key::ALPHA_MASK, - (description.mColor.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE) + .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE) .set(Key::BLEND_MASK, - description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) + description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL) .set(Key::OPACITY_MASK, - description.mOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) + description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT) .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK, - description.hasInputTransformMatrix() ? - Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF) + description.hasInputTransformMatrix() + ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF) .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK, - description.hasOutputTransformMatrix() || description.hasColorMatrix() ? - Key::OUTPUT_TRANSFORM_MATRIX_ON : Key::OUTPUT_TRANSFORM_MATRIX_OFF); + description.hasOutputTransformMatrix() || description.hasColorMatrix() + ? Key::OUTPUT_TRANSFORM_MATRIX_ON + : Key::OUTPUT_TRANSFORM_MATRIX_OFF) + .set(Key::ROUNDED_CORNERS_MASK, + description.cornerRadius > 0 + ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF); needs.set(Key::Y410_BT2020_MASK, - description.mY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); + description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); - if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) { - switch (description.mInputTransferFunction) { + if (needs.hasTransformMatrix() || + (description.inputTransferFunction != description.outputTransferFunction)) { + switch (description.inputTransferFunction) { case Description::TransferFunction::LINEAR: default: needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR); @@ -172,7 +169,7 @@ ProgramCache::Key ProgramCache::computeKey(const Description& description) { break; } - switch (description.mOutputTransferFunction) { + switch (description.outputTransferFunction) { case Description::TransferFunction::LINEAR: default: needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR); @@ -522,6 +519,10 @@ String8 ProgramCache::generateVertexShader(const Key& needs) { vs << "attribute vec4 texCoords;" << "varying vec2 outTexCoords;"; } + if (needs.hasRoundedCorners()) { + vs << "attribute lowp vec4 cropCoords;"; + vs << "varying lowp vec2 outCropCoords;"; + } vs << "attribute vec4 position;" << "uniform mat4 projection;" << "uniform mat4 texture;" @@ -529,6 +530,9 @@ String8 ProgramCache::generateVertexShader(const Key& needs) { if (needs.isTexturing()) { vs << "outTexCoords = (texture * texCoords).st;"; } + if (needs.hasRoundedCorners()) { + vs << "outCropCoords = cropCoords.st;"; + } vs << dedent << "}"; return vs.getString(); } @@ -550,6 +554,27 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { << "varying vec2 outTexCoords;"; } + if (needs.hasRoundedCorners()) { + // Rounded corners implementation using a signed distance function. + fs << R"__SHADER__( + uniform float cornerRadius; + uniform vec2 cropCenter; + varying vec2 outCropCoords; + + /** + * This function takes the current crop coordinates and calculates an alpha value based + * on the corner radius and distance from the crop center. + */ + float applyCornerRadius(vec2 cropCoords) + { + vec2 position = cropCoords - cropCenter; + vec2 dist = abs(position) + vec2(cornerRadius) - cropCenter; + float plane = length(max(dist, vec2(0.0))); + return 1.0 - clamp(plane - cornerRadius, 0.0, 1.0); + } + )__SHADER__"; + } + if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) { fs << "uniform vec4 color;"; } @@ -640,18 +665,27 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { // avoid divide by 0 by adding 0.5/256 to the alpha channel fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);"; } - fs << "gl_FragColor.rgb = OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));"; + fs << "gl_FragColor.rgb = " + "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));"; if (!needs.isOpaque() && needs.isPremultiplied()) { // and re-premultiply if needed after gamma correction fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);"; } } + if (needs.hasRoundedCorners()) { + if (needs.isPremultiplied()) { + fs << "gl_FragColor *= vec4(applyCornerRadius(outCropCoords));"; + } else { + fs << "gl_FragColor.a *= applyCornerRadius(outCropCoords);"; + } + } + fs << dedent << "}"; return fs.getString(); } -Program* ProgramCache::generateProgram(const Key& needs) { +std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) { ATRACE_CALL(); // vertex shader @@ -660,32 +694,34 @@ Program* ProgramCache::generateProgram(const Key& needs) { // 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 index 983e7baf02..400ad74fca 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 @@ public: 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 @@ public: 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 @@ public: } 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 @@ private: // 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 + +ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key) -} /* namespace android */ +} // 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 0000000000..0c923535bb --- /dev/null +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -0,0 +1,61 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <math/mat4.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +namespace android { +namespace renderengine { + +// DisplaySettings contains the settings that are applicable when drawing all +// layers for a given display. +struct DisplaySettings { + // Rectangle describing the physical display. We will project from the + // logical clip onto this rectangle. + Rect physicalDisplay = Rect::INVALID_RECT; + + // Rectangle bounded by the x,y- clipping planes in the logical display, so + // that the orthographic projection matrix can be computed. When + // constructing this matrix, z-coordinate bound are assumed to be at z=0 and + // z=1. + Rect clip = Rect::INVALID_RECT; + + // Global transform to apply to all layers. + mat4 globalTransform = mat4(); + + // Maximum luminance pulled from the display's HDR capabilities. + float maxLuminance = 1.0f; + + // Output dataspace that will be populated if wide color gamut is used, or + // DataSpace::UNKNOWN otherwise. + ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN; + + // Additional color transform to apply in linear space after transforming + // to the output dataspace. + mat4 colorTransform = mat4(); + + // Region that will be cleared to (0, 0, 0, 0) prior to rendering. + // clearRegion will first be transformed by globalTransform so that it will + // be in the same coordinate space as the rendered layers. + Region clearRegion = Region::INVALID_REGION; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h new file mode 100644 index 0000000000..66eb9ef206 --- /dev/null +++ b/libs/renderengine/include/renderengine/Framebuffer.h @@ -0,0 +1,34 @@ +/* + * 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) = 0; +}; + +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/include/renderengine/Image.h b/libs/renderengine/include/renderengine/Image.h new file mode 100644 index 0000000000..3bb47318ef --- /dev/null +++ b/libs/renderengine/include/renderengine/Image.h @@ -0,0 +1,31 @@ +/* + * 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 + +struct ANativeWindowBuffer; + +namespace android { +namespace renderengine { + +class Image { +public: + virtual ~Image() = default; + virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0; +}; + +} // 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 0000000000..93abf5c458 --- /dev/null +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -0,0 +1,96 @@ +/* + * 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/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; + + // 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; + + // 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(); +}; + +// 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 apply to the source pixels + half alpha = half(0.0); + + // Color space describing how the source pixels should be interpreted. + ui::Dataspace sourceDataspace; + + // Additional layer-specific color transform to be applied before the global + // transform. + mat4 colorTransform; +}; + +} // namespace renderengine +} // namespace android diff --git a/services/surfaceflinger/RenderEngine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h index d0a9ac0c65..7618424e85 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 @@ public: }; 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 @@ public: 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 @@ private: 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 @@ private: 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 0000000000..20dd996ec2 --- /dev/null +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -0,0 +1,228 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_RENDERENGINE_H_ +#define SF_RENDERENGINE_H_ + +#include <stdint.h> +#include <sys/types.h> +#include <memory> + +#include <android-base/unique_fd.h> +#include <math/mat4.h> +#include <renderengine/DisplaySettings.h> +#include <renderengine/Framebuffer.h> +#include <renderengine/Image.h> +#include <renderengine/LayerSettings.h> +#include <ui/GraphicTypes.h> +#include <ui/Transform.h> + +/** + * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported). + */ +#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend" + +struct ANativeWindowBuffer; + +namespace android { + +class Rect; +class Region; + +namespace renderengine { + +class BindNativeBufferAsFramebuffer; +class Image; +class Mesh; +class Texture; + +namespace impl { +class RenderEngine; +} + +enum class Protection { + UNPROTECTED = 1, + PROTECTED = 2, +}; + +class RenderEngine { +public: + enum FeatureFlag { + USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color + USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context + }; + + static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags); + + virtual ~RenderEngine() = 0; + + // ----- BEGIN DEPRECATED INTERFACE ----- + // This interface, while still in use until a suitable replacement is built, + // should be considered deprecated, minus some methods which still may be + // used to support legacy behavior. + + virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0; + virtual std::unique_ptr<Image> createImage() = 0; + + virtual void primeCache() const = 0; + + // dump the extension strings. always call the base class. + virtual void dump(std::string& result) = 0; + + virtual bool useNativeFenceSync() const = 0; + virtual bool useWaitSync() const = 0; + + virtual bool isCurrent() const = 0; + + // helpers + // flush submits RenderEngine command stream for execution and returns a + // native fence fd that is signaled when the execution has completed. It + // returns -1 on errors. + virtual base::unique_fd flush() = 0; + // finish waits until RenderEngine command stream has been executed. It + // returns false on errors. + virtual bool finish() = 0; + // waitFence inserts a wait on an external fence fd to RenderEngine + // command stream. It returns false on errors. + virtual bool waitFence(base::unique_fd fenceFd) = 0; + + virtual void clearWithColor(float red, float green, float blue, float alpha) = 0; + virtual void fillRegionWithColor(const Region& region, float red, float green, float blue, + float alpha) = 0; + + virtual void setScissor(const Rect& region) = 0; + virtual void disableScissor() = 0; + virtual void genTextures(size_t count, uint32_t* names) = 0; + virtual void deleteTextures(size_t count, uint32_t const* names) = 0; + virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; + // 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 displayFence fires. + // @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. + // TODO(alecmouri): Consider making this a multi-display API, so that the + // caller deoes not need to handle multiple fences. + virtual status_t drawLayers(const DisplaySettings& display, + const std::vector<LayerSettings>& layers, + ANativeWindowBuffer* buffer, base::unique_fd* drawFence) = 0; + + // TODO(alecmouri): Expose something like bindTexImage() so that devices + // that don't support native sync fences can get rid of code duplicated + // between BufferStateLayer and BufferQueueLayer for binding an external + // texture. + + // TODO(alecmouri): Add API to help with managing a texture pool. +}; + +class BindNativeBufferAsFramebuffer { +public: + BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer) + : mEngine(engine), mFramebuffer(mEngine.createFramebuffer()), mStatus(NO_ERROR) { + mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected()) + ? mEngine.bindFrameBuffer(mFramebuffer.get()) + : NO_MEMORY; + } + ~BindNativeBufferAsFramebuffer() { + mFramebuffer->setNativeWindowBuffer(nullptr, false); + mEngine.unbindFrameBuffer(mFramebuffer.get()); + } + status_t getStatus() const { return mStatus; } + +private: + RenderEngine& mEngine; + std::unique_ptr<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 index 56b6b31573..c69ace0603 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 @@ public: 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/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h new file mode 100644 index 0000000000..bd2055f4e5 --- /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/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp new file mode 100644 index 0000000000..9b483ef51d --- /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 0000000000..a0542dd0cd --- /dev/null +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -0,0 +1,446 @@ +/* + * 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> + +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 { + 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, + "output"); + } + + RenderEngineTest() { mBuffer = allocateDefaultBuffer(); } + + 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); + } + + static 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(), &fence); + + int fd = fence.release(); + if (fd >= 0) { + sync_wait(fd, -1); + close(fd); + } + + ASSERT_EQ(NO_ERROR, status); + } + + static 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 fillBufferLayerTransform(); + + template <typename SourceVariant> + void fillBufferColorTransform(); + + // Dumb hack to get aroud the fact that tear-down for renderengine isn't + // well defined right now, so we can't create multiple instances + static std::unique_ptr<renderengine::RenderEngine> sRE; + + sp<GraphicBuffer> mBuffer; +}; + +std::unique_ptr<renderengine::RenderEngine> RenderEngineTest::sRE = + renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), 0); + +struct ColorSourceVariant { + static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b) { + layer.source.solidColor = half3(r, g, b); + } +}; + +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); + 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); + 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); + 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); + 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); + 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::fillBufferLayerTransform() { + 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); + layer.source.solidColor = half3(1.0f, 0.0f, 0.0f); + layer.alpha = 1.0f; + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); + + 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::fillBufferColorTransform() { + 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(); + layer.source.solidColor = half3(0.5f, 0.25f, 0.125f); + 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); + + layers.push_back(layer); + + invokeDraw(settings, layers, mBuffer); + + expectBufferColor(fullscreenRect(), 191, 0, 0, 255); +} + +TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) { + drawEmptyLayers(); +} + +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>(); +} + +} // namespace android diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index a0e368c7e4..d9a986ed00 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -318,7 +318,7 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi // 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 6f68fb5f7b..46ba7c6250 100644 --- a/libs/sensor/SensorEventQueue.cpp +++ b/libs/sensor/SensorEventQueue.cpp @@ -37,7 +37,7 @@ namespace android { // ---------------------------------------------------------------------------- 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 +82,9 @@ ssize_t SensorEventQueue::read(ASensorEvent* events, size_t numEvents) { 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 +97,7 @@ status_t SensorEventQueue::waitForEvent() const 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 diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index b9ae524ee8..5840d51079 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -62,7 +62,7 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) // to the wrong package and stats based on app ops may be slightly off. if (opPackageName.size() <= 0) { sp<IBinder> binder = defaultServiceManager()->getService(String16("permission")); - if (binder != 0) { + if (binder != nullptr) { const uid_t uid = IPCThreadState::self()->getCallingUid(); Vector<String16> packages; interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages); @@ -93,7 +93,7 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) } SensorManager::SensorManager(const String16& opPackageName) - : mSensorList(0), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { + : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { // okay we're not locked here, but it's not needed during construction assertStateLocked(); } @@ -128,13 +128,13 @@ void SensorManager::sensorManagerDied() { 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 @@ status_t SensorManager::assertStateLocked() { 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 @@ Sensor const* SensorManager::getDefaultSensor(int type) } } } - return NULL; + return nullptr; } sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) { @@ -232,10 +232,10 @@ sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mo 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/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp new file mode 100644 index 0000000000..e0e3469fbb --- /dev/null +++ b/libs/sensorprivacy/Android.bp @@ -0,0 +1,47 @@ +// Copyright 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_shared { + name: "libsensorprivacy", + + aidl: { + export_aidl_headers: true, + local_include_dirs: ["aidl"], + }, + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wzero-as-null-pointer-constant", + ], + + srcs: [ + "aidl/android/hardware/ISensorPrivacyListener.aidl", + "aidl/android/hardware/ISensorPrivacyManager.aidl", + "SensorPrivacyManager.cpp", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libutils", + "liblog", + "libhardware", + ], + + export_include_dirs: ["include"], + + export_shared_lib_headers: ["libbinder"], +} diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp new file mode 100644 index 0000000000..1da79a0c8e --- /dev/null +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -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. + */ + +#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; +} + +}; // namespace android diff --git a/services/thermalservice/aidl/android/os/IThermalEventListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl index 050325e2fc..58177d837f 100644 --- a/services/thermalservice/aidl/android/os/IThermalEventListener.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017, 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. @@ -14,19 +14,11 @@ * limitations under the License. */ -package android.os; - -import android.os.Temperature; +package android.hardware; /** - * 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); + * @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 0000000000..4c2d5dbb8f --- /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 0000000000..882659598c --- /dev/null +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h @@ -0,0 +1,47 @@ +/* + * 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(); + +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 d25ad1a46d..956465c52a 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -53,6 +53,9 @@ cc_library_shared { srcs: [ "ColorSpace.cpp", + "BufferHubBuffer.cpp", + "BufferHubEventFd.cpp", + "BufferHubMetadata.cpp", "DebugUtils.cpp", "Fence.cpp", "FenceTime.cpp", @@ -65,6 +68,7 @@ cc_library_shared { "PixelFormat.cpp", "Rect.cpp", "Region.cpp", + "Transform.cpp", "UiConfig.cpp", ], @@ -74,7 +78,7 @@ cc_library_shared { shared_libs: [ "android.hardware.graphics.allocator@2.0", - "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", "android.hardware.configstore@1.0", @@ -89,10 +93,11 @@ cc_library_shared { "libutils", "libutilscallstack", "liblog", + "libpdx_default_transport", // TODO(b/112338294): Remove this once BufferHub moved to use Binder. ], export_shared_lib_headers: [ - "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", ], static_libs: [ @@ -101,9 +106,32 @@ cc_library_shared { "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", + "libnativewindow_headers", + ], + exclude_shared_libs: [ + "libpdx_default_transport", + ], + }, + }, + header_libs: [ "libbase_headers", + "libbufferhub_headers", + "libdvr_headers", "libnativebase_headers", + "libnativewindow_headers", "libhardware_headers", "libui_headers", "libpdx_headers", @@ -120,6 +148,9 @@ cc_library_shared { "libhardware_headers", "libui_headers", ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } cc_library_headers { @@ -128,6 +159,7 @@ cc_library_headers { vendor_available: true, target: { vendor: { + cflags: ["-DLIBUI_IN_VNDK"], override_export_include_dirs: ["include_vndk"], }, }, diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp new file mode 100644 index 0000000000..0582e1afe7 --- /dev/null +++ b/libs/ui/BufferHubBuffer.cpp @@ -0,0 +1,310 @@ +/* + * 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. + */ + +// We would eliminate the clang warnings introduced by libdpx. +// TODO(b/112338294): Remove those once BufferHub moved to use Binder +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wdouble-promotion" +#pragma clang diagnostic ignored "-Wgnu-case-range" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Winconsistent-missing-destructor-override" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wundefined-func-template" +#pragma clang diagnostic ignored "-Wunused-template" +#pragma clang diagnostic ignored "-Wweak-vtables" +#include <pdx/default_transport/client_channel.h> +#include <pdx/default_transport/client_channel_factory.h> +#include <pdx/file_handle.h> +#include <private/dvr/bufferhub_rpc.h> +#pragma clang diagnostic pop + +#include <poll.h> + +#include <android-base/unique_fd.h> +#include <ui/BufferHubBuffer.h> +#include <ui/BufferHubDefs.h> + +using android::base::unique_fd; +using android::dvr::BufferTraits; +using android::dvr::DetachedBufferRPC; +using android::dvr::NativeHandleWrapper; + +// TODO(b/112338294): Remove PDX dependencies from libui. +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 { + +// TODO(b/112338294): Remove this string literal after refactoring BufferHub +// to use Binder. +static constexpr char kBufferHubClientPath[] = "system/buffer_hub/client"; + +using BufferHubDefs::AnyClientAcquired; +using BufferHubDefs::AnyClientGained; +using BufferHubDefs::AnyClientPosted; +using BufferHubDefs::IsClientAcquired; +using BufferHubDefs::IsClientGained; +using BufferHubDefs::IsClientPosted; +using BufferHubDefs::IsClientReleased; +using BufferHubDefs::kHighBitsMask; + +} // namespace + +BufferHubClient::BufferHubClient() : Client(ClientChannelFactory::Create(kBufferHubClientPath)) {} + +BufferHubClient::BufferHubClient(LocalChannelHandle mChannelHandle) + : Client(ClientChannel::Create(std::move(mChannelHandle))) {} + +BufferHubClient::~BufferHubClient() {} + +bool BufferHubClient::IsValid() const { + return IsConnected() && GetChannelHandle().valid(); +} + +LocalChannelHandle BufferHubClient::TakeChannelHandle() { + if (IsConnected()) { + return std::move(GetChannelHandle()); + } else { + return {}; + } +} + +BufferHubBuffer::BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, + uint32_t format, uint64_t usage, size_t mUserMetadataSize) { + ATRACE_CALL(); + ALOGD("%s: width=%u height=%u layerCount=%u, format=%u usage=%" PRIx64 " mUserMetadataSize=%zu", + __FUNCTION__, width, height, layerCount, format, usage, mUserMetadataSize); + + auto status = + mClient.InvokeRemoteMethod<DetachedBufferRPC::Create>(width, height, layerCount, format, + usage, mUserMetadataSize); + if (!status) { + ALOGE("%s: Failed to create detached buffer: %s", __FUNCTION__, + status.GetErrorMessage().c_str()); + mClient.Close(-status.error()); + } + + const int ret = ImportGraphicBuffer(); + if (ret < 0) { + ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-ret)); + mClient.Close(ret); + } +} + +BufferHubBuffer::BufferHubBuffer(LocalChannelHandle mChannelHandle) + : mClient(std::move(mChannelHandle)) { + const int ret = ImportGraphicBuffer(); + if (ret < 0) { + ALOGE("%s: Failed to import buffer: %s", __FUNCTION__, strerror(-ret)); + mClient.Close(ret); + } +} + +int BufferHubBuffer::ImportGraphicBuffer() { + ATRACE_CALL(); + + auto status = mClient.InvokeRemoteMethod<DetachedBufferRPC::Import>(); + if (!status) { + ALOGE("%s: Failed to import GraphicBuffer: %s", __FUNCTION__, + status.GetErrorMessage().c_str()); + return -status.error(); + } + + BufferTraits<LocalHandle> bufferTraits = status.take(); + if (bufferTraits.id() < 0) { + ALOGE("%s: Received an invalid id!", __FUNCTION__); + return -EIO; + } + + // Stash the buffer id to replace the value in mId. + const int bufferId = bufferTraits.id(); + + // Import the metadata. + LocalHandle metadataHandle = bufferTraits.take_metadata_handle(); + unique_fd metadataFd(metadataHandle.Release()); + mMetadata = BufferHubMetadata::Import(std::move(metadataFd)); + + if (!mMetadata.IsValid()) { + ALOGE("%s: invalid metadata.", __FUNCTION__); + return -ENOMEM; + } + + if (mMetadata.metadata_size() != bufferTraits.metadata_size()) { + ALOGE("%s: metadata buffer too small: %zu, expected: %" PRIu64 ".", __FUNCTION__, + mMetadata.metadata_size(), bufferTraits.metadata_size()); + return -ENOMEM; + } + + size_t metadataSize = static_cast<size_t>(bufferTraits.metadata_size()); + if (metadataSize < BufferHubDefs::kMetadataHeaderSize) { + ALOGE("%s: metadata too small: %zu", __FUNCTION__, metadataSize); + return -EINVAL; + } + + // Populate shortcuts to the atomics in metadata. + auto metadata_header = mMetadata.metadata_header(); + buffer_state_ = &metadata_header->buffer_state; + fence_state_ = &metadata_header->fence_state; + active_clients_bit_mask_ = &metadata_header->active_clients_bit_mask; + // 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(buffer_state_) || + !std::atomic_is_lock_free(fence_state_) || + !std::atomic_is_lock_free(active_clients_bit_mask_), + "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 = bufferTraits.take_buffer_handle(); + + // Populate buffer desc based on buffer traits. + mBufferDesc.width = bufferTraits.width(); + mBufferDesc.height = bufferTraits.height(); + mBufferDesc.layers = bufferTraits.layer_count(); + mBufferDesc.format = bufferTraits.format(); + mBufferDesc.usage = bufferTraits.usage(); + mBufferDesc.stride = bufferTraits.stride(); + mBufferDesc.rfu0 = 0U; + mBufferDesc.rfu1 = 0U; + + // If all imports succeed, replace the previous buffer and id. + mId = bufferId; + mClientStateMask = bufferTraits.client_state_mask(); + + // TODO(b/112012161) Set up shared fences. + ALOGD("%s: id=%d, buffer_state=%" PRIx32 ".", __FUNCTION__, id(), + buffer_state_->load(std::memory_order_acquire)); + return 0; +} + +int BufferHubBuffer::Gain() { + uint32_t current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientGained(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already gained by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + do { + if (AnyClientGained(current_buffer_state & (~mClientStateMask)) || + AnyClientAcquired(current_buffer_state)) { + ALOGE("%s: Buffer is in use, id=%d mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Change the buffer state to gained state, whose value happens to be the same as + // mClientStateMask. + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, 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 current_buffer_state = buffer_state_->load(std::memory_order_acquire); + uint32_t current_active_clients_bit_mask = 0U; + uint32_t updated_buffer_state = 0U; + do { + if (!IsClientGained(current_buffer_state, mClientStateMask)) { + ALOGE("%s: Cannot post a buffer that is not gained by this client. buffer_id=%d " + "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Set the producer client buffer state to released, other clients' buffer state to posted. + current_active_clients_bit_mask = active_clients_bit_mask_->load(std::memory_order_acquire); + updated_buffer_state = + current_active_clients_bit_mask & (~mClientStateMask) & kHighBitsMask; + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + 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 current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientAcquired(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already acquired by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint32_t updated_buffer_state = 0U; + do { + if (!IsClientPosted(current_buffer_state, mClientStateMask)) { + ALOGE("%s: Cannot acquire a buffer that is not in posted state. buffer_id=%d " + "mClientStateMask=%" PRIx32 " state=%" PRIx32 ".", + __FUNCTION__, mId, mClientStateMask, current_buffer_state); + return -EBUSY; + } + // Change the buffer state for this consumer from posted to acquired. + updated_buffer_state = current_buffer_state ^ mClientStateMask; + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + 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 current_buffer_state = buffer_state_->load(std::memory_order_acquire); + if (IsClientReleased(current_buffer_state, mClientStateMask)) { + ALOGV("%s: Buffer is already released by this client %" PRIx32 ".", __FUNCTION__, + mClientStateMask); + return 0; + } + uint32_t updated_buffer_state = 0U; + do { + updated_buffer_state = current_buffer_state & (~mClientStateMask); + } while (!buffer_state_->compare_exchange_weak(current_buffer_state, updated_buffer_state, + 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::Poll(int timeoutMs) { + ATRACE_CALL(); + + pollfd p = {mClient.event_fd(), POLLIN, 0}; + return poll(&p, 1, timeoutMs); +} + +Status<LocalChannelHandle> BufferHubBuffer::Duplicate() { + ATRACE_CALL(); + ALOGD("%s: id=%d.", __FUNCTION__, mId); + + auto statusOrHandle = mClient.InvokeRemoteMethod<DetachedBufferRPC::Duplicate>(); + + if (!statusOrHandle.ok()) { + ALOGE("%s: Failed to duplicate buffer (id=%d): %s.", __FUNCTION__, mId, + statusOrHandle.GetErrorMessage().c_str()); + } + return statusOrHandle; +} + +} // namespace android diff --git a/libs/ui/BufferHubEventFd.cpp b/libs/ui/BufferHubEventFd.cpp new file mode 100644 index 0000000000..978b3526f3 --- /dev/null +++ b/libs/ui/BufferHubEventFd.cpp @@ -0,0 +1,47 @@ +/* + * 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)) {} + +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 0000000000..816707db9d --- /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, metadata_size()); + ALOGE_IF(ret != 0, + "BufferHubMetadata::~BufferHubMetadata: failed to unmap ashmem, error=%d.", errno); + mMetadataHeader = nullptr; + } +} + +} // namespace android diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index 61df02d41d..ee06d930d8 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -234,6 +234,9 @@ std::string decodeColorMode(ColorMode colorMode) { case ColorMode::BT2020: return std::string("ColorMode::BT2020"); + case ColorMode::DISPLAY_BT2020: + return std::string("ColorMode::DISPLAY_BT2020"); + case ColorMode::BT2100_PQ: return std::string("ColorMode::BT2100_PQ"); diff --git a/libs/ui/FenceTime.cpp b/libs/ui/FenceTime.cpp index 14147663de..340231d352 100644 --- a/libs/ui/FenceTime.cpp +++ b/libs/ui/FenceTime.cpp @@ -33,18 +33,6 @@ namespace android { const auto FenceTime::NO_FENCE = std::make_shared<FenceTime>(Fence::NO_FENCE); -void* FenceTime::operator new(size_t byteCount) noexcept { - void *p = nullptr; - if (posix_memalign(&p, alignof(FenceTime), byteCount)) { - return nullptr; - } - return p; -} - -void FenceTime::operator delete(void *p) { - free(p); -} - FenceTime::FenceTime(const sp<Fence>& fence) : mState(((fence.get() != nullptr) && fence->isValid()) ? State::VALID : State::INVALID), diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp index 37cf617374..20f27c53e3 100644 --- a/libs/ui/Gralloc2.cpp +++ b/libs/ui/Gralloc2.cpp @@ -42,9 +42,6 @@ uint64_t getValid10UsageBits() { 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; diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 4aa9f628ce..f408fcbe84 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -22,7 +22,10 @@ #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> @@ -90,6 +93,22 @@ GraphicBuffer::GraphicBuffer(const native_handle_t* inHandle, HandleWrapMethod m 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() { if (handle) { @@ -484,23 +503,11 @@ status_t GraphicBuffer::unflatten( return NO_ERROR; } -bool GraphicBuffer::isDetachedBuffer() const { - return mDetachedBufferHandle && mDetachedBufferHandle->isValid(); -} - -status_t GraphicBuffer::setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> channel) { - if (isDetachedBuffer()) { - ALOGW("setDetachedBuffer: there is already a BufferHub channel associated with this " - "GraphicBuffer. Replacing the old one."); - } - - mDetachedBufferHandle = std::move(channel); - return NO_ERROR; -} - -std::unique_ptr<DetachedBufferHandle> GraphicBuffer::takeDetachedBufferHandle() { - return std::move(mDetachedBufferHandle); +#ifndef LIBUI_IN_VNDK +bool GraphicBuffer::isBufferHubBuffer() const { + return mBufferHubBuffer != nullptr; } +#endif // LIBUI_IN_VNDK // --------------------------------------------------------------------------- diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index eaba1ed1aa..f56e6b9e86 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -24,9 +24,9 @@ #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/Gralloc2.h> @@ -35,6 +35,8 @@ namespace android { // --------------------------------------------------------------------------- +using base::StringAppendF; + ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferAllocator ) Mutex GraphicBufferAllocator::sLock; @@ -50,46 +52,37 @@ GraphicBufferAllocator::GraphicBufferAllocator() GraphicBufferAllocator::~GraphicBufferAllocator() {} -void GraphicBufferAllocator::dump(String8& result) const -{ +void GraphicBufferAllocator::dump(std::string& result) const { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); size_t total = 0; - const size_t SIZE = 4096; - char buffer[SIZE]; - snprintf(buffer, SIZE, "Allocated buffers:\n"); - result.append(buffer); + result.append("Allocated buffers:\n"); const size_t c = list.size(); for (size_t i=0 ; i<c ; i++) { const alloc_rec_t& rec(list.valueAt(i)); if (rec.size) { - snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 - " | %s\n", - list.keyAt(i), rec.size/1024.0, - rec.width, rec.stride, rec.height, rec.layerCount, rec.format, - rec.usage, rec.requestorName.c_str()); + StringAppendF(&result, + "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", + list.keyAt(i), rec.size / 1024.0, rec.width, rec.stride, rec.height, + rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str()); } else { - snprintf(buffer, SIZE, "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 - " | %s\n", - list.keyAt(i), - rec.width, rec.stride, rec.height, rec.layerCount, rec.format, - rec.usage, rec.requestorName.c_str()); + StringAppendF(&result, + "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", + list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount, + rec.format, rec.usage, rec.requestorName.c_str()); } - result.append(buffer); total += rec.size; } - snprintf(buffer, SIZE, "Total allocated (estimate): %.2f KB\n", total/1024.0); - result.append(buffer); + StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", total / 1024.0); - std::string deviceDump = mAllocator->dumpDebugInfo(); - result.append(deviceDump.c_str(), deviceDump.size()); + result.append(mAllocator->dumpDebugInfo()); } void GraphicBufferAllocator::dumpToSystemLog() { - String8 s; + std::string s; GraphicBufferAllocator::getInstance().dump(s); - ALOGD("%s", s.string()); + ALOGD("%s", s.c_str()); } status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, @@ -108,6 +101,9 @@ status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, if (layerCount < 1) layerCount = 1; + // TODO(b/72323293, b/72703005): Remove these invalid bits from callers + usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13)); + Gralloc2::IMapper::BufferDescriptorInfo info = {}; info.width = width; info.height = height; diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index d8702e5755..13fed3a239 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp @@ -72,6 +72,14 @@ Rect& Rect::offsetBy(int32_t x, int32_t y) { 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 fe4ae6c414..3bd3748439 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -19,8 +19,9 @@ #include <inttypes.h> #include <limits.h> +#include <android-base/stringprintf.h> + #include <utils/Log.h> -#include <utils/String8.h> #include <utils/CallStack.h> #include <ui/Rect.h> @@ -41,6 +42,8 @@ namespace android { // ---------------------------------------------------------------------------- +using base::StringAppendF; + enum { op_nand = region_operator<Rect>::op_nand, op_and = region_operator<Rect>::op_and, @@ -325,6 +328,20 @@ Region& Region::translateSelf(int x, int y) { return *this; } +Region& Region::scaleSelf(float sx, float sy) { + size_t count = mStorage.size(); + Rect* rects = mStorage.editArray(); + while (count) { + rects->left = static_cast<int32_t>(rects->left * sx + 0.5f); + rects->right = static_cast<int32_t>(rects->right * sx + 0.5f); + rects->top = static_cast<int32_t>(rects->top * sy + 0.5f); + rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f); + rects++; + count--; + } + return *this; +} + // ---------------------------------------------------------------------------- const Region Region::merge(const Rect& rhs) const { @@ -854,16 +871,14 @@ Rect const* Region::getArray(size_t* count) const { // ---------------------------------------------------------------------------- -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/Transform.cpp b/libs/ui/Transform.cpp index e05ed5375e..25128effaf 100644 --- a/services/surfaceflinger/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -17,17 +17,12 @@ #include <math.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 +36,7 @@ Transform::Transform(uint32_t orientation) { set(orientation, 0, 0); } -Transform::~Transform() { -} +Transform::~Transform() = default; static const float EPSILON = 0.0f; @@ -67,7 +61,7 @@ Transform Transform::operator * (const Transform& rhs) const 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 +77,12 @@ Transform Transform::operator * (const Transform& rhs) const 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 +95,20 @@ float Transform::ty() const { 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 +145,7 @@ status_t Transform::set(uint32_t flags, float w, float h) 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 +218,15 @@ Rect Transform::transform(const Rect& bounds, bool roundOutwards) const 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 +245,10 @@ FloatRect Transform::transform(const FloatRect& bounds) const 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 +267,8 @@ Region Transform::transform(const Region& reg) const 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 +351,7 @@ Transform Transform::inverse() const { 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; @@ -404,28 +412,13 @@ void Transform::dump(const char* name) const type.append("TRANSLATE "); 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]); + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][0]), static_cast<double>(m[1][0]), + static_cast<double>(m[2][0])); + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][1]), static_cast<double>(m[1][1]), + static_cast<double>(m[2][1])); + ALOGD("%.4f %.4f %.4f", static_cast<double>(m[0][2]), static_cast<double>(m[1][2]), + static_cast<double>(m[2][2])); } -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; - } -} - -// --------------------------------------------------------------------------- - -}; // namespace android +} // namespace ui +} // namespace android diff --git a/libs/ui/UiConfig.cpp b/libs/ui/UiConfig.cpp index 7730690cfd..0ac863d718 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 0000000000..90dd391d8d --- /dev/null +++ b/libs/ui/include/ui/BufferHubBuffer.h @@ -0,0 +1,179 @@ +/* + * 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_ + +// We would eliminate the clang warnings introduced by libdpx. +// TODO(b/112338294): Remove those once BufferHub moved to use Binder +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wdouble-promotion" +#pragma clang diagnostic ignored "-Wgnu-case-range" +#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +#pragma clang diagnostic ignored "-Winconsistent-missing-destructor-override" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#pragma clang diagnostic ignored "-Wpacked" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wundefined-func-template" +#pragma clang diagnostic ignored "-Wunused-template" +#pragma clang diagnostic ignored "-Wweak-vtables" +#include <pdx/client.h> +#include <private/dvr/native_handle_wrapper.h> +#pragma clang diagnostic pop + +#include <android/hardware_buffer.h> +#include <ui/BufferHubDefs.h> +#include <ui/BufferHubMetadata.h> + +namespace android { + +class BufferHubClient : public pdx::Client { +public: + BufferHubClient(); + virtual ~BufferHubClient(); + explicit BufferHubClient(pdx::LocalChannelHandle mChannelHandle); + + bool IsValid() const; + pdx::LocalChannelHandle TakeChannelHandle(); + + using pdx::Client::Close; + using pdx::Client::event_fd; + using pdx::Client::GetChannel; + using pdx::Client::InvokeRemoteMethod; +}; + +class BufferHubBuffer { +public: + // Allocates a standalone BufferHubBuffer not associated with any producer consumer set. + static std::unique_ptr<BufferHubBuffer> Create(uint32_t width, uint32_t height, + uint32_t layerCount, uint32_t format, + uint64_t usage, size_t userMetadataSize) { + return std::unique_ptr<BufferHubBuffer>( + new BufferHubBuffer(width, height, layerCount, format, usage, userMetadataSize)); + } + + // Imports the given channel handle to a BufferHubBuffer, taking ownership. + static std::unique_ptr<BufferHubBuffer> Import(pdx::LocalChannelHandle mChannelHandle) { + return std::unique_ptr<BufferHubBuffer>(new BufferHubBuffer(std::move(mChannelHandle))); + } + + BufferHubBuffer(const BufferHubBuffer&) = delete; + void operator=(const BufferHubBuffer&) = delete; + + // Gets ID of the buffer client. All BufferHubBuffer clients derived from the same buffer in + // bufferhubd share the same buffer id. + int id() const { return mId; } + + // Returns the buffer description, which is guaranteed to be faithful values from bufferhubd. + const AHardwareBuffer_Desc& desc() const { return mBufferDesc; } + + const native_handle_t* DuplicateHandle() { return mBufferHandle.DuplicateHandle(); } + + // Returns the current value of MetadataHeader::buffer_state. + uint32_t buffer_state() { + return mMetadata.metadata_header()->buffer_state.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 client_state_mask() const { return mClientStateMask; } + + size_t user_metadata_size() const { return mMetadata.user_metadata_size(); } + + // Returns true if the buffer holds an open PDX channels towards bufferhubd. + bool IsConnected() const { return mClient.IsValid(); } + + // Returns true if the buffer holds an valid native buffer handle that's availble for the client + // to read from and/or write into. + bool IsValid() const { return mBufferHandle.IsValid(); } + + // 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 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 = mClient.GetChannel()) { + return channel->GetEventMask(events); + } else { + return pdx::ErrorStatus(EINVAL); + } + } + + // Polls the fd for |timeoutMs| milliseconds (-1 for infinity). + int Poll(int timeoutMs); + + // Creates a BufferHubBuffer client from an existing one. The new client will + // share the same underlying gralloc buffer and ashmem region for metadata. + pdx::Status<pdx::LocalChannelHandle> Duplicate(); + +private: + BufferHubBuffer(uint32_t width, uint32_t height, uint32_t layerCount, uint32_t format, + uint64_t usage, size_t userMetadataSize); + + BufferHubBuffer(pdx::LocalChannelHandle mChannelHandle); + + int ImportGraphicBuffer(); + + // Global id for the buffer that is consistent across processes. + int mId = -1; + + // 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; + + // Wrapps the gralloc buffer handle of this buffer. + dvr::NativeHandleWrapper<pdx::LocalHandle> mBufferHandle; + + // 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>* buffer_state_ = nullptr; + std::atomic<uint32_t>* fence_state_ = nullptr; + std::atomic<uint32_t>* active_clients_bit_mask_ = nullptr; + + // PDX backend. + BufferHubClient mClient; +}; + +} // 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 0000000000..d259fefb8f --- /dev/null +++ b/libs/ui/include/ui/BufferHubDefs.h @@ -0,0 +1,165 @@ +/* + * 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 +// buffer_state; the second bit locates in the last 16 bits of buffer_state. +// 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 AnyClientGained(uint32_t state) { + uint32_t high_bits = state >> kMaxNumberOfClients; + uint32_t low_bits = state & kLowbitsMask; + return high_bits == low_bits && low_bits != 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 AnyClientPosted(uint32_t state) { + uint32_t high_bits = state >> kMaxNumberOfClients; + uint32_t low_bits = state & kLowbitsMask; + uint32_t posted_or_acquired = high_bits ^ low_bits; + return posted_or_acquired & high_bits; +} + +// Returns true if the input client is in posted state. +static inline bool IsClientPosted(uint32_t state, uint32_t client_bit_mask) { + uint32_t client_bits = state & client_bit_mask; + if (client_bits == 0U) return false; + uint32_t low_bits = client_bits & kLowbitsMask; + return low_bits == 0U; +} + +// Return true if any of the client is in acquired state. +static inline bool AnyClientAcquired(uint32_t state) { + uint32_t high_bits = state >> kMaxNumberOfClients; + uint32_t low_bits = state & kLowbitsMask; + uint32_t posted_or_acquired = high_bits ^ low_bits; + return posted_or_acquired & low_bits; +} + +// Return true if the input client is in acquired state. +static inline bool IsClientAcquired(uint32_t state, uint32_t client_bit_mask) { + uint32_t client_bits = state & client_bit_mask; + if (client_bits == 0U) return false; + uint32_t high_bits = client_bits & kHighBitsMask; + return high_bits == 0U; +} + +// Returns true if all clients are in released state. +static inline bool IsBufferReleased(uint32_t state) { + return state == 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 low_union = union_bits & kLowbitsMask; + if (low_union == kLowbitsMask) return 0U; + uint32_t incremented = low_union + 1U; + uint32_t difference = incremented ^ low_union; + uint32_t new_low_bit = (difference + 1U) >> 1; + return new_low_bit + (new_low_bit << 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 + // buffer_state. + std::atomic<uint32_t> buffer_state; + + // Every client takes up one bit in fence_state. 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> fence_state; + + // Every client takes up one bit from the higher 32 bits and one bit from the lower 32 bits in + // active_clients_bit_mask. + std::atomic<uint32_t> active_clients_bit_mask; + + // Explicit padding 4 bytes. + uint32_t padding; + + // The index of the buffer queue where the buffer belongs to. + 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 + +} // 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 0000000000..0e856bdb67 --- /dev/null +++ b/libs/ui/include/ui/BufferHubEventFd.h @@ -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. + */ + +#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(); + + /** + * 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 0000000000..212189497a --- /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 user_metadata_size() const { return mUserMetadataSize; } + size_t metadata_size() const { return mUserMetadataSize + BufferHubDefs::kMetadataHeaderSize; } + + const unique_fd& ashmem_fd() const { return mAshmemFd; } + BufferHubDefs::MetadataHeader* metadata_header() { 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/DetachedBufferHandle.h b/libs/ui/include/ui/DetachedBufferHandle.h deleted file mode 100644 index f3c328d52b..0000000000 --- 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 94caf6b9d3..8976d2d584 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h @@ -35,6 +35,8 @@ struct DisplayInfo { 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 0000000000..7a70ea1f06 --- /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/FenceTime.h b/libs/ui/include/ui/FenceTime.h index 871fcf2dfe..a5a1fcbde7 100644 --- a/libs/ui/include/ui/FenceTime.h +++ b/libs/ui/include/ui/FenceTime.h @@ -113,11 +113,6 @@ public: void signalForTest(nsecs_t signalTime); - // Override new and delete since this needs 8-byte alignment, which - // is not guaranteed on x86. - static void* operator new(size_t nbytes) noexcept; - static void operator delete(void *p); - private: // For tests only. If forceValidForTest is true, then getSignalTime will // never return SIGNAL_TIME_INVALID and isValid will always return true. diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h index 6a7479a68a..4cd9a0b236 100644 --- a/libs/ui/include/ui/FloatRect.h +++ b/libs/ui/include/ui/FloatRect.h @@ -28,7 +28,7 @@ public: 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 @@ public: (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/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index 315db110a1..b73ca2b793 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -34,7 +34,10 @@ namespace android { -class DetachedBufferHandle; +#ifndef LIBUI_IN_VNDK +class BufferHubBuffer; +#endif // LIBUI_IN_VNDK + class GraphicBufferMapper; // =========================================================================== @@ -134,6 +137,11 @@ public: 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 +153,7 @@ public: 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) { @@ -189,10 +198,10 @@ public: 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(); +#ifndef LIBUI_IN_VNDK + // Returns whether this GraphicBuffer is backed by BufferHubBuffer. + bool isBufferHubBuffer() const; +#endif // LIBUI_IN_VNDK private: ~GraphicBuffer(); @@ -239,21 +248,21 @@ private: uint64_t mId; + // System unique buffer ID. Note that this is different from mId, which is process unique. For + // GraphicBuffer backed by BufferHub, the mBufferId is a system unique identifier that stays the + // same cross process for the same chunck of underlying memory. Also note that this only applies + // to GraphicBuffers that are backed by BufferHub. + int32_t mBufferId = -1; + // Stores the generation number of this buffer. If this number does not // match the BufferQueue's internal generation number (set through // IGBP::setGenerationNumber), attempts to attach the buffer will fail. uint32_t mGenerationNumber; - // Stores a BufferHub handle that can be used to re-attach this GraphicBuffer back into a - // BufferHub producer/consumer set. In terms of GraphicBuffer's relationship with BufferHub, - // there are three different modes: - // 1. Legacy mode: GraphicBuffer is not backed by BufferHub and mDetachedBufferHandle must be - // invalid. - // 2. Detached mode: GraphicBuffer is backed by BufferHub, but not part of a producer/consumer - // set. In this mode, mDetachedBufferHandle must be valid. - // 3. Attached mode: GraphicBuffer is backed by BufferHub and it's part of a producer/consumer - // set. In this mode, mDetachedBufferHandle must be invalid. - std::unique_ptr<DetachedBufferHandle> mDetachedBufferHandle; +#ifndef LIBUI_IN_VNDK + // Stores a BufferHubBuffer that handles buffer signaling, identification. + std::unique_ptr<BufferHubBuffer> mBufferHubBuffer; +#endif // LIBUI_IN_VNDK }; }; // namespace android diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 14a865e16c..7e2b230fe9 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -39,7 +39,6 @@ class Allocator; } class GraphicBufferMapper; -class String8; class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator> { @@ -53,7 +52,7 @@ public: status_t free(buffer_handle_t handle); - void dump(String8& res) const; + void dump(std::string& res) const; static void dumpToSystemLog(); private: diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h index 0fa819dce8..fb3c5f8eca 100644 --- a/libs/ui/include/ui/GraphicTypes.h +++ b/libs/ui/include/ui/GraphicTypes.h @@ -17,6 +17,7 @@ #pragma once #include <android/hardware/graphics/common/1.1/types.h> +#include <android/hardware/graphics/common/1.2/types.h> #include <system/graphics.h> // android::ui::* in this header file will alias different types as @@ -24,11 +25,11 @@ 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; } // namespace ui } // namespace android diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h index 0bec0b7f78..e9da087347 100644 --- a/libs/ui/include/ui/Rect.h +++ b/libs/ui/include/ui/Rect.h @@ -120,7 +120,7 @@ public: 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 +175,11 @@ public: 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 778845295f..79642ae032 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> -namespace android { -// --------------------------------------------------------------------------- +#include <android-base/macros.h> -class String8; +#include <string> +namespace android { // --------------------------------------------------------------------------- + class Region : public LightFlattenable<Region> { public: @@ -87,17 +88,19 @@ public: // 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 @@ public: 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/Transform.h b/libs/ui/include/ui/Transform.h new file mode 100644 index 0000000000..900a5c46eb --- /dev/null +++ b/libs/ui/include/ui/Transform.h @@ -0,0 +1,115 @@ +/* + * 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 <hardware/hardware.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; + + Transform inverse() const; + + // for debugging + 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 fcf8ed5d6b..d1d6014a7b 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/libs/ui/include_vndk/ui/DisplayedFrameStats.h b/libs/ui/include_vndk/ui/DisplayedFrameStats.h new file mode 120000 index 0000000000..6014e1954a --- /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/Transform.h b/libs/ui/include_vndk/ui/Transform.h new file mode 120000 index 0000000000..60633c2ef5 --- /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 aef6428cc8..a670b3ce63 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -30,7 +30,44 @@ cc_test { cc_test { name: "GraphicBuffer_test", - shared_libs: ["libpdx_default_transport", "libui", "libutils"], + header_libs: [ + "libbufferhub_headers", + "libdvr_headers", + "libnativewindow_headers", + ], + shared_libs: [ + "libpdx_default_transport", + "libui", + "libutils", + ], srcs: ["GraphicBuffer_test.cpp"], cflags: ["-Wall", "-Werror"], } + +cc_test { + name: "BufferHub_test", + header_libs: [ + "libbufferhub_headers", + "libdvr_headers", + "libnativewindow_headers", + ], + static_libs: [ + "libgmock", + ], + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libcutils", + "libhidlbase", + "libhwbinder", + "liblog", + "libpdx_default_transport", + "libui", + "libutils" + ], + srcs: [ + "BufferHubBuffer_test.cpp", + "BufferHubEventFd_test.cpp", + "BufferHubMetadata_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 0000000000..1b339a06d9 --- /dev/null +++ b/libs/ui/tests/BufferHubBuffer_test.cpp @@ -0,0 +1,344 @@ +/* + * 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 <android/frameworks/bufferhub/1.0/IBufferClient.h> +#include <android/frameworks/bufferhub/1.0/IBufferHub.h> +#include <android/hardware_buffer.h> +#include <cutils/native_handle.h> +#include <gtest/gtest.h> +#include <hidl/ServiceManagement.h> +#include <hwbinder/IPCThreadState.h> +#include <ui/BufferHubBuffer.h> + +namespace android { + +namespace { + +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; + +using BufferHubDefs::AnyClientAcquired; +using BufferHubDefs::AnyClientGained; +using BufferHubDefs::AnyClientPosted; +using BufferHubDefs::IsBufferReleased; +using BufferHubDefs::IsClientAcquired; +using BufferHubDefs::IsClientGained; +using BufferHubDefs::IsClientPosted; +using BufferHubDefs::IsClientReleased; +using BufferHubDefs::kFirstClientBitMask; +using BufferHubDefs::kMetadataHeaderSize; +using frameworks::bufferhub::V1_0::BufferHubStatus; +using frameworks::bufferhub::V1_0::IBufferClient; +using frameworks::bufferhub::V1_0::IBufferHub; +using hardware::hidl_handle; +using hardware::graphics::common::V1_2::HardwareBufferDescription; +using hidl::base::V1_0::IBase; +using pdx::LocalChannelHandle; + +class BufferHubBufferTest : public ::testing::Test { +protected: + void SetUp() override { android::hardware::ProcessState::self()->startThreadPool(); } +}; + +class BufferHubBufferStateTransitionTest : public BufferHubBufferTest { +protected: + void SetUp() override { + BufferHubBufferTest::SetUp(); + CreateTwoClientsOfABuffer(); + } + + std::unique_ptr<BufferHubBuffer> b1; + uint64_t b1ClientMask = 0U; + std::unique_ptr<BufferHubBuffer> b2; + uint64_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); + b1ClientMask = b1->client_state_mask(); + ASSERT_NE(b1ClientMask, 0U); + auto statusOrHandle = b1->Duplicate(); + ASSERT_TRUE(statusOrHandle); + LocalChannelHandle h2 = statusOrHandle.take(); + b2 = BufferHubBuffer::Import(std::move(h2)); + b2ClientMask = b2->client_state_mask(); + ASSERT_NE(b2ClientMask, 0U); + ASSERT_NE(b2ClientMask, b1ClientMask); +} + +TEST_F(BufferHubBufferTest, CreateBufferHubBufferFails) { + // 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_FALSE(b1->IsConnected()); + EXPECT_FALSE(b1->IsValid()); + + // 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_FALSE(b2->IsConnected()); + EXPECT_FALSE(b2->IsValid()); + + // 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_FALSE(b3->IsConnected()); + EXPECT_FALSE(b3->IsValid()); +} + +TEST_F(BufferHubBufferTest, CreateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + EXPECT_TRUE(b1->IsConnected()); + EXPECT_TRUE(b1->IsValid()); + EXPECT_NE(b1->id(), 0); +} + +TEST_F(BufferHubBufferTest, DuplicateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, kUsage, + kUserMetadataSize); + int id1 = b1->id(); + uint64_t bufferStateMask1 = b1->client_state_mask(); + EXPECT_NE(bufferStateMask1, 0U); + EXPECT_TRUE(b1->IsValid()); + EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize); + + auto statusOrHandle = b1->Duplicate(); + EXPECT_TRUE(statusOrHandle); + + // The detached buffer should still be valid. + EXPECT_TRUE(b1->IsConnected()); + EXPECT_TRUE(b1->IsValid()); + + // Gets the channel handle for the duplicated buffer. + LocalChannelHandle h2 = statusOrHandle.take(); + EXPECT_TRUE(h2.valid()); + + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(std::move(h2)); + EXPECT_FALSE(h2.valid()); + ASSERT_TRUE(b2 != nullptr); + EXPECT_TRUE(b2->IsValid()); + EXPECT_EQ(b2->user_metadata_size(), kUserMetadataSize); + + int id2 = b2->id(); + uint64_t bufferStateMask2 = b2->client_state_mask(); + EXPECT_NE(bufferStateMask2, 0U); + + // These two buffer instances are based on the same physical buffer under the + // hood, so they should share the same id. + EXPECT_EQ(id1, id2); + // We use client_state_mask() to tell those two instances apart. + EXPECT_NE(bufferStateMask1, bufferStateMask2); + + // Both buffer instances should be in released state currently. + EXPECT_TRUE(IsBufferReleased(b1->buffer_state())); + EXPECT_TRUE(IsBufferReleased(b2->buffer_state())); + + // TODO(b/112338294): rewrite test after migration + return; +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // 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->buffer_state(), b1ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, GainBuffer_fromGainedState) { + ASSERT_EQ(b1->Gain(), 0); + auto current_buffer_state = b1->buffer_state(); + ASSERT_TRUE(IsClientGained(current_buffer_state, 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(AnyClientAcquired(b1->buffer_state())); + + // 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(AnyClientPosted(b1->buffer_state())); + + // 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(AnyClientPosted(b1->buffer_state())); + + // 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->buffer_state(), b1ClientMask)); + + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromSelfInGainedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(IsClientGained(b1->buffer_state(), b1ClientMask)); + + EXPECT_EQ(b1->Post(), 0); + auto current_buffer_state = b1->buffer_state(); + EXPECT_TRUE(IsClientReleased(current_buffer_state, b1ClientMask)); + EXPECT_TRUE(IsClientPosted(current_buffer_state, b2ClientMask)); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + // 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(AnyClientAcquired(b1->buffer_state())); + + // Posting from acquired state should fail. + EXPECT_EQ(b1->Post(), -EBUSY); + EXPECT_EQ(b2->Post(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, PostBuffer_fromReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + // 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->buffer_state(), 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->buffer_state(), 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 current_buffer_state = b1->buffer_state(); + ASSERT_TRUE(IsClientAcquired(current_buffer_state, 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(IsBufferReleased(b1->buffer_state())); + + // 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(AnyClientGained(b1->buffer_state())); + + // Acquiring from gained state should fail. + EXPECT_EQ(b1->Acquire(), -EBUSY); + EXPECT_EQ(b2->Acquire(), -EBUSY); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInReleasedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + + EXPECT_EQ(b1->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInGainedState) { + ASSERT_TRUE(IsBufferReleased(b1->buffer_state())); + ASSERT_EQ(b1->Gain(), 0); + ASSERT_TRUE(AnyClientGained(b1->buffer_state())); + + EXPECT_EQ(b1->Release(), 0); +} + +TEST_F(BufferHubBufferStateTransitionTest, ReleaseBuffer_fromSelfInPostedState) { + ASSERT_EQ(b1->Gain(), 0); + ASSERT_EQ(b1->Post(), 0); + ASSERT_TRUE(AnyClientPosted(b1->buffer_state())); + + 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(AnyClientAcquired(b1->buffer_state())); + + EXPECT_EQ(b2->Release(), 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 0000000000..92fb33ff48 --- /dev/null +++ b/libs/ui/tests/BufferHubEventFd_test.cpp @@ -0,0 +1,338 @@ +/* + * 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); + +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)); + 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); + + 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); +} + +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. + base::unique_fd 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); + + eventfd_write(dupedEventFd.get(), 1); + 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); + + eventfd_write(dupedEventFd.get(), 1); + + eventfd_t value; + eventfd_read(dupedEventFd.get(), &value); + 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 0000000000..11f8e57adc --- /dev/null +++ b/libs/ui/tests/BufferHubMetadata_test.cpp @@ -0,0 +1,97 @@ +/* + * 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> + +using android::BufferHubDefs::IsBufferReleased; + +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.metadata_header(), nullptr); +} + +TEST_F(BufferHubMetadataTest, Import_Success) { + BufferHubMetadata m1 = BufferHubMetadata::Create(kEmptyUserMetadataSize); + EXPECT_TRUE(m1.IsValid()); + EXPECT_NE(m1.metadata_header(), nullptr); + + unique_fd h2 = unique_fd(dup(m1.ashmem_fd().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.metadata_header(); + EXPECT_NE(mh1, nullptr); + + EXPECT_TRUE(IsBufferReleased(mh1->buffer_state.load())); + + EXPECT_TRUE(m2.IsValid()); + BufferHubDefs::MetadataHeader* mh2 = m2.metadata_header(); + EXPECT_NE(mh2, nullptr); + + EXPECT_TRUE(IsBufferReleased(mh2->buffer_state.load())); +} + +TEST_F(BufferHubMetadataTest, MoveMetadataInvalidatesOldOne) { + BufferHubMetadata m1 = BufferHubMetadata::Create(sizeof(int)); + EXPECT_TRUE(m1.IsValid()); + EXPECT_NE(m1.metadata_header(), nullptr); + EXPECT_NE(m1.ashmem_fd().get(), -1); + EXPECT_EQ(m1.user_metadata_size(), 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.metadata_header(), nullptr); + EXPECT_NE(m2.metadata_header(), nullptr); + + EXPECT_EQ(m1.ashmem_fd().get(), -1); + EXPECT_NE(m2.ashmem_fd().get(), -1); + + EXPECT_EQ(m1.user_metadata_size(), 0U); + EXPECT_EQ(m2.user_metadata_size(), 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.metadata_header(), nullptr); + EXPECT_NE(m3.metadata_header(), nullptr); + + EXPECT_EQ(m2.ashmem_fd().get(), -1); + EXPECT_NE(m3.ashmem_fd().get(), -1); + + EXPECT_EQ(m2.user_metadata_size(), 0U); + EXPECT_EQ(m3.user_metadata_size(), sizeof(int)); +} + +} // namespace dvr +} // namespace android diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp index eb679ac236..81ab3acfe4 100644 --- a/libs/ui/tests/GraphicBuffer_test.cpp +++ b/libs/ui/tests/GraphicBuffer_test.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "GraphicBufferTest" -#include <ui/DetachedBufferHandle.h> +#include <ui/BufferHubBuffer.h> #include <ui/GraphicBuffer.h> #include <gtest/gtest.h> @@ -35,30 +35,43 @@ constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; class GraphicBufferTest : public testing::Test {}; -TEST_F(GraphicBufferTest, DetachedBuffer) { - sp<GraphicBuffer> buffer( - new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage)); +TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) { + std::unique_ptr<BufferHubBuffer> b1 = + BufferHubBuffer::Create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, + kTestUsage, /*userMetadataSize=*/0); + EXPECT_NE(b1, nullptr); + EXPECT_TRUE(b1->IsValid()); + + sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1))); + EXPECT_TRUE(gb->isBufferHubBuffer()); - // 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()); + 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); +} - pdx::LocalChannelHandle channel{nullptr, 1234}; - EXPECT_TRUE(channel.valid()); +TEST_F(GraphicBufferTest, InvalidBufferIdForNoneBufferHubBuffer) { + sp<GraphicBuffer> gb( + new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage)); + EXPECT_FALSE(gb->isBufferHubBuffer()); + EXPECT_EQ(gb->getBufferId(), -1); +} - std::unique_ptr<DetachedBufferHandle> handle = DetachedBufferHandle::Create(std::move(channel)); - EXPECT_FALSE(channel.valid()); - EXPECT_TRUE(handle->isValid()); - EXPECT_TRUE(handle->handle().valid()); +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()); - buffer->setDetachedBufferHandle(std::move(handle)); - EXPECT_TRUE(handle == nullptr); - EXPECT_TRUE(buffer->isDetachedBuffer()); + int b1_id = b1->id(); + EXPECT_GE(b1_id, 0); - handle = buffer->takeDetachedBufferHandle(); - EXPECT_TRUE(handle != nullptr); - EXPECT_TRUE(handle->isValid()); - EXPECT_FALSE(buffer->isDetachedBuffer()); + sp<GraphicBuffer> gb(new GraphicBuffer(std::move(b1))); + EXPECT_TRUE(gb->isBufferHubBuffer()); + EXPECT_EQ(gb->getBufferId(), b1_id); } } // namespace android diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index 69b6422d1d..fa928308cf 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. +cc_library_headers { + name: "libbufferhub_headers", + export_include_dirs: ["include"], + vendor_available: true, // TODO(b/112338314): Does shouldn't be available to vendor. +} + sourceFiles = [ - "buffer_hub_client.cpp", + "buffer_hub_base.cpp", "buffer_hub_rpc.cpp", - "detached_buffer.cpp", + "consumer_buffer.cpp", "ion_buffer.cpp", -] - -localIncludeFiles = [ - "include", + "producer_buffer.cpp", ] sharedLibraries = [ "libbase", - "libbinder", "libcutils", "libhardware", "liblog", @@ -36,6 +38,7 @@ sharedLibraries = [ ] headerLibraries = [ + "libbufferhub_headers", "libdvr_headers", "libnativebase_headers", ] @@ -49,13 +52,16 @@ cc_library { "-Wall", "-Werror", ], - export_include_dirs: localIncludeFiles, shared_libs: sharedLibraries, header_libs: headerLibraries, name: "libbufferhub", export_header_lib_headers: [ + "libbufferhub_headers", "libnativebase_headers", ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } cc_test { @@ -64,5 +70,7 @@ cc_test { shared_libs: sharedLibraries, header_libs: headerLibraries, name: "buffer_hub-test", -} + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} diff --git a/libs/vr/libbufferhub/buffer_hub-test.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp index e24739845d..1359f4c137 100644 --- a/libs/vr/libbufferhub/buffer_hub-test.cpp +++ b/libs/vr/libbufferhub/buffer_hub-test.cpp @@ -1,11 +1,12 @@ #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/BufferHubBuffer.h> +#include <ui/BufferHubDefs.h> #include <mutex> #include <thread> @@ -19,18 +20,20 @@ return result; \ })() +using android::BufferHubBuffer; 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::BufferHubDefs::AnyClientAcquired; +using android::BufferHubDefs::AnyClientGained; +using android::BufferHubDefs::AnyClientPosted; +using android::BufferHubDefs::IsBufferReleased; +using android::BufferHubDefs::IsClientAcquired; +using android::BufferHubDefs::IsClientPosted; +using android::BufferHubDefs::IsClientReleased; +using android::BufferHubDefs::kFirstClientBitMask; +using android::BufferHubDefs::kMetadataHeaderSize; +using android::dvr::ConsumerBuffer; +using android::dvr::ProducerBuffer; using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Status; @@ -41,80 +44,70 @@ 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; 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(c1->Poll(kPollTimeoutMs))); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - 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(c1->Poll(kPollTimeoutMs))); EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - 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(0, c1->Acquire(&fence)); + EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); EXPECT_EQ(1, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, c2->Acquire(&fence, &context)); - EXPECT_EQ(kContext, context); + EXPECT_EQ(0, c2->Acquire(&fence)); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); + EXPECT_EQ(0, RETRY_EINTR(c1->Poll(kPollTimeoutMs))); - EXPECT_EQ(0, c->Release(LocalHandle())); + EXPECT_EQ(0, c1->Release(LocalHandle())); EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); EXPECT_EQ(0, c2->Discard()); - 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(c1->Poll(kPollTimeoutMs))); EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); } 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 +139,9 @@ TEST_F(LibBufferHubTest, TestEpoll) { // 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 +171,21 @@ TEST_F(LibBufferHubTest, TestEpoll) { } 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,97 +193,84 @@ TEST_F(LibBufferHubTest, TestStateMask) { // 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))); - // 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(AnyClientPosted(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()); @@ -298,7 +279,7 @@ TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { 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(AnyClientAcquired(p->buffer_state())); // Acquire, post, and gain in acquired state should fail. EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); @@ -313,8 +294,7 @@ TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { EXPECT_EQ(p->buffer_state(), c->buffer_state()); EXPECT_TRUE(IsBufferReleased(p->buffer_state())); - // 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,64 +303,101 @@ TEST_F(LibBufferHubTest, TestAsyncStateTransitions) { 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(AnyClientGained(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); - DvrNativeBufferMetadata metadata; - LocalHandle invalid_fence; + ASSERT_EQ(0, p->GainAsync()); + ASSERT_EQ(0, p->GainAsync()); +} - // 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) { + 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(AnyClientPosted(p->buffer_state())); - // The buffer should stay in posted stay until a consumer picks it up. - EXPECT_GE(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); + // 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)); +} - // A new consumer should still be able to acquire the buffer immediately. - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); +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); - EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferAcquired(c->buffer_state())); + ASSERT_EQ(0, p->GainAsync()); + ASSERT_EQ(0, p->Post(LocalHandle())); + ASSERT_TRUE(AnyClientPosted(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)); +} + +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. The overall state of + // the buffer is also released because there is no consumer of this buffer. + ASSERT_TRUE(IsBufferReleased(p->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(IsBufferReleased(cs[i]->buffer_state())); + 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())); + IsClientPosted(cs[i]->buffer_state(), cs[i]->client_state_mask())); EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(kPollTimeoutMs))); 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 @@ -400,58 +417,72 @@ TEST_F(LibBufferHubTest, TestMaxConsumers) { } 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(AnyClientGained(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(AnyClientGained(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_TRUE(AnyClientPosted(p->buffer_state())); EXPECT_LT(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferAcquired(c->buffer_state())); + EXPECT_TRUE(AnyClientAcquired(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(AnyClientGained(p->buffer_state())); DvrNativeBufferMetadata metadata; LocalHandle invalid_fence; // Post the gained buffer before any consumer gets created. + // The buffer should be in released state because it is not expected to be + // read by any clients. EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); - EXPECT_TRUE(IsBufferPosted(p->buffer_state())); + EXPECT_TRUE(IsBufferReleased(p->buffer_state())); + EXPECT_EQ(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); - // Newly created consumer should be automatically sigalled. - std::unique_ptr<BufferConsumer> c = - BufferConsumer::Import(p->CreateConsumer()); + // Newly created consumer will not be signalled for the posted buffer before + // its creation. It cannot acquire the buffer immediately. + std::unique_ptr<ConsumerBuffer> c = + ConsumerBuffer::Import(p->CreateConsumer()); ASSERT_TRUE(c.get() != nullptr); - EXPECT_TRUE(IsBufferPosted(c->buffer_state())); + EXPECT_FALSE(IsClientPosted(c->buffer_state(), c->client_state_mask())); + EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence)); + + // Producer should be able to gain back and post the buffer + EXPECT_EQ(0, p->GainAsync()); + EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence)); + + // Consumer should be able to pick up the buffer this time. EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferAcquired(c->buffer_state())); + EXPECT_TRUE(IsClientAcquired(c->buffer_state(), c->client_state_mask())); } 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; @@ -470,13 +501,13 @@ TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) { // 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_EQ(0, p->GainAsync(&metadata, &invalid_fence)); - EXPECT_TRUE(IsBufferGained(p->buffer_state())); + EXPECT_TRUE(AnyClientGained(p->buffer_state())); } TEST_F(LibBufferHubTest, TestWithCustomMetadata) { @@ -484,23 +515,21 @@ TEST_F(LibBufferHubTest, TestWithCustomMetadata) { 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_EQ(0, p->Post(LocalHandle(), &m, sizeof(Metadata))); EXPECT_LE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); - 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))); } @@ -515,23 +544,23 @@ TEST_F(LibBufferHubTest, TestPostWithWrongMetaSize) { 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_NE(0, p->Post(LocalHandle(), &evil_meta, sizeof(OverSizedMetadata))); EXPECT_GE(0, RETRY_EINTR(c->Poll(kPollTimeoutMs))); // 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 +573,16 @@ TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { 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 +590,56 @@ TEST_F(LibBufferHubTest, TestAcquireWithWrongMetaSize) { // 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 +652,13 @@ int PollFd(int fd, int timeout_ms) { } // 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)); @@ -680,59 +714,112 @@ TEST_F(LibBufferHubTest, TestAcquireFence) { } 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_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()); + // The buffer is now available for the producer to gain. + EXPECT_LT(0, RETRY_EINTR(p->Poll(kPollTimeoutMs))); + + // 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 uint64_t consumer_state_bit2 = c2->buffer_state_bit(); - EXPECT_NE(consumer_state_bit1, consumer_state_bit2); + const uint32_t client_state_mask2 = c2->client_state_mask(); + EXPECT_NE(client_state_mask1, client_state_mask2); + EXPECT_EQ(0, RETRY_EINTR(c2->Poll(kPollTimeoutMs))); + 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(c1->Poll(kPollTimeoutMs))); - // The new consumer is available for acquire. + // 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(c2->Poll(kPollTimeoutMs))); - EXPECT_LE(0, c2->AcquireAsync(&meta, &fence)); - // Releasing the consumer makes the buffer gainable. - EXPECT_EQ(0, c2->ReleaseAsync(&meta, LocalHandle())); + 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 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(c3->Poll(kPollTimeoutMs))); + EXPECT_EQ(0, c3->AcquireAsync(&meta, &invalid_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(p->Poll(kPollTimeoutMs))); - // But if another consumer is created in released state. - std::unique_ptr<BufferConsumer> c3 = - BufferConsumer::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. + // 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(c3->Poll(kPollTimeoutMs))); - EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &fence)); + EXPECT_EQ(-EBUSY, c3->AcquireAsync(&meta, &invalid_fence)); - // Producer should be able to gain no matter what. - EXPECT_EQ(0, p->GainAsync(&meta, &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,6 +828,7 @@ TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) { 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); auto s1 = p->Detach(); @@ -789,159 +877,114 @@ TEST_F(LibBufferHubTest, TestDetachBufferFromProducer) { // 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); } -TEST_F(LibBufferHubTest, TestCreateDetachedBufferFails) { +TEST_F(LibBufferHubTest, TestCreateBufferHubBufferFails) { // 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); + auto b1 = BufferHubBuffer::Create(kWidth, /*height=2*/ 2, kLayerCount, + /*format=*/HAL_PIXEL_FORMAT_BLOB, kUsage, + kUserMetadataSize); 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( + auto b2 = BufferHubBuffer::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( + auto b3 = BufferHubBuffer::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(); - +TEST_F(LibBufferHubTest, TestCreateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, + kUsage, kUserMetadataSize); 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()); +TEST_F(LibBufferHubTest, TestDetach) { + // TODO(b/112338294) rewrite test after migration + return; - 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); +} + +TEST_F(LibBufferHubTest, TestDuplicateBufferHubBuffer) { + auto b1 = BufferHubBuffer::Create(kWidth, kHeight, kLayerCount, kFormat, + kUsage, kUserMetadataSize); + int b1_id = b1->id(); + EXPECT_TRUE(b1->IsValid()); + EXPECT_EQ(b1->user_metadata_size(), kUserMetadataSize); + EXPECT_NE(b1->client_state_mask(), 0U); - // Promote the detached buffer. - status_or_handle = b1->Promote(); - // The buffer client is still considered as connected but invalid. + auto status_or_handle = b1->Duplicate(); + EXPECT_TRUE(status_or_handle); + + // The detached buffer should still be valid. EXPECT_TRUE(b1->IsConnected()); - EXPECT_FALSE(b1->IsValid()); - EXPECT_TRUE(status_or_handle.ok()); + EXPECT_TRUE(b1->IsValid()); - // Gets the channel handle for the producer. + // Gets the channel handle for the duplicated buffer. LocalChannelHandle h2 = status_or_handle.take(); EXPECT_TRUE(h2.valid()); - std::unique_ptr<BufferProducer> p2 = BufferProducer::Import(std::move(h2)); + std::unique_ptr<BufferHubBuffer> b2 = BufferHubBuffer::Import(std::move(h2)); EXPECT_FALSE(h2.valid()); - ASSERT_TRUE(p2 != nullptr); - int p2_id = p2->id(); + ASSERT_TRUE(b2 != nullptr); + EXPECT_TRUE(b2->IsValid()); + EXPECT_EQ(b2->user_metadata_size(), kUserMetadataSize); + EXPECT_NE(b2->client_state_mask(), 0U); + + int b2_id = b2->id(); + + // 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 client_state_mask() to tell those two instances apart. + EXPECT_NE(b1->client_state_mask(), b2->client_state_mask()); + + // Both buffer instances should be in gained state. + EXPECT_TRUE(IsBufferReleased(b1->buffer_state())); + EXPECT_TRUE(IsBufferReleased(b2->buffer_state())); - // A newly promoted ProducerBuffer should inherit the same buffer id. - EXPECT_EQ(b1_id, p2_id); - EXPECT_TRUE(IsBufferGained(p2->buffer_state())); + // TODO(b/112338294) rewrite test after migration + return; } diff --git a/libs/vr/libbufferhub/buffer_hub_base.cpp b/libs/vr/libbufferhub/buffer_hub_base.cpp new file mode 100644 index 0000000000..8497f3edc1 --- /dev/null +++ b/libs/vr/libbufferhub/buffer_hub_base.cpp @@ -0,0 +1,229 @@ +#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_->buffer_state; + ALOGD_IF(TRACE, + "BufferHubBase::ImportBuffer: id=%d, buffer_state=%" PRIx32 ".", + id(), buffer_state_->load(std::memory_order_acquire)); + fence_state_ = &metadata_header_->fence_state; + ALOGD_IF(TRACE, + "BufferHubBase::ImportBuffer: id=%d, fence_state=%" PRIx32 ".", id(), + fence_state_->load(std::memory_order_acquire)); + active_clients_bit_mask_ = &metadata_header_->active_clients_bit_mask; + 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::Poll(int timeout_ms) { + ATRACE_NAME("BufferHubBase::Poll"); + pollfd p = {event_fd(), POLLIN, 0}; + return poll(&p, 1, timeout_ms); +} + +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; +} + +void BufferHubBase::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); +} + +} // 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 159f2bd1b3..0000000000 --- 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 0000000000..b6ca64eef2 --- /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 6fae16d265..0000000000 --- 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 0000000000..440a59dc71 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_base.h @@ -0,0 +1,170 @@ +#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(); + + // 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 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])); + } + + // 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_; } + + // 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); + }; + + // 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_->queue_index; } + void SetQueueIndex(uint64_t index) { metadata_header_->queue_index = 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); + + // 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 0ea77c85fb..0000000000 --- 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 0000000000..f2c40fe3d3 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_defs.h @@ -0,0 +1,201 @@ +#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 AnyClientGained(uint32_t state) { + return android::BufferHubDefs::AnyClientGained(state); +} + +static inline bool IsClientGained(uint32_t state, uint32_t client_bit_mask) { + return android::BufferHubDefs::IsClientGained(state, client_bit_mask); +} + +static inline bool AnyClientPosted(uint32_t state) { + return android::BufferHubDefs::AnyClientPosted(state); +} + +static inline bool IsClientPosted(uint32_t state, uint32_t client_bit_mask) { + return android::BufferHubDefs::IsClientPosted(state, client_bit_mask); +} + +static inline bool AnyClientAcquired(uint32_t state) { + return android::BufferHubDefs::AnyClientAcquired(state); +} + +static inline bool IsClientAcquired(uint32_t state, uint32_t client_bit_mask) { + return android::BufferHubDefs::IsClientAcquired(state, client_bit_mask); +} + +static inline bool IsBufferReleased(uint32_t state) { + return android::BufferHubDefs::IsBufferReleased(state); +} + +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; +}; + +struct DetachedBufferRPC { + private: + enum { + kOpDetachedBufferBase = 1000, + + // Allocates a standalone DetachedBuffer not associated with any producer + // consumer set. + kOpCreate, + + // Imports the given channel handle to a DetachedBuffer, taking ownership. + kOpImport, + + // Creates a DetachedBuffer client from an existing one. The new client will + // share the same underlying gralloc buffer and ashmem region for metadata. + kOpDuplicate, + }; + + // Aliases. + using LocalChannelHandle = pdx::LocalChannelHandle; + using LocalHandle = pdx::LocalHandle; + using Void = pdx::rpc::Void; + + 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, BufferTraits<LocalHandle>(Void)); + PDX_REMOTE_METHOD(Duplicate, kOpDuplicate, LocalChannelHandle(Void)); + + PDX_REMOTE_API(API, Create, Import, Duplicate); +}; + +} // 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 04f4fb4c21..f1cd0b4adc 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 @@ class BufferDescription { 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 @@ class BufferDescription { 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 @@ class BufferDescription { 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 @@ class BufferDescription { 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 @@ struct BufferHubRPC { 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 @@ struct BufferHubRPC { 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 @@ struct BufferHubRPC { 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 0000000000..7aa50b1985 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/consumer_buffer.h @@ -0,0 +1,83 @@ +#ifndef ANDROID_DVR_CONSUMER_BUFFER_H_ +#define ANDROID_DVR_CONSUMER_BUFFER_H_ + +#include <private/dvr/buffer_hub_base.h> + +namespace android { +namespace dvr { + +// BufferConsumer was originally poorly named and gets easily confused with +// IGraphicBufferConsumer. Actually, BufferConsumer is a single buffer that can +// consume (i.e. read) data from a buffer, but it doesn't consume buffer. On +// the other hand, IGraphicBufferConsumer is the consumer end of a BufferQueue +// and it is used to consume buffers. +// +// TODO(b/116855254): Remove this typedef once rename is complete in other +// projects and/or branches. +typedef class ConsumerBuffer BufferConsumer; + +// 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 6d0b502271..0000000000 --- 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 860f08ae7a..ed38e7f448 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 @@ class IonBuffer { 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 0000000000..a5c6ca238a --- /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 0000000000..2761416034 --- /dev/null +++ b/libs/vr/libbufferhub/include/private/dvr/producer_buffer.h @@ -0,0 +1,113 @@ +#ifndef ANDROID_DVR_PRODUCER_BUFFER_H_ +#define ANDROID_DVR_PRODUCER_BUFFER_H_ + +#include <private/dvr/buffer_hub_base.h> + +namespace android { +namespace dvr { + +// BufferProducer was originally poorly named and gets easily confused with +// IGraphicBufferProducer. Actually, BufferProducer is a single buffer that can +// produce (i.e. write) data into a buffer, but it doesn't produce buffer. On +// the other hand, IGraphicBufferProducer is the producer end of a BufferQueue +// and it is used to produce buffers. +// +// TODO(b/116855254): Remove this typedef once rename is complete in other +// projects and/or branches. +typedef class ProducerBuffer BufferProducer; + +// 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 (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 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 129553128e..196541010e 100644 --- a/libs/vr/libbufferhub/ion_buffer.cpp +++ b/libs/vr/libbufferhub/ion_buffer.cpp @@ -205,7 +205,7 @@ int IonBuffer::Lock(uint32_t usage, int x, int y, int width, int height, 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 @@ int IonBuffer::LockYUV(uint32_t usage, int x, int y, int width, int height, 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 @@ int IonBuffer::Unlock() { 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 0000000000..5274bf25d5 --- /dev/null +++ b/libs/vr/libbufferhub/producer_buffer.cpp @@ -0,0 +1,311 @@ +#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, other clients' buffer + // state to posted. + uint32_t current_active_clients_bit_mask = + active_clients_bit_mask_->load(std::memory_order_acquire); + uint32_t updated_buffer_state = current_active_clients_bit_mask & + (~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::AnyClientAcquired(current_buffer_state) || + BufferHubDefs::AnyClientGained(current_buffer_state) || + (BufferHubDefs::AnyClientPosted(current_buffer_state) && + !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::AnyClientAcquired(current_buffer_state) || + BufferHubDefs::AnyClientGained(current_buffer_state) || + (BufferHubDefs::AnyClientPosted(current_buffer_state) && + !gain_posted_buffer)) { + ALOGE( + "%s: Failed to gain the buffer. The buffer is no longer released. " + "id=%d state=%" PRIx32 ".", + __FUNCTION__, id(), current_buffer_state); + return -EBUSY; + } + } + + // Canonical metadata is undefined on Gain. Except for user_metadata and + // release_fence_mask. Fill in the user_metadata_ptr in address space of the + // local process. + if (metadata_header_->metadata.user_metadata_size && user_metadata_ptr_) { + out_meta->user_metadata_size = + metadata_header_->metadata.user_metadata_size; + out_meta->user_metadata_ptr = + reinterpret_cast<uint64_t>(user_metadata_ptr_); + } else { + out_meta->user_metadata_size = 0; + out_meta->user_metadata_ptr = 0; + } + + uint32_t current_fence_state = fence_state_->load(std::memory_order_acquire); + uint32_t current_active_clients_bit_mask = + active_clients_bit_mask_->load(std::memory_order_acquire); + // If there are release fence(s) from consumer(s), we need to return it to the + // consumer(s). + // TODO(b/112007999) add an atomic variable in metadata header in shared + // memory to indicate which client is the last producer of the buffer. + // Currently, assume the first client is the only producer to the buffer. + if (current_fence_state & current_active_clients_bit_mask & + (~BufferHubDefs::kFirstClientBitMask)) { + *out_fence = shared_release_fence_.Duplicate(); + out_meta->release_fence_mask = current_fence_state & + current_active_clients_bit_mask & + (~BufferHubDefs::kFirstClientBitMask); + } + + return 0; +} + +int ProducerBuffer::Gain(LocalHandle* release_fence, bool gain_posted_buffer) { + ATRACE_NAME("ProducerBuffer::Gain"); + + DvrNativeBufferMetadata meta; + if (const int error = LocalGain(&meta, release_fence, gain_posted_buffer)) + return error; + + auto status = InvokeRemoteMethod<BufferHubRPC::ProducerGain>(); + if (!status) + return -status.error(); + return 0; +} + +int ProducerBuffer::GainAsync(DvrNativeBufferMetadata* out_meta, + LocalHandle* release_fence, + bool gain_posted_buffer) { + ATRACE_NAME("ProducerBuffer::GainAsync"); + + if (const int error = LocalGain(out_meta, release_fence, gain_posted_buffer)) + return error; + + return ReturnStatusOrError(SendImpulse(BufferHubRPC::ProducerGain::Opcode)); +} + +int ProducerBuffer::GainAsync() { + DvrNativeBufferMetadata meta; + LocalHandle fence; + return GainAsync(&meta, &fence); +} + +std::unique_ptr<ProducerBuffer> ProducerBuffer::Import( + LocalChannelHandle channel) { + ALOGD_IF(TRACE, "ProducerBuffer::Import: channel=%d", channel.value()); + return ProducerBuffer::Create(std::move(channel)); +} + +std::unique_ptr<ProducerBuffer> ProducerBuffer::Import( + Status<LocalChannelHandle> status) { + return Import(status ? status.take() + : LocalChannelHandle{nullptr, -status.error()}); +} + +Status<LocalChannelHandle> ProducerBuffer::Detach() { + // TODO(b/112338294) remove after migrate producer buffer to binder + ALOGW("ProducerBuffer::Detach: not supported operation during migration"); + return {}; + + // TODO(b/112338294) Keep here for reference. Remove it after new logic is + // written. + /* uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsClientGained( + buffer_state, BufferHubDefs::kFirstClientStateMask)) { + // Can only detach a ProducerBuffer when it's in gained state. + ALOGW("ProducerBuffer::Detach: The buffer (id=%d, state=0x%" PRIx32 + ") is not in gained state.", + id(), buffer_state); + return {}; + } + + Status<LocalChannelHandle> status = + InvokeRemoteMethod<BufferHubRPC::ProducerBufferDetach>(); + ALOGE_IF(!status, + "ProducerBuffer::Detach: Failed to detach buffer (id=%d): %s.", id(), + status.GetErrorMessage().c_str()); + return status; */ +} + +} // namespace dvr +} // namespace android diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp index 9f72c05f0c..20894e3588 100644 --- a/libs/vr/libbufferhubqueue/Android.bp +++ b/libs/vr/libbufferhubqueue/Android.bp @@ -59,6 +59,9 @@ cc_library_shared { static_libs: staticLibraries, shared_libs: sharedLibraries, header_libs: headerLibraries, + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, } subdirs = ["benchmarks", "tests"] diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp index 5089b8754e..ef1eed6d0a 100644 --- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp +++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp @@ -5,7 +5,7 @@ cc_benchmark { "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 4ca8671f24..b6813eb51d 100644 --- a/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp +++ b/libs/vr/libbufferhubqueue/benchmarks/buffer_transport_benchmark.cpp @@ -66,7 +66,7 @@ class BufferTransportService : public BBinder { 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 @@ class BufferTransportService : public BBinder { /*waitForFence=*/false); } - if (ret != NO_ERROR) { + if (ret != OK) { LOG(ERROR) << "Failed to acquire next buffer."; return; } @@ -99,7 +99,7 @@ class BufferTransportService : public BBinder { ret = buffer_item_consumer_->releaseBuffer(buffer); } - if (ret != NO_ERROR) { + if (ret != OK) { LOG(ERROR) << "Failed to release buffer."; return; } @@ -171,14 +171,14 @@ class BinderBufferTransport : public BufferTransport { 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 BinderBufferTransport : public BufferTransport { 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 8feb1cd803..9c4f73f37a 100644 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp @@ -69,7 +69,7 @@ void BufferHubQueue::Initialize() { .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 @@ void BufferHubQueue::Initialize() { 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 @@ BufferHubQueue::CreateConsumerQueueParcelable(bool silent) { 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 @@ bool BufferHubQueue::WaitForBuffers(int timeout) { } 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 @@ Status<void> BufferHubQueue::HandleQueueEvent(int poll_event) { // 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 @@ Status<void> BufferHubQueue::AddBuffer( 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::AddBuffer( } 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::RemoveBuffer(size_t slot) { 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 @@ Status<void> BufferHubQueue::Enqueue(Entry entry) { 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 @@ Status<std::shared_ptr<BufferHubBuffer>> BufferHubQueue::Dequeue(int timeout, 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 @@ ProducerQueue::ProducerQueue(const ProducerQueueConfig& config, 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 " @@ -481,10 +488,13 @@ Status<std::vector<size_t>> ProducerQueue::AllocateBuffers( } } - 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,11 +513,6 @@ Status<size_t> ProducerQueue::AllocateBuffer(uint32_t width, uint32_t height, return status.error_status(); } - if (status.get().size() == 0) { - ALOGE_IF(TRACE, "ProducerQueue::AllocateBuffer: no buffer allocated."); - ErrorStatus(ENOMEM); - } - return {status.get()[0]}; } @@ -524,11 +529,46 @@ Status<void> ProducerQueue::AddBuffer( return BufferHubQueue::Enqueue({buffer, slot, 0ULL}); } +Status<size_t> ProducerQueue::InsertBuffer( + const std::shared_ptr<BufferProducer>& 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 BufferProducer. + 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(); } @@ -544,31 +584,81 @@ Status<std::shared_ptr<BufferProducer>> ProducerQueue::Dequeue( pdx::Status<std::shared_ptr<BufferProducer>> 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<BufferProducer> buffer; + Status<std::shared_ptr<BufferHubBase>> dequeue_status = + BufferHubQueue::Dequeue(timeout, slot); + if (dequeue_status.ok()) { + buffer = std::static_pointer_cast<BufferProducer>(dequeue_status.take()); + } else { + if (gain_posted_buffer) { + Status<std::shared_ptr<BufferProducer>> 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<BufferProducer>> 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<BufferProducer> 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::AnyClientAcquired(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 @@ ConsumerQueue::ConsumerQueue(LocalChannelHandle handle) : 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 @@ Status<size_t> ConsumerQueue::ImportBuffers() { 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,13 +715,13 @@ Status<size_t> ConsumerQueue::ImportBuffers() { 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", + ALOGE("%s: Failed to import buffer: slot=%zu", __FUNCTION__, buffer_handle_slot.second); last_error = ErrorStatus(EPIPE); continue; @@ -644,7 +730,7 @@ Status<size_t> ConsumerQueue::ImportBuffers() { auto add_status = AddBuffer(std::move(buffer_consumer), 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 { @@ -660,8 +746,8 @@ Status<size_t> ConsumerQueue::ImportBuffers() { 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); + ALOGD_IF(TRACE, "%s: queue_id=%d buffer_id=%d slot=%zu", __FUNCTION__, id(), + buffer->id(), slot); return BufferHubQueue::AddBuffer(buffer, slot); } @@ -670,9 +756,9 @@ Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( 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,7 +773,7 @@ Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( 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__); } } @@ -699,7 +785,7 @@ Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( 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); } @@ -716,19 +802,18 @@ Status<std::shared_ptr<BufferConsumer>> ConsumerQueue::Dequeue( } 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 2cd7c452be..f705749243 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 BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const { } 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 @@ status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) { } 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 60e1c4b8a9..53ab2b2e7a 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 @@ namespace dvr { 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 @@ class BufferHubQueue : public pdx::Client { 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 @@ class BufferHubQueue : public pdx::Client { : -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 @@ class BufferHubQueue : public pdx::Client { // 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 @@ class BufferHubQueue : public pdx::Client { // 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 @@ class BufferHubQueue : public pdx::Client { // 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 @@ class BufferHubQueue : public pdx::Client { 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 @@ class BufferHubQueue : public pdx::Client { // 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 @@ class BufferHubQueue : public pdx::Client { // 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}; @@ -331,6 +336,13 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { pdx::Status<void> AddBuffer(const std::shared_ptr<BufferProducer>& 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<BufferProducer>& buffer); + // Remove producer buffer from the queue. pdx::Status<void> RemoveBuffer(size_t slot) override; @@ -342,11 +354,30 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { // 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. + // @return a buffer in gained state, which was originally in released state. pdx::Status<std::shared_ptr<BufferProducer>> Dequeue( int timeout, size_t* slot, pdx::LocalHandle* release_fence); + + // 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 BufferProducer. + // @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<BufferProducer>> 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, @@ -367,6 +398,16 @@ class ProducerQueue : public pdx::ClientBase<ProducerQueue, BufferHubQueue> { // 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<BufferProducer>> DequeueUnacquiredBuffer( + size_t* slot); }; class ConsumerQueue : public BufferHubQueue { diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp index 47a27344bd..159d6dc4b9 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 @@ TEST_F(BufferHubQueueTest, TestDequeue) { // 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, TestDequeue) { } } +TEST_F(BufferHubQueueTest, + TestDequeuePostedBufferIfNoAvailableReleasedBuffer_withBufferConsumer) { + 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_noBufferConsumer) { + 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, TestProducerConsumer) { } } +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<BufferProducer> p1 = BufferProducer::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<BufferProducer> p2 = BufferProducer::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; @@ -211,8 +390,8 @@ TEST_F(BufferHubQueueTest, TestRemoveBuffer) { 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 @@ TEST_F(BufferHubQueueTest, TestUserMetadata) { 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 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { 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 @@ TEST_F(BufferHubQueueTest, TestAllocateBuffer) { 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( @@ -705,7 +908,7 @@ TEST_F(BufferHubQueueTest, TestFreeAllBuffers) { 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 @@ TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { 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 @@ TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { 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); @@ -799,7 +1002,7 @@ TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) { // 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 @@ TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) { 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()); @@ -866,7 +1069,7 @@ TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) { // 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 4f10f83211..8cc7081e4f 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 @@ class BufferHubQueueProducerTest : public ::testing::Test { 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, ConnectAgain_ReturnsError) { 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 @@ TEST_F(BufferHubQueueProducerTest, Query_Succeeds) { 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 @@ TEST_F(BufferHubQueueProducerTest, Queue_Succeeds) { // 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 @@ TEST_F(BufferHubQueueProducerTest, QueueNoFence_ReturnsError) { 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 @@ TEST_F(BufferHubQueueProducerTest, QueueTestInvalidScalingMode_ReturnsError) { 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 @@ TEST_F(BufferHubQueueProducerTest, QueueCropOutOfBounds_ReturnsError) { 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 @@ TEST_F(BufferHubQueueProducerTest, CancelBuffer_Succeeds) { 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 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { 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 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { 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 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Succeeds) { } // 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 @@ TEST_F(BufferHubQueueProducerTest, SetMaxDequeuedBufferCount_Fails) { << "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 @@ TEST_F(BufferHubQueueProducerTest, 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 @@ TEST_F(BufferHubQueueProducerTest, 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 @@ TEST_F(BufferHubQueueProducerTest, 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 @@ TEST_F(BufferHubQueueProducerTest, ConnectDisconnectReconnect) { 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 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { 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 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { 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 9c678815cd..8c354fbc18 100644 --- a/libs/vr/libdisplay/Android.bp +++ b/libs/vr/libdisplay/Android.bp @@ -16,8 +16,8 @@ sourceFiles = [ "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 974c231375..fdeeb70dfb 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 caf3182e91..f8f5b3ddb3 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 1eeb80e09d..0000000000 --- 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 0000000000..152464abd1 --- /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 bc6cf6cabe..0000000000 --- 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 0000000000..4668b9836c --- /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 16906f57cd..81a9b2d26a 100644 --- a/libs/vr/libdvr/Android.bp +++ b/libs/vr/libdvr/Android.bp @@ -19,7 +19,14 @@ cc_library_headers { 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 @@ srcs = [ "dvr_performance.cpp", "dvr_pose.cpp", "dvr_surface.cpp", - "dvr_vsync.cpp", + "dvr_tracking.cpp", ] static_libs = [ @@ -66,7 +73,7 @@ shared_libs = [ ] cc_library_shared { - name: "libdvr", + name: "libdvr.google", owner: "google", cflags: cflags, header_libs: ["libdvr_headers"], @@ -81,7 +88,7 @@ cc_library_shared { // 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 d14f040f12..e099f6a699 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 baf1f2f5da..c11706fc7f 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 571558a5bd..f4c6600469 100644 --- a/libs/vr/libdvr/dvr_buffer_queue.cpp +++ b/libs/vr/libdvr/dvr_buffer_queue.cpp @@ -9,7 +9,7 @@ using namespace android; using android::dvr::BufferConsumer; -using android::dvr::BufferHubBuffer; +using android::dvr::BufferHubBase; using android::dvr::BufferProducer; using android::dvr::ConsumerQueue; using android::dvr::ProducerQueue; @@ -439,7 +439,7 @@ void DvrReadBufferQueue::SetBufferRemovedCallback( 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 = diff --git a/libs/vr/libdvr/dvr_display_manager.cpp b/libs/vr/libdvr/dvr_display_manager.cpp index 852f9a4726..fe91b14679 100644 --- a/libs/vr/libdvr/dvr_display_manager.cpp +++ b/libs/vr/libdvr/dvr_display_manager.cpp @@ -2,8 +2,8 @@ #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> diff --git a/libs/vr/libdvr/dvr_internal.h b/libs/vr/libdvr/dvr_internal.h index de8bb96aec..df8125a414 100644 --- a/libs/vr/libdvr/dvr_internal.h +++ b/libs/vr/libdvr/dvr_internal.h @@ -16,8 +16,11 @@ typedef struct DvrWriteBuffer DvrWriteBuffer; namespace android { namespace dvr { -class BufferProducer; -class BufferConsumer; +// TODO(b/116855254): Remove this typedef once rename is complete in libdvr. +// Note that the dvr::BufferProducer and dvr::BufferConsumer were poorly named, +// they should really be named as ProducerBuffer and ConsumerBuffer. +typedef class ProducerBuffer BufferProducer; +typedef class ConsumerBuffer BufferConsumer; class IonBuffer; DvrBuffer* CreateDvrBufferFromIonBuffer( diff --git a/libs/vr/libdvr/dvr_tracking.cpp b/libs/vr/libdvr/dvr_tracking.cpp new file mode 100644 index 0000000000..73addc9a0c --- /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 099240e53a..0000000000 --- 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 80ffc82920..e383bb2cb3 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 int32_t DvrGlobalBufferKey; 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 DvrHwcRecti (*DvrHwcFrameGetLayerDamagedRegionPtr)(DvrHwcFrame* frame, 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 @@ struct ALIGNED_DVR_STRUCT(8) DvrNativeBufferMetadata { // 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 f0d8ec6d24..3006b61b81 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(ReadBufferQueueSetBufferRemovedCallback); 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(ReadBufferQueueReleaseBuffer); 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 943384f802..fe59d1ffba 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 DvrSurfaceState DvrSurfaceState; 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 dvrSurfaceStateDestroy(DvrSurfaceState* surface_state); void dvrSurfaceDestroy(DvrSurface* surface); void dvrHwcClientDestroy(DvrHwcClient* client); void dvrHwcFrameDestroy(DvrHwcFrame* frame); -void dvrVSyncClientDestroy(DvrVSyncClient* client); __END_DECLS @@ -55,7 +53,6 @@ struct DvrObjectDeleter { 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 UniqueDvrSurfaceState = MakeUniqueDvrPointer<DvrSurfaceState>; 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 0000000000..5e388f391a --- /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 0000000000..81310d2303 --- /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 87fdf31b2b..498bb5cc6e 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 @@ typedef struct __attribute__((packed, aligned(16))) DvrVsync { 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 1ae75fbe04..357dffe193 100644 --- a/libs/vr/libdvr/tests/Android.bp +++ b/libs/vr/libdvr/tests/Android.bp @@ -12,38 +12,36 @@ // See the License for the specific language governing permissions and // limitations under the License. -shared_libraries = [ - "libbase", - "libbinder", - "libbufferhubqueue", - "libcutils", - "libgui", - "liblog", - "libhardware", - "libui", - "libutils", - "libnativewindow", - "libpdx_default_transport", -] - -static_libraries = [ - "libdvr_static", - "libchrome", - "libdvrcommon", - "libdisplay", - "libbroadcastring", -] - cc_test { srcs: [ "dvr_display_manager-test.cpp", "dvr_named_buffer-test.cpp", + "dvr_tracking-test.cpp", ], header_libs: ["libdvr_headers"], - static_libs: static_libraries, - shared_libs: shared_libraries, + static_libs: [ + "libdvr_static.google", + "libchrome", + "libdvrcommon", + "libdisplay", + "libbroadcastring", + ], + shared_libs: [ + "libbase", + "libbinder", + "libbufferhubqueue", + "libcutils", + "libgui", + "liblog", + "libhardware", + "libui", + "libutils", + "libnativewindow", + "libpdx_default_transport", + ], cflags: [ + "-DDVR_TRACKING_IMPLEMENTED=0", "-DLOG_TAG=\"dvr_api-test\"", "-DTRACE=0", "-Wno-missing-field-initializers", @@ -51,4 +49,59 @@ cc_test { "-g", ], name: "dvr_api-test", + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} + +cc_test { + name: "dvr_buffer_queue-test", + + // Includes the dvr_api.h header. Tests should only include "dvr_api.h", + // and shall only get access to |dvrGetApi|, as other symbols are hidden + // from the library. + include_dirs: ["frameworks/native/libs/vr/libdvr/include"], + + srcs: ["dvr_buffer_queue-test.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + ], + + cflags: [ + "-DTRACE=0", + "-O2", + "-g", + ], + + // DTS Should only link to NDK libraries. + sdk_version: "26", + stl: "c++_static", +} + +cc_test { + name: "dvr_display-test", + + include_dirs: [ + "frameworks/native/libs/vr/libdvr/include", + "frameworks/native/libs/nativewindow/include", + ], + + srcs: ["dvr_display-test.cpp"], + + shared_libs: [ + "libandroid", + "liblog", + ], + + cflags: [ + "-DTRACE=0", + "-O2", + "-g", + ], + + // DTS Should only link to NDK libraries. + sdk_version: "26", + stl: "c++_static", } diff --git a/libs/vr/libdvr/tests/Android.mk b/libs/vr/libdvr/tests/Android.mk deleted file mode 100644 index 0f3840d52c..0000000000 --- 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 d8359e78a8..5d2ec285eb 100644 --- a/libs/vr/libdvr/tests/dvr_api_test.h +++ b/libs/vr/libdvr/tests/dvr_api_test.h @@ -14,7 +14,7 @@ class DvrApiTest : public ::testing::Test { // 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 2d5f0043e3..df060973ec 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 @@ class DvrBufferQueueTest : public DvrApiTest { 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 c9a5c09caa..ed725777aa 100644 --- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp +++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp @@ -1,5 +1,6 @@ #include <android-base/properties.h> #include <base/logging.h> +#include <cutils/properties.h> #include <gtest/gtest.h> #include <log/log.h> #include <poll.h> @@ -479,6 +480,11 @@ TEST_F(DvrDisplayManagerTest, ExpectInt) { #endif TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) { + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + // Get surface state and verify there are no surfaces. ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); @@ -518,6 +524,11 @@ TEST_F(DvrDisplayManagerTest, SurfaceCreateEvent) { } TEST_F(DvrDisplayManagerTest, SurfaceAttributeEvent) { + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + // Get surface state and verify there are no surfaces. ASSERT_STATUS_OK(manager_->UpdateSurfaceState()); ASSERT_STATUS_EQ(0u, manager_->GetSurfaceCount()); @@ -757,6 +768,11 @@ TEST_F(DvrDisplayManagerTest, SurfaceAttributeTypes) { } TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) { + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + // Create an application surface. auto surface_status = CreateApplicationSurface(); ASSERT_STATUS_OK(surface_status); @@ -825,6 +841,11 @@ TEST_F(DvrDisplayManagerTest, SurfaceQueueEvent) { } TEST_F(DvrDisplayManagerTest, MultiLayerBufferQueue) { + // This test doesn't apply to standalone vr devices. + if (property_get_bool("ro.boot.vr", false)) { + return; + } + // Create an application surface. auto surface_status = CreateApplicationSurface(); ASSERT_STATUS_OK(surface_status); diff --git a/libs/vr/libdvr/tests/dvr_tracking-test.cpp b/libs/vr/libdvr/tests/dvr_tracking-test.cpp new file mode 100644 index 0000000000..3b6d6e1ec4 --- /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 234b24afe4..d38b174184 100644 --- a/libs/vr/libpdx/private/pdx/service.h +++ b/libs/vr/libpdx/private/pdx/service.h @@ -59,9 +59,18 @@ class Channel : public std::enable_shared_from_this<Channel> { 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 68b8dd7ed7..3769162344 100644 --- a/libs/vr/libpdx/service.cpp +++ b/libs/vr/libpdx/service.cpp @@ -318,13 +318,7 @@ Status<void> Message::Reply(const RemoteHandle& handle) { 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 e7bce27045..515684696b 100644 --- a/libs/vr/libpdx_uds/channel_parcelable.cpp +++ b/libs/vr/libpdx_uds/channel_parcelable.cpp @@ -36,7 +36,7 @@ LocalChannelHandle ChannelParcelable::TakeChannelHandle() { } 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 @@ status_t ChannelParcelable::writeToParcel(Parcel* parcel) const { } 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 @@ status_t ChannelParcelable::writeToParcel(Parcel* parcel) const { } 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::writeToParcel(Parcel* parcel) const { 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 @@ status_t ChannelParcelable::readFromParcel(const Parcel* parcel) { } 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 32d40e8371..ecbfdba7c4 100644 --- a/libs/vr/libpdx_uds/service_endpoint.cpp +++ b/libs/vr/libpdx_uds/service_endpoint.cpp @@ -521,6 +521,9 @@ Status<void> Endpoint::ReceiveMessageForChannel( 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 233e0fceaa..4f8bdbf383 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -20,7 +20,6 @@ sourceFiles = [ "display_surface.cpp", "hardware_composer.cpp", "vr_flinger.cpp", - "vsync_service.cpp", ] includeFiles = [ "include" ] @@ -40,6 +39,7 @@ sharedLibraries = [ "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", @@ -64,6 +64,7 @@ sharedLibraries = [ 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", ] @@ -89,3 +90,7 @@ cc_library_static { header_libs: headerLibraries, name: "libvrflinger", } + +subdirs = [ + "tests", +] diff --git a/libs/vr/libvrflinger/acquired_buffer.h b/libs/vr/libvrflinger/acquired_buffer.h index 1a200aabd2..9e35a39bfd 100644 --- a/libs/vr/libvrflinger/acquired_buffer.h +++ b/libs/vr/libvrflinger/acquired_buffer.h @@ -2,7 +2,7 @@ #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> @@ -43,7 +43,7 @@ class AcquiredBuffer { // Accessors for the underlying BufferConsumer, the acquire fence, and the // use-case specific sequence value from the acquisition (see - // private/dvr/buffer_hub_client.h). + // private/dvr/consumer_buffer.h). std::shared_ptr<BufferConsumer> buffer() const { return buffer_; } int acquire_fence() const { return acquire_fence_.Get(); } diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h index 3090bd1e7a..e0f2eddfea 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 @@ class DisplayService : public pdx::ServiceBase<DisplayService> { 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 b8d5e2ba97..6d259bd3c9 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 kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns"; 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 @@ HardwareComposer::~HardwareComposer(void) { UpdatePostThreadState(PostThreadState::Quit, true); if (post_thread_.joinable()) post_thread_.join(); + composer_callback_->SetVsyncService(nullptr); } bool HardwareComposer::Initialize( @@ -147,6 +153,13 @@ bool HardwareComposer::Initialize( 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 @@ void HardwareComposer::CreateComposer() { 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 @@ void HardwareComposer::OnPostThreadPaused() { // 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 @@ HWC::Error HardwareComposer::Present(hwc2_display_t display) { // 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 @@ void HardwareComposer::PostThread() { 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 @@ void HardwareComposer::PostThread() { 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 @@ void HardwareComposer::PostThread() { 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 @@ void HardwareComposer::UpdateLayerConfig() { 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::onRefresh( 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 @@ HardwareComposer::ComposerCallback::GetDisplays() { 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 @@ Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime( 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 539a7fb6d7..6c25b3ee12 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 @@ class Layer { // 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 @@ class HardwareComposer { std::string Dump(); - void SetVSyncCallback(VSyncCallback callback); - const DisplayParams& GetPrimaryDisplayParams() const { return primary_display_; } @@ -350,6 +346,18 @@ class HardwareComposer { // 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 @@ class HardwareComposer { 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 @@ class HardwareComposer { 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 @@ class HardwareComposer { // 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 @@ class HardwareComposer { 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 0000000000..c884cb3cfe --- /dev/null +++ b/libs/vr/libvrflinger/tests/Android.bp @@ -0,0 +1,39 @@ +shared_libs = [ + "android.hardware.configstore-utils", + "android.hardware.configstore@1.0", + "libbinder", + "libbufferhubqueue", + "libcutils", + "libgui", + "libhidlbase", + "liblog", + "libui", + "libutils", + "libnativewindow", + "libpdx_default_transport", +] + +static_libs = [ + "libdisplay", +] + +cc_test { + srcs: ["vrflinger_test.cpp"], + // See go/apct-presubmit for documentation on how this .filter file is used + // by Android's automated testing infrastructure for test filtering. + data: ["vrflinger_test.filter"], + static_libs: static_libs, + shared_libs: shared_libs, + cflags: [ + "-DLOG_TAG=\"VrFlingerTest\"", + "-DTRACE=0", + "-O0", + "-g", + "-Wall", + "-Werror", + ], + name: "vrflinger_test", + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp new file mode 100644 index 0000000000..0eb7fec3c4 --- /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()->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()->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 0000000000..030bb7b67c --- /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 26aed4f25f..b57383ad44 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 @@ bool VrFlinger::Init(Hwc2::Composer* hidl, 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 b8d8b08b5f..0000000000 --- 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 822f02b266..0000000000 --- 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 4acc085428..c72f75e69c 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> @@ -228,7 +228,7 @@ class PoseClient : public pdx::ClientBase<PoseClient> { } 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; diff --git a/libs/vr/public.libraries-google.txt b/libs/vr/public.libraries-google.txt new file mode 100644 index 0000000000..8271b9421f --- /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 0000000000..f2b501cdec --- /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 93a3965cfc..c9e8b7c52c 100644 --- a/opengl/include/EGL/egl.h +++ b/opengl/include/EGL/egl.h @@ -33,12 +33,12 @@ extern "C" { ** 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 EGLBoolean EGLAPIENTRY eglWaitClient (void); 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 44f4dbc929..501bf58531 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -28,17 +28,17 @@ extern "C" { ** 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 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurfacePointerANGLE (EGLDisplay dpy, EGLSu #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 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribEXT (EGLDisplay dpy, EGLint a #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 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribEXT (EGLDisplay dpy, EGLint a #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 @@ EGLAPI EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT (EGLDisplay dpy, EGLSu #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 @@ EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerGLTextureExternalAttribsNV (EGLDi #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 @@ EGLAPI EGLuint64NV EGLAPIENTRY eglGetSystemTimeNV (void); #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 0000000000..0556ea1342 --- /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 c77c3338de..0bc2cb9a74 100644 --- a/opengl/include/EGL/eglplatform.h +++ b/opengl/include/EGL/eglplatform.h @@ -77,12 +77,24 @@ typedef HDC EGLNativeDisplayType; 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 ANativeWindow* EGLNativeWindowType; 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 Display *EGLNativeDisplayType; 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 d43c1648be..c80b79a319 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -140,16 +140,22 @@ cc_library_shared { "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", + "libnativeloader", "libutils", ], static_libs: [ @@ -191,6 +197,7 @@ cc_library_shared { 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 b3752f5bcc..74c4d7d07a 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -79,7 +79,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, } 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 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, 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 @@ BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) : 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 1f5d5357e6..e5c5e5bc25 100644 --- a/opengl/libs/EGL/BlobCache.h +++ b/opengl/libs/EGL/BlobCache.h @@ -97,6 +97,10 @@ public: // 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 edbaaf090d..cf67cf443b 100644 --- a/opengl/libs/EGL/BlobCache_test.cpp +++ b/opengl/libs/EGL/BlobCache_test.cpp @@ -97,7 +97,7 @@ TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { 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 @@ TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { } 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 @@ TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { } 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 @@ TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { 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 @@ TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { 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 79237159bf..cc42ac7fef 100644 --- a/opengl/libs/EGL/FileBlobCache.cpp +++ b/opengl/libs/EGL/FileBlobCache.cpp @@ -77,7 +77,7 @@ FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxT return; } - uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, + uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0)); if (buf == MAP_FAILED) { ALOGE("error mmaping cache file: %s (%d)", strerror(errno), diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 91a34558aa..8a409aeb36 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> @@ -33,12 +33,10 @@ #endif #include <vndksupport/linker.h> +#include "egl_platform_entries.h" #include "egl_trace.h" #include "egldefs.h" - -extern "C" { - android_namespace_t* android_get_exported_namespace(const char*); -} +#include <EGL/eglext_angle.h> // ---------------------------------------------------------------------------- namespace android { @@ -129,7 +127,7 @@ 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 +135,7 @@ Loader::driver_t::~driver_t() for (size_t i=0 ; i<NELEM(dso) ; i++) { if (dso[i]) { dlclose(dso[i]); - dso[i] = 0; + dso[i] = nullptr; } } } @@ -163,7 +161,7 @@ int Loader::driver_t::set(void* hnd, int32_t api) // ---------------------------------------------------------------------------- Loader::Loader() - : getProcAddress(NULL) + : getProcAddress(nullptr) { } @@ -221,10 +219,17 @@ void* Loader::open(egl_connection_t* cnx) ATRACE_CALL(); void* dso; - driver_t* hnd = 0; + driver_t* hnd = nullptr; setEmulatorGlesValue(); + // Check if we should use ANGLE early, so loading each driver doesn't require repeated queries. + if (android::GraphicsEnv::getInstance().shouldUseAngle()) { + cnx->shouldUseAngle = true; + } else { + cnx->shouldUseAngle = false; + } + dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2); if (dso) { hnd = new driver_t(dso); @@ -253,14 +258,25 @@ void* Loader::open(egl_connection_t* cnx) return (void*)hnd; } -void Loader::close(void* driver) +void Loader::close(egl_connection_t* cnx) { - driver_t* hnd = (driver_t*)driver; + driver_t* hnd = (driver_t*) cnx->dso; delete hnd; + cnx->dso = nullptr; + + cnx->shouldUseAngle = false; + cnx->angleDecided = false; + cnx->useAngle = false; + + if (cnx->vendorEGL) { + dlclose(cnx->vendorEGL); + cnx->vendorEGL = nullptr; + } } void Loader::init_api(void* dso, char const * const * api, + char const * const * ref_api, __eglMustCastToProperFunctionPointerType* curr, getProcAddressType getProcAddress) { @@ -270,13 +286,22 @@ void Loader::init_api(void* dso, char scrap[SIZE]; while (*api) { char const * name = *api; + if (ref_api) { + char const * ref_name = *ref_api; + if (std::strcmp(name, ref_name) != 0) { + *curr++ = nullptr; + ref_api++; + continue; + } + } + __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); - if (f == NULL) { + if (f == nullptr) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); } - if (f == NULL) { + if (f == nullptr) { // Try without the OES postfix ssize_t index = ssize_t(strlen(name)) - 3; if ((index>0 && (index<SIZE-1)) && (!strcmp(name+index, "OES"))) { @@ -286,7 +311,7 @@ void Loader::init_api(void* dso, //ALOGD_IF(f, "found <%s> instead", scrap); } } - if (f == NULL) { + if (f == nullptr) { // Try with the OES postfix ssize_t index = ssize_t(strlen(name)) - 3; if (index>0 && strcmp(name+index, "OES")) { @@ -295,7 +320,7 @@ void Loader::init_api(void* dso, //ALOGD_IF(f, "found <%s> instead", scrap); } } - if (f == NULL) { + if (f == nullptr) { //ALOGD("%s", name); f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented; @@ -314,6 +339,7 @@ void Loader::init_api(void* dso, } *curr++ = f; api++; + if (ref_api) ref_api++; } } @@ -406,9 +432,9 @@ static void* load_system_driver(const char* kind) { } DIR* d = opendir(search); - if (d != NULL) { + if (d != nullptr) { struct dirent* e; - while ((e = readdir(d)) != NULL) { + while ((e = readdir(d)) != nullptr) { if (e->d_type == DT_DIR) { continue; } @@ -434,7 +460,7 @@ static void* load_system_driver(const char* kind) { std::string absolutePath = MatchFile::find(kind); if (absolutePath.empty()) { // this happens often, we don't want to log an error - return 0; + return nullptr; } const char* const driver_absolute_path = absolutePath.c_str(); @@ -444,10 +470,10 @@ static void* load_system_driver(const char* kind) { // sphal namespace. void* dso = do_android_load_sphal_library(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); - if (dso == 0) { + if (dso == nullptr) { const char* err = dlerror(); ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown"); - return 0; + return nullptr; } ALOGD("loaded %s", driver_absolute_path); @@ -455,6 +481,81 @@ static void* load_system_driver(const char* kind) { return dso; } +static void* load_angle_from_namespace(const char* kind, android_namespace_t* ns) { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = ns, + }; + + std::string name = std::string("lib") + kind + "_angle.so"; + + void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + + if (so) { + ALOGD("dlopen_ext from APK (%s) success at %p", name.c_str(), so); + return so; + } else { + ALOGE("dlopen_ext(\"%s\") failed: %s", name.c_str(), dlerror()); + } + + return nullptr; +} + +static void* load_angle(const char* kind, android_namespace_t* ns, egl_connection_t* cnx) { + // Only attempt to load ANGLE libs + if (strcmp(kind, "EGL") != 0 && strcmp(kind, "GLESv2") != 0 && strcmp(kind, "GLESv1_CM") != 0) { + return nullptr; + } + + void* so = nullptr; + + if ((cnx->shouldUseAngle) || android::GraphicsEnv::getInstance().shouldUseAngle()) { + so = load_angle_from_namespace(kind, ns); + cnx->shouldUseAngle = true; + } else { + cnx->shouldUseAngle = false; + } + + if (so) { + ALOGV("Loaded ANGLE %s library for '%s' (instead of native)", kind, + android::GraphicsEnv::getInstance().getAngleAppName().c_str()); + cnx->useAngle = true; + + char prop[PROPERTY_VALUE_MAX]; + + property_get("debug.hwui.renderer", prop, "UNSET"); + ALOGV("Skia's renderer set to %s", prop); + + EGLint angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; + property_get("debug.angle.backend", prop, "0"); + switch (atoi(prop)) { + case 1: + ALOGV("%s: Requesting OpenGLES back-end", __FUNCTION__); + angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE; + break; + case 2: + ALOGV("%s: Requesting Vulkan back-end", __FUNCTION__); + angleBackendDefault = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE; + break; + default: + break; + } + + cnx->angleBackend = angleBackendDefault; + if (!cnx->vendorEGL && (cnx->angleBackend == EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)) { + // Find and load vendor libEGL for ANGLE's GL back-end to use. + cnx->vendorEGL = load_system_driver("EGL"); + } + } else { + ALOGV("Loaded native %s library for '%s' (instead of ANGLE)", kind, + android::GraphicsEnv::getInstance().getAngleAppName().c_str()); + cnx->useAngle = false; + } + cnx->angleDecided = true; + + return so; +} + static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = { "ro.hardware.egl", "ro.board.platform", @@ -486,16 +587,22 @@ void *Loader::load_driver(const char* kind, ATRACE_CALL(); void* dso = nullptr; -#ifndef __ANDROID_VNDK__ - android_namespace_t* ns = android_getDriverNamespace(); + android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); if (ns) { - dso = load_updated_driver(kind, ns); + dso = load_angle(kind, ns, cnx); + } +#ifndef __ANDROID_VNDK__ + if (!dso) { + android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace(); + if (ns) { + dso = load_updated_driver(kind, ns); + } } #endif if (!dso) { dso = load_system_driver(kind); if (!dso) - return NULL; + return nullptr; } if (mask & EGL) { @@ -512,11 +619,11 @@ void *Loader::load_driver(const char* kind, char const * name = *api; __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); - if (f == NULL) { + if (f == nullptr) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); - if (f == NULL) { - f = (__eglMustCastToProperFunctionPointerType)0; + if (f == nullptr) { + f = (__eglMustCastToProperFunctionPointerType)nullptr; } } *curr++ = f; @@ -525,14 +632,14 @@ void *Loader::load_driver(const char* kind, } if (mask & GLESv1_CM) { - init_api(dso, gl_names, + init_api(dso, gl_names_1, gl_names, (__eglMustCastToProperFunctionPointerType*) &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl, getProcAddress); } if (mask & GLESv2) { - init_api(dso, gl_names, + init_api(dso, gl_names, nullptr, (__eglMustCastToProperFunctionPointerType*) &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl, getProcAddress); diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h index 6a32bb3066..392887d42c 100644 --- a/opengl/libs/EGL/Loader.h +++ b/opengl/libs/EGL/Loader.h @@ -29,11 +29,12 @@ struct egl_connection_t; class Loader { typedef __eglMustCastToProperFunctionPointerType (* getProcAddressType)(const char*); - + enum { EGL = 0x01, GLESv1_CM = 0x02, - GLESv2 = 0x04 + GLESv2 = 0x04, + PLATFORM = 0x08 }; struct driver_t { explicit driver_t(void* gles); @@ -42,25 +43,26 @@ class Loader { int set(void* hnd, int32_t api); void* dso[3]; }; - + getProcAddressType getProcAddress; public: static Loader& getInstance(); ~Loader(); - + void* open(egl_connection_t* cnx); - void close(void* driver); - + void close(egl_connection_t* cnx); + private: Loader(); void *load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask); static __attribute__((noinline)) - void init_api(void* dso, - char const * const * api, - __eglMustCastToProperFunctionPointerType* curr, - getProcAddressType getProcAddress); + void init_api(void* dso, + char const * const * api, + char const * const * ref_api, + __eglMustCastToProperFunctionPointerType* curr, + getProcAddressType getProcAddress); }; // ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index f53cf3f8cc..8870d5f571 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 @@ static int sEarlyInitState = pthread_once(&once_control, &early_egl_init); 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 @@ const GLubyte * egl_get_string_for_current_context(GLenum name) { 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 @@ const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) { 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 @@ GLint egl_get_num_extensions_for_current_context() { return -1; egl_context_t const * const c = get_context(context); - if (c == NULL) // this should never happen, by construction + if (c == nullptr) // this should never happen, by construction return -1; return (GLint)c->tokenized_gl_extensions.size(); } +egl_connection_t* egl_get_connection() { + return &gEGLImpl; +} + // ---------------------------------------------------------------------------- // this mutex protects: @@ -184,7 +187,7 @@ static EGLBoolean egl_init_drivers_locked() { // dynamically load our EGL implementation egl_connection_t* cnx = &gEGLImpl; - if (cnx->dso == 0) { + if (cnx->dso == nullptr) { cnx->hooks[egl_connection_t::GLESv1_INDEX] = &gHooks[egl_connection_t::GLESv1_INDEX]; cnx->hooks[egl_connection_t::GLESv2_INDEX] = @@ -192,6 +195,14 @@ static EGLBoolean egl_init_drivers_locked() { cnx->dso = loader.open(cnx); } + // Check to see if any layers are enabled and route functions through them + if (cnx->dso) { + // Layers can be enabled long after the drivers have been loaded. + // They will only be initialized once. + LayerLoader& layer_loader(LayerLoader::getInstance()); + layer_loader.InitLayers(cnx); + } + return cnx->dso ? EGL_TRUE : EGL_FALSE; } @@ -249,12 +260,22 @@ void setGlThreadSpecific(gl_hooks_t const *value) { char const * const gl_names[] = { #include "../entries.in" - NULL + nullptr +}; + +char const * const gl_names_1[] = { + #include "../entries_gles1.in" + nullptr }; char const * const egl_names[] = { #include "egl_entries.in" - NULL + nullptr +}; + +char const * const platform_names[] = { + #include "platform_entries.in" + nullptr }; #undef GL_ENTRY diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 36deedcb85..29a966d348 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; - -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 " - ; -// 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; -} - -// ---------------------------------------------------------------------------- - -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; +} // namespace android -// ---------------------------------------------------------------------------- - -static inline void clearError() { egl_tls_t::clearError(); } -static inline EGLContext getContext() { return egl_tls_t::getContext(); } - -// ---------------------------------------------------------------------------- +static inline void clearError() { + egl_tls_t::clearError(); +} -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); + if (egl_init_drivers() == EGL_FALSE) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } - return res; + // 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 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. - +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { clearError(); - egl_display_ptr dp = get_display(dpy); - if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); - - EGLBoolean res = dp->terminate(); - - return res; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglInitialize(dpy, major, minor); } -// ---------------------------------------------------------------------------- -// configuration -// ---------------------------------------------------------------------------- - -EGLBoolean eglGetConfigs( EGLDisplay dpy, - 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) { - res = cnx->egl.eglGetConfigs( - dp->disp.dpy, configs, config_size, num_config); - } - - return res; + return cnx->platform.eglTerminate(dpy); } -EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config) -{ +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); - } - - 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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetConfigs(dpy, configs, config_size, num_config); } -EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, 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.eglChooseConfig(dpy, attrib_list, configs, config_size, num_config); } -// ---------------------------------------------------------------------------- -// surfaces -// ---------------------------------------------------------------------------- +EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value) { + clearError(); -// 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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetConfigAttrib(dpy, config, attribute, value); } -// 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; -} +EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, NativeWindowType window, + const EGLint* attrib_list) { + clearError(); -// 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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateWindowSurface(dpy, config, window, attrib_list); +} - // 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); +EGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void* native_window, + const EGLAttrib* attrib_list) { + clearError(); - 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; - } - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePlatformWindowSurface(dpy, config, native_window, attrib_list); } -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; - 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; -} - -EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, - NativePixmapType pixmap, - 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); - - // 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; -} - -EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, - 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; +EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, NativePixmapType pixmap, + const EGLint* attrib_list) { + clearError(); + + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreatePixmapSurface(dpy, config, pixmap, attrib_list); } -EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) -{ +EGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void* native_pixmap, + const EGLAttrib* 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.eglCreatePlatformPixmapSurface(dpy, config, native_pixmap, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { + clearError(); - 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.eglCreatePbufferSurface(dpy, config, attrib_list); } -EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroySurface(dpy, surface); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) { + 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.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(); -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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateContext(dpy, config, share_list, attrib_list); } -EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) -{ +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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroyContext(dpy, ctx); } -EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx) -{ +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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglMakeCurrent(dpy, draw, read, ctx); } - -EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, - EGLint attribute, EGLint *value) -{ +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); - + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryContext(dpy, ctx, 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. - +EGLContext eglGetCurrentContext(void) { clearError(); - EGLContext ctx = getContext(); - return ctx; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentContext(); } -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. - +EGLSurface eglGetCurrentSurface(EGLint readdraw) { 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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentSurface(readdraw); } -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. - +EGLDisplay eglGetCurrentDisplay(void) { 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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetCurrentDisplay(); } -EGLBoolean eglWaitGL(void) -{ +EGLBoolean eglWaitGL(void) { 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.eglWaitGL(); } -EGLBoolean eglWaitNative(EGLint engine) -{ +EGLBoolean eglWaitNative(EGLint engine) { 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.eglWaitNative(engine); } -EGLint eglGetError(void) -{ - EGLint err = EGL_SUCCESS; +EGLint eglGetError(void) { 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 = NULL; - - 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; + return cnx->platform.eglGetError(); } -__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) -{ +__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,98 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) if (egl_init_drivers() == EGL_FALSE) { setError(EGL_BAD_PARAMETER, NULL); - return NULL; - } - - 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; -} - -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())); - } + return nullptr; } - uint32_t mFramesQueued; - uint32_t mFramesCompleted; - std::deque<EGLSyncKHR> mQueue; - std::condition_variable mCondition; - std::mutex mMutex; -}; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglGetProcAddress(procname); +} -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); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapBuffersWithDamageKHR(dpy, draw, rects, n_rects); } -EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, - NativePixmapType target) -{ +EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { + ATRACE_CALL(); 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); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapBuffers(dpy, surface); } -const char* eglQueryString(EGLDisplay dpy, EGLint name) -{ +EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) { 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); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCopyBuffers(dpy, surface, target); } -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) -{ +const char* eglQueryString(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_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryString(dpy, name); } -// ---------------------------------------------------------------------------- -// EGL 1.1 -// ---------------------------------------------------------------------------- - -EGLBoolean eglSurfaceAttrib( - EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) -{ +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name) { 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); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglQueryStringImplementationANDROID(dpy, name); } -EGLBoolean eglBindTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ +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 * 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); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSurfaceAttrib(dpy, surface, attribute, value); } -EGLBoolean eglReleaseTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ +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.eglReleaseTexImage) { - return s->cnx->egl.eglReleaseTexImage( - dp->disp.dpy, s->surface, buffer); - } - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglBindTexImage(dpy, surface, buffer); } -EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) -{ +EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { 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.eglReleaseTexImage(dpy, surface, buffer); } +EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) { + clearError(); -// ---------------------------------------------------------------------------- -// EGL 1.2 -// ---------------------------------------------------------------------------- + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglSwapInterval(dpy, interval); +} -EGLBoolean eglWaitClient(void) -{ +EGLBoolean eglWaitClient(void) { 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.eglWaitClient(); } -EGLBoolean eglBindAPI(EGLenum api) -{ +EGLBoolean eglBindAPI(EGLenum api) { 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.eglBindAPI(api); } -EGLenum eglQueryAPI(void) -{ +EGLenum eglQueryAPI(void) { clearError(); if (egl_init_drivers() == EGL_FALSE) { @@ -1620,824 +342,324 @@ EGLenum eglQueryAPI(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; + return cnx->platform.eglQueryAPI(); } -EGLBoolean eglReleaseThread(void) -{ +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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglLockSurfaceKHR(dpy, surface, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) { + clearError(); - 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.eglUnlockSurfaceKHR(dpy, surface); } -EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) -{ +EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, 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.eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list); +} - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +EGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attrib_list) { + clearError(); - 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.eglCreateImage(dpy, ctx, target, buffer, attrib_list); } -EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, - 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); +EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) { + clearError(); - 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.eglDestroyImageKHR(dpy, img); } -EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) -{ +EGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImageKHR img) { 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.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; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateSync(dpy, type, attrib_list); +} + +EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { + clearError(); - 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.eglCreateSyncKHR(dpy, type, attrib_list); } -EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) -{ +EGLBoolean eglDestroySync(EGLDisplay dpy, EGLSyncKHR sync) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDestroySync(dpy, sync); +} + +EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { + clearError(); - 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.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 -// ---------------------------------------------------------------------------- - -EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { +EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHR(EGLDisplay dpy, EGLStreamKHR stream) { 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.eglGetStreamFileDescriptorKHR(dpy, stream); } -// ---------------------------------------------------------------------------- -// ANDROID extensions -// ---------------------------------------------------------------------------- - -EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) -{ +EGLStreamKHR eglCreateStreamFromFileDescriptorKHR(EGLDisplay dpy, + EGLNativeFileDescriptorKHR file_descriptor) { clearError(); - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglCreateStreamFromFileDescriptorKHR(dpy, file_descriptor); +} - EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; +EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { + clearError(); 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.eglWaitSyncKHR(dpy, sync, flags); } -EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, - EGLnsecsANDROID time) -{ +EGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags) { clearError(); + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglWaitSync(dpy, sync, flags); +} - const egl_display_ptr dp = validate_display(dpy); - if (!dp) { - return EGL_FALSE; - } +EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync) { + clearError(); - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) { - setError(EGL_BAD_SURFACE, EGL_FALSE); - return EGL_FALSE; - } + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglDupNativeFenceFDANDROID(dpy, sync); +} - egl_surface_t const * const s = get_surface(surface); - native_window_set_buffers_timestamp(s->getNativeWindow(), time); +EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface, EGLnsecsANDROID time) { + clearError(); - return EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + return cnx->platform.eglPresentationTimeANDROID(dpy, surface, time); } -EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer *buffer) { +EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) { 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.eglGetNativeClientBufferANDROID(buffer); } -// ---------------------------------------------------------------------------- -// NVIDIA extensions -// ---------------------------------------------------------------------------- -EGLuint64NV eglGetSystemTimeFrequencyNV() -{ +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 0000000000..297e0c49cb --- /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("libEGL_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/opengl/libs/EGL/egl_angle_platform.h b/opengl/libs/EGL/egl_angle_platform.h new file mode 100644 index 0000000000..6c24aa5418 --- /dev/null +++ b/opengl/libs/EGL/egl_angle_platform.h @@ -0,0 +1,33 @@ +/* + * 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 + +#if defined(__ANDROID__) + +#include "egldefs.h" + +#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 ec548f3121..bcf496164b 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -95,7 +95,7 @@ void egl_cache_t::initialize(egl_display_t *display) { 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 @@ void egl_cache_t::terminate() { 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 2aec249aa5..c100db7c2a 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 @@ namespace android { // ---------------------------------------------------------------------------- static char const * const sVendorString = "Android"; -static char const * const sVersionString = "1.4 Android META-EGL"; +static char const* const sVersionString14 = "1.4 Android META-EGL"; +static char const* const sVersionString15 = "1.5 Android META-EGL"; static char const * const sClientApiString = "OpenGL_ES"; extern char const * const gBuiltinExtensionString; @@ -114,15 +120,91 @@ bool egl_display_t::getObject(egl_object_t* object) const { 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)].getPlatformDisplay(disp, attrib_list); +} + +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 sDisplay[uintptr_t(disp)].getDisplay(disp); + return true; } -EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) { +static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx, + const EGLAttrib* attrib_list, EGLint* error) { + EGLDisplay dpy = EGL_NO_DISPLAY; + *error = EGL_NONE; + + if (cnx->egl.eglGetPlatformDisplay) { + std::vector<EGLAttrib> attrs; + if (attrib_list) { + for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) { + attrs.push_back(attr[0]); + attrs.push_back(attr[1]); + } + } + + if (!addAnglePlatformAttributes(cnx, attrs)) { + ALOGE("eglGetDisplay(%p) failed: Mismatch display request", display); + *error = EGL_BAD_PARAMETER; + return EGL_NO_DISPLAY; + } + attrs.push_back(EGL_NONE); + + dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE, + reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY), + attrs.data()); + if (dpy == EGL_NO_DISPLAY) { + ALOGE("eglGetPlatformDisplay failed!"); + } else { + if (!angle::initializeAnglePlatform(dpy)) { + ALOGE("initializeAnglePlatform failed!"); + } + } + } else { + ALOGE("eglGetDisplay(%p) failed: Unable to look up eglGetPlatformDisplay from ANGLE", + display); + } + + return dpy; +} +EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display, + const EGLAttrib* attrib_list) { std::lock_guard<std::mutex> _l(lock); ATRACE_CALL(); @@ -131,11 +213,37 @@ EGLDisplay egl_display_t::getDisplay(EGLNativeDisplayType display) { egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && disp.dpy == EGL_NO_DISPLAY) { - EGLDisplay dpy = cnx->egl.eglGetDisplay(display); + EGLDisplay dpy = EGL_NO_DISPLAY; + + if (cnx->useAngle) { + EGLint error; + dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error); + if (error != EGL_NONE) { + return setError(error, dpy); + } + } + if (dpy == EGL_NO_DISPLAY) { + // NOTE: eglGetPlatformDisplay with a empty attribute list + // behaves the same as eglGetDisplay + if (cnx->egl.eglGetPlatformDisplay) { + dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANDROID_KHR, display, + attrib_list); + } + + // It is possible that eglGetPlatformDisplay does not have a + // working implementation for Android platform; in that case, + // one last fallback to eglGetDisplay + if(dpy == EGL_NO_DISPLAY) { + if (attrib_list) { + ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored"); + } + dpy = cnx->egl.eglGetDisplay(display); + } + } + disp.dpy = dpy; if (dpy == EGL_NO_DISPLAY) { - loader.close(cnx->dso); - cnx->dso = NULL; + loader.close(cnx); } } @@ -148,13 +256,20 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { std::unique_lock<std::mutex> _l(refLock); refs++; if (refs > 1) { - if (major != NULL) - *major = VERSION_MAJOR; - if (minor != NULL) - *minor = VERSION_MINOR; + // We don't know what to report until we know what the + // driver supports. Make sure we are initialized before + // returning the version info. while(!eglIsInitialized) { refCond.wait(_l); } + egl_connection_t* const cnx = &gEGLImpl; + + // TODO: If device doesn't provide 1.4 or 1.5 then we'll be + // changing the behavior from the past where we always advertise + // version 1.4. May need to check that revision is valid + // before using cnx->major & cnx->minor + if (major != nullptr) *major = cnx->major; + if (minor != nullptr) *minor = cnx->minor; return EGL_TRUE; } while(eglIsInitialized) { @@ -201,7 +316,52 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { // the query strings are per-display mVendorString = sVendorString; - mVersionString = sVersionString; + mVersionString.clear(); + cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0); + if ((cnx->major == 1) && (cnx->minor == 5)) { + mVersionString = sVersionString15; + cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0); + } else if ((cnx->major == 1) && (cnx->minor == 4)) { + mVersionString = sVersionString14; + // Extensions needed for an EGL 1.4 implementation to be + // able to support EGL 1.5 functionality + std::vector<const char*> egl15extensions = { + "EGL_EXT_client_extensions", + // "EGL_EXT_platform_base", // implemented by EGL runtime + "EGL_KHR_image_base", + "EGL_KHR_fence_sync", + "EGL_KHR_wait_sync", + "EGL_KHR_create_context", + "EGL_EXT_create_context_robustness", + "EGL_KHR_gl_colorspace", + "EGL_ANDROID_native_fence_sync", + }; + bool extensionsFound = true; + for (const auto& name : egl15extensions) { + extensionsFound &= findExtension(disp.queryString.extensions, name); + ALOGV("Extension %s: %s", name, + findExtension(disp.queryString.extensions, name) ? "Found" : "Missing"); + } + // NOTE: From the spec: + // Creation of fence sync objects requires support from the bound + // client API, and will not succeed unless the client API satisfies: + // client API is OpenGL ES, and either the OpenGL ES version is 3.0 + // or greater, or the GL_OES_EGL_sync extension is supported. + // We don't have a way to check the GL_EXTENSIONS string at this + // point in the code, assume that GL_OES_EGL_sync is supported + // because EGL_KHR_fence_sync is supported (as verified above). + if (extensionsFound) { + // Have everything needed to emulate EGL 1.5 so report EGL 1.5 + // to the application. + mVersionString = sVersionString15; + cnx->major = 1; + cnx->minor = 5; + } + } + if (mVersionString.empty()) { + ALOGW("Unexpected driver version: %d.%d, want 1.4 or 1.5", cnx->major, cnx->minor); + mVersionString = sVersionString14; + } mClientApiString = sClientApiString; mExtensionString = gBuiltinExtensionString; @@ -218,7 +378,8 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (wideColorBoardConfig && hasColorSpaceSupport) { mExtensionString.append( "EGL_EXT_gl_colorspace_scrgb EGL_EXT_gl_colorspace_scrgb_linear " - "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 "); + "EGL_EXT_gl_colorspace_display_p3_linear EGL_EXT_gl_colorspace_display_p3 " + "EGL_EXT_gl_colorspace_display_p3_passthrough "); } bool hasHdrBoardConfig = @@ -240,12 +401,6 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (len) { // NOTE: we could avoid the copy if we had strnstr. const std::string ext(start, len); - // Temporary hack: Adreno 530 driver exposes this extension under the draft - // KHR name, but during Khronos review it was decided to demote it to EXT. - if (ext == "EGL_EXT_image_gl_colorspace" && - findExtension(disp.queryString.extensions, "EGL_KHR_image_gl_colorspace")) { - mExtensionString.append("EGL_EXT_image_gl_colorspace "); - } if (findExtension(disp.queryString.extensions, ext.c_str(), len)) { mExtensionString.append(ext + " "); } @@ -268,10 +423,12 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { traceGpuCompletion = true; } - if (major != NULL) - *major = VERSION_MAJOR; - if (minor != NULL) - *minor = VERSION_MINOR; + // TODO: If device doesn't provide 1.4 or 1.5 then we'll be + // changing the behavior from the past where we always advertise + // version 1.4. May need to check that revision is valid + // before using cnx->major & cnx->minor + if (major != nullptr) *major = cnx->major; + if (minor != nullptr) *minor = cnx->minor; } { // scope for refLock @@ -311,6 +468,10 @@ EGLBoolean egl_display_t::terminate() { egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && disp.state == egl_display_t::INITIALIZED) { + // If we're using ANGLE reset any custom DisplayPlatform + if (cnx->useAngle) { + angle::resetAnglePlatform(disp.dpy); + } if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) { ALOGW("eglTerminate(%p) failed (%s)", disp.dpy, egl_tls_t::egl_strerror(cnx->egl.eglGetError())); @@ -361,8 +522,8 @@ void egl_display_t::loseCurrentImpl(egl_context_t * cur_c) // by construction, these are either 0 or valid (possibly terminated) // it should be impossible for these to be invalid ContextRef _cur_c(cur_c); - SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : NULL); - SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : NULL); + SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr); + SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr); { // scope for the lock std::lock_guard<std::mutex> _l(lock); @@ -387,8 +548,8 @@ EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, // 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 79a9f082a6..36856b79fe 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -49,6 +49,7 @@ bool findExtension(const char* exts, const char* name, size_t nameLen = 0); class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes static egl_display_t sDisplay[NUM_DISPLAYS]; EGLDisplay getDisplay(EGLNativeDisplayType display); + EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list); void loseCurrentImpl(egl_context_t * cur_c); public: @@ -72,7 +73,7 @@ public: bool getObject(egl_object_t* object) const; static egl_display_t* get(EGLDisplay dpy); - static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp); + static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list); EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read, EGLContext ctx, @@ -160,7 +161,7 @@ public: 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 b587a16203..2921d512f1 100644 --- a/opengl/libs/EGL/egl_entries.in +++ b/opengl/libs/EGL/egl_entries.in @@ -44,6 +44,18 @@ EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGL /* 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(EGLNativeFileDescriptorKHR, eglGetStreamFileDescriptorKHR, 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 0000000000..dd8fbfcb7d --- /dev/null +++ b/opengl/libs/EGL/egl_layers.cpp @@ -0,0 +1,435 @@ +/* + ** Copyright 2018, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "egl_layers.h" + +#include <EGL/egl.h> +#include <android-base/file.h> +#include <android-base/strings.h> +#include <android/dlext.h> +#include <cutils/properties.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> +#include <log/log.h> +#include <nativebridge/native_bridge.h> +#include <nativeloader/native_loader.h> +#include <sys/prctl.h> + +namespace android { + +// GLES Layers +// +// - Layer discovery - +// 1. Check for debug layer list from GraphicsEnv +// 2. If none enabled, check system properties +// +// - Layer initializing - +// TODO: ADD DETAIL ABOUT NEW INTERFACES +// - InitializeLayer (provided by layer, called by loader) +// - GetLayerProcAddress (provided by layer, called by loader) +// - getNextLayerProcAddress (provided by loader, called by layer) +// +// 1. Walk through defs for egl and each gl version +// 2. Call GetLayerProcAddress passing the name and the target hook entry point +// - This tells the layer the next point in the chain it should call +// 3. Replace the hook with the layer's entry point +// - All entryoints will be present, anything unsupported by the driver will +// have gl_unimplemented +// +// - Extension layering - +// Not all functions are known to Android, so libEGL handles extensions. +// They are looked up by applications using eglGetProcAddress +// Layers can look them up with getNextLayerProcAddress + +const int kFuncCount = sizeof(platform_impl_t) / sizeof(char*) + sizeof(egl_t) / sizeof(char*) + + sizeof(gl_hooks_t) / sizeof(char*); + +typedef struct FunctionTable { + EGLFuncPointer x[kFuncCount]; + EGLFuncPointer& operator[](int i) { return x[i]; } +} FunctionTable; + +// TODO: Move these to class +std::unordered_map<std::string, int> func_indices; +// func_indices.reserve(kFuncCount); + +std::unordered_map<int, std::string> func_names; +// func_names.reserve(kFuncCount); + +std::vector<FunctionTable> layer_functions; + +const void* getNextLayerProcAddress(void* layer_id, const char* name) { + // Use layer_id to find funcs for layer below current + // This is the same key provided in InitializeLayer + auto next_layer_funcs = reinterpret_cast<FunctionTable*>(layer_id); + EGLFuncPointer val; + + if (func_indices.find(name) == func_indices.end()) { + // No entry for this function - it is an extension + // call down the GPA chain directly to the impl + ALOGV("getNextLayerProcAddress servicing %s", name); + + // Look up which GPA we should use + int gpaIndex = func_indices["eglGetProcAddress"]; + EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex]; + + ALOGV("Calling down the GPA chain (%llu) for %s", (unsigned long long)gpaNext, name); + + // Call it for the requested function + typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*); + PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext); + + val = reinterpret_cast<EGLFuncPointer>(next(name)); + ALOGV("Got back %llu for %s", (unsigned long long)val, name); + + // We should store it now, but to do that, we need to move func_idx to the class so we can + // increment it separately + // TODO: Move func_idx to class and store the result of GPA + return reinterpret_cast<void*>(val); + } + + // int index = func_indices[name]; + // val = (*next_layer_funcs)[index]; + // return reinterpret_cast<void*>(val); + return reinterpret_cast<void*>((*next_layer_funcs)[func_indices[name]]); +} + +void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFuncPointer* curr, + int& func_idx) { + while (*entries) { + const char* name = *entries; + + // Some names overlap, only fill with initial entry + // This does mean that some indices will not be used + if (func_indices.find(name) == func_indices.end()) { + func_names[func_idx] = name; + func_indices[name] = func_idx; + } + + // Populate layer_functions once with initial value + // These values will arrive in priority order, starting with platform entries + if (functions[func_idx] == nullptr) { + functions[func_idx] = *curr; + } + + entries++; + curr++; + func_idx++; + } +} + +LayerLoader& LayerLoader::getInstance() { + // This function is mutex protected in egl_init_drivers_locked and eglGetProcAddressImpl + static LayerLoader layer_loader; + + if (!layer_loader.layers_loaded_) layer_loader.LoadLayers(); + + return layer_loader; +} + +const char kSystemLayerLibraryDir[] = "/data/local/debug/gles"; + +std::string LayerLoader::GetDebugLayers() { + // Layers can be specified at the Java level in GraphicsEnvironemnt + // gpu_debug_layers_gles = layer1:layer2:layerN + std::string debug_layers = android::GraphicsEnv::getInstance().getDebugLayersGLES(); + + if (debug_layers.empty()) { + // Only check system properties if Java settings are empty + char prop[PROPERTY_VALUE_MAX]; + property_get("debug.gles.layers", prop, ""); + debug_layers = prop; + } + + return debug_layers; +} + +EGLFuncPointer LayerLoader::ApplyLayer(layer_setup_func layer_setup, const char* name, + EGLFuncPointer next) { + // Walk through our list of LayerSetup functions (they will already be in reverse order) to + // build up a call chain from the driver + + EGLFuncPointer layer_entry = next; + + layer_entry = layer_setup(name, layer_entry); + + if (next != layer_entry) { + ALOGV("We succeeded, replacing hook (%llu) with layer entry (%llu), for %s", + (unsigned long long)next, (unsigned long long)layer_entry, name); + } + + return layer_entry; +} + +EGLFuncPointer LayerLoader::ApplyLayers(const char* name, EGLFuncPointer next) { + if (!layers_loaded_ || layer_setup_.empty()) return next; + + ALOGV("ApplyLayers called for %s with next (%llu), current_layer_ (%i)", name, + (unsigned long long)next, current_layer_); + + EGLFuncPointer val = next; + + // Only ApplyLayers for layers that have been setup, not all layers yet + for (unsigned i = 0; i < current_layer_; i++) { + ALOGV("ApplyLayers: Calling ApplyLayer with i = %i for %s with next (%llu)", i, name, + (unsigned long long)next); + val = ApplyLayer(layer_setup_[i], name, val); + } + + ALOGV("ApplyLayers returning %llu for %s", (unsigned long long)val, name); + + return val; +} + +void LayerLoader::LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer* curr, + char const* const* entries) { + while (*entries) { + char const* name = *entries; + + EGLFuncPointer prev = *curr; + + // Pass the existing entry point into the layer, replace the call with return value + *curr = ApplyLayer(layer_setup, name, *curr); + + if (prev != *curr) { + ALOGV("LayerPlatformEntries: Replaced (%llu) with platform entry (%llu), for %s", + (unsigned long long)prev, (unsigned long long)*curr, name); + } else { + ALOGV("LayerPlatformEntries: No change(%llu) for %s, which means layer did not " + "intercept", + (unsigned long long)prev, name); + } + + curr++; + entries++; + } +} + +void LayerLoader::LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer* curr, + char const* const* entries) { + while (*entries) { + char const* name = *entries; + EGLFuncPointer prev = *curr; + + // Only apply layers to driver entries if not handled by the platform + if (FindPlatformImplAddr(name) == nullptr) { + // Pass the existing entry point into the layer, replace the call with return value + *curr = ApplyLayer(layer_setup, name, *prev); + + if (prev != *curr) { + ALOGV("LayerDriverEntries: Replaced (%llu) with platform entry (%llu), for %s", + (unsigned long long)prev, (unsigned long long)*curr, name); + } + + } else { + ALOGV("LayerDriverEntries: Skipped (%llu) for %s", (unsigned long long)prev, name); + } + + curr++; + entries++; + } +} + +bool LayerLoader::Initialized() { + return initialized_; +} + +void LayerLoader::InitLayers(egl_connection_t* cnx) { + if (!layers_loaded_) return; + + if (initialized_) return; + + if (layer_setup_.empty()) { + initialized_ = true; + return; + } + + // Include the driver in layer_functions + layer_functions.resize(layer_setup_.size() + 1); + + // Walk through the initial lists and create layer_functions[0] + int func_idx = 0; + char const* const* entries; + EGLFuncPointer* curr; + + entries = platform_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after platform_names: %i", func_idx); + + entries = egl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after egl_names: %i", func_idx); + + entries = gl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl); + SetupFuncMaps(layer_functions[0], entries, curr, func_idx); + ALOGV("InitLayers: func_idx after gl_names: %i", func_idx); + + // Walk through each layer's entry points per API, starting just above the driver + for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) { + // Init the layer with a key that points to layer just below it + layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]), + reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>( + getNextLayerProcAddress)); + + // Check functions implemented by the platform + func_idx = 0; + entries = platform_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform); + LayerPlatformEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + + // EGL + entries = egl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl); + LayerDriverEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + + // GLES 2+ + // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x + // If it were added in the future, a different layer initialization model would be needed, + // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase + // initialization. + entries = gl_names; + curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl); + LayerDriverEntries(layer_setup_[current_layer_], curr, entries); + + // Populate next function table after layers have been applied + SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx); + } + + // We only want to apply layers once + initialized_ = true; +} + +void LayerLoader::LoadLayers() { + std::string debug_layers = GetDebugLayers(); + + // If no layers are specified, we're done + if (debug_layers.empty()) return; + + // Only enable the system search path for non-user builds + std::string system_path; + if (property_get_bool("ro.debuggable", false) && prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + system_path = kSystemLayerLibraryDir; + } + + ALOGI("Debug layer list: %s", debug_layers.c_str()); + std::vector<std::string> layers = android::base::Split(debug_layers, ":"); + + // Load the layers in reverse order so we start with the driver's entrypoint and work our way up + for (int32_t i = layers.size() - 1; i >= 0; i--) { + // Check each layer path for the layer + std::vector<std::string> paths = + android::base::Split(android::GraphicsEnv::getInstance().getLayerPaths().c_str(), + ":"); + + if (!system_path.empty()) { + // Prepend the system paths so they override other layers + auto it = paths.begin(); + paths.insert(it, system_path); + } + + bool layer_found = false; + for (uint32_t j = 0; j < paths.size() && !layer_found; j++) { + std::string layer; + + ALOGI("Searching %s for GLES layers", paths[j].c_str()); + + // Realpath will return null for non-existent files + android::base::Realpath(paths[j] + "/" + layers[i], &layer); + + if (!layer.empty()) { + layer_found = true; + ALOGI("GLES layer found: %s", layer.c_str()); + + // Load the layer + // + // TODO: This code is common with Vulkan loader, refactor + // + // Libraries in the system layer library dir can't be loaded into + // the application namespace. That causes compatibility problems, since + // any symbol dependencies will be resolved by system libraries. They + // can't safely use libc++_shared, for example. Which is one reason + // (among several) we only allow them in non-user builds. + void* handle = nullptr; + auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace(); + if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) { + bool native_bridge = false; + std::string error_message; + handle = OpenNativeLibrary(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.c_str()); + return; + } + + } else { + handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL); + } + + if (handle) { + ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle, + layers[i].c_str()); + } else { + // If the layer is found but can't be loaded, try setenforce 0 + const char* dlsym_error = dlerror(); + ALOGE("Failed to load layer %s with error: %s", layer.c_str(), dlsym_error); + return; + } + + // Find the layer's Initialize function + std::string init_func = "InitializeLayer"; + ALOGV("Looking for entrypoint %s", init_func.c_str()); + + layer_init_func LayerInit = + reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str())); + if (LayerInit) { + ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str()); + layer_init_.push_back(LayerInit); + } else { + ALOGE("Failed to dlsym %s for layer %s", init_func.c_str(), layer.c_str()); + return; + } + + // Find the layer's setup function + std::string setup_func = "GetLayerProcAddress"; + ALOGV("Looking for entrypoint %s", setup_func.c_str()); + + layer_setup_func LayerSetup = + reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str())); + if (LayerSetup) { + ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str()); + layer_setup_.push_back(LayerSetup); + } else { + ALOGE("Failed to dlsym %s for layer %s", setup_func.c_str(), layer.c_str()); + return; + } + } + } + } + // Track this so we only attempt to load these once + layers_loaded_ = true; +} + +} // namespace android diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h new file mode 100644 index 0000000000..e401b448cf --- /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 f879254f76..ff4fe2dd9c 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(egl_display_t* dpy, EGLConfig config, EGLNativeWind } 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 @@ void egl_surface_t::terminate() { 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 4e1de5cec4..fb2bdf4c51 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -67,7 +67,7 @@ public: 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 0000000000..79166a75c3 --- /dev/null +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -0,0 +1,2699 @@ +/* + ** Copyright 2007, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "egl_platform_entries.h" + +#include <ctype.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <string.h> + +#include <hardware/gralloc1.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <EGL/eglext_angle.h> + +#include <android/hardware_buffer.h> +#include <android-base/strings.h> +#include <graphicsenv/GraphicsEnv.h> +#include <private/android/AHardwareBufferHelpers.h> + +#include <cutils/compiler.h> +#include <cutils/properties.h> +#include <log/log.h> + +#include <condition_variable> +#include <deque> +#include <mutex> +#include <unordered_map> +#include <string> +#include <thread> + +#include "../egl_impl.h" + +#include "egl_display.h" +#include "egl_object.h" +#include "egl_layers.h" +#include "egl_tls.h" +#include "egl_trace.h" + +using namespace android; + +// ---------------------------------------------------------------------------- + +namespace android { + +using nsecs_t = int64_t; + +struct extention_map_t { + const char* name; + __eglMustCastToProperFunctionPointerType address; +}; + +/* + * This is the list of EGL extensions exposed to applications. + * + * Some of them (gBuiltinExtensionString) are implemented entirely in this EGL + * wrapper and are always available. + * + * The rest (gExtensionString) depend on support in the EGL driver, and are + * only available if the driver supports them. However, some of these must be + * supported because they are used by the Android system itself; these are + * listed as mandatory below and are required by the CDD. The system *assumes* + * the mandatory extensions are present and may not function properly if some + * are missing. + * + * NOTE: Both strings MUST have a single space as the last character. + */ + +extern char const * const gBuiltinExtensionString; +extern char const * const gExtensionString; + +// clang-format off +// Extensions implemented by the EGL wrapper. +char const * const gBuiltinExtensionString = + "EGL_KHR_get_all_proc_addresses " + "EGL_ANDROID_presentation_time " + "EGL_KHR_swap_buffers_with_damage " + "EGL_ANDROID_get_native_client_buffer " + "EGL_ANDROID_front_buffer_auto_refresh " + "EGL_ANDROID_get_frame_timestamps " + "EGL_EXT_surface_SMPTE2086_metadata " + "EGL_EXT_surface_CTA861_3_metadata " + ; + +// Whitelist of extensions exposed to applications if implemented in the vendor driver. +char const * const gExtensionString = + "EGL_KHR_image " // mandatory + "EGL_KHR_image_base " // mandatory + "EGL_EXT_image_gl_colorspace " + "EGL_KHR_image_pixmap " + "EGL_KHR_lock_surface " + "EGL_KHR_gl_colorspace " + "EGL_KHR_gl_texture_2D_image " + "EGL_KHR_gl_texture_3D_image " + "EGL_KHR_gl_texture_cubemap_image " + "EGL_KHR_gl_renderbuffer_image " + "EGL_KHR_reusable_sync " + "EGL_KHR_fence_sync " + "EGL_KHR_create_context " + "EGL_KHR_config_attribs " + "EGL_KHR_surfaceless_context " + "EGL_KHR_stream " + "EGL_KHR_stream_fifo " + "EGL_KHR_stream_producer_eglsurface " + "EGL_KHR_stream_consumer_gltexture " + "EGL_KHR_stream_cross_process_fd " + "EGL_EXT_create_context_robustness " + "EGL_NV_system_time " + "EGL_ANDROID_image_native_buffer " // mandatory + "EGL_KHR_wait_sync " // strongly recommended + "EGL_ANDROID_recordable " // mandatory + "EGL_KHR_partial_update " // strongly recommended + "EGL_EXT_pixel_format_float " + "EGL_EXT_buffer_age " // strongly recommended with partial_update + "EGL_KHR_create_context_no_error " + "EGL_KHR_mutable_render_buffer " + "EGL_EXT_yuv_surface " + "EGL_EXT_protected_content " + "EGL_IMG_context_priority " + "EGL_KHR_no_config_context " + ; + +char const * const gClientExtensionString = + "EGL_EXT_client_extensions " + "EGL_KHR_platform_android " + "EGL_ANGLE_platform_angle"; +// clang-format on + +// extensions not exposed to applications but used by the ANDROID system +// "EGL_ANDROID_blob_cache " // strongly recommended +// "EGL_IMG_hibernate_process " // optional +// "EGL_ANDROID_native_fence_sync " // strongly recommended +// "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 + +/* + * EGL Extensions entry-points exposed to 3rd party applications + * (keep in sync with gExtensionString above) + * + */ +static const extention_map_t sExtensionMap[] = { + // EGL_KHR_lock_surface + { "eglLockSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, + { "eglUnlockSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, + + // EGL_KHR_image, EGL_KHR_image_base + { "eglCreateImageKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, + { "eglDestroyImageKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, + + // EGL_KHR_reusable_sync, EGL_KHR_fence_sync + { "eglCreateSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR }, + { "eglDestroySyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR }, + { "eglClientWaitSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR }, + { "eglSignalSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR }, + { "eglGetSyncAttribKHR", + (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR }, + + // EGL_NV_system_time + { "eglGetSystemTimeFrequencyNV", + (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, + { "eglGetSystemTimeNV", + (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, + + // EGL_KHR_wait_sync + { "eglWaitSyncKHR", + (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR }, + + // EGL_ANDROID_presentation_time + { "eglPresentationTimeANDROID", + (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID }, + + // EGL_KHR_swap_buffers_with_damage + { "eglSwapBuffersWithDamageKHR", + (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR }, + + // EGL_ANDROID_get_native_client_buffer + { "eglGetNativeClientBufferANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID }, + + // EGL_KHR_partial_update + { "eglSetDamageRegionKHR", + (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR }, + + { "eglCreateStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR }, + { "eglDestroyStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR }, + { "eglStreamAttribKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR }, + { "eglQueryStreamKHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR }, + { "eglQueryStreamu64KHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR }, + { "eglQueryStreamTimeKHR", + (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR }, + { "eglCreateStreamProducerSurfaceKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR }, + { "eglStreamConsumerGLTextureExternalKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR }, + { "eglStreamConsumerAcquireKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR }, + { "eglStreamConsumerReleaseKHR", + (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR }, + { "eglGetStreamFileDescriptorKHR", + (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, + { "eglCreateStreamFromFileDescriptorKHR", + (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, + + // EGL_ANDROID_get_frame_timestamps + { "eglGetNextFrameIdANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, + { "eglGetCompositorTimingANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, + { "eglGetCompositorTimingSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, + { "eglGetFrameTimestampsANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, + { "eglGetFrameTimestampSupportedANDROID", + (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, + + // EGL_ANDROID_native_fence_sync + { "eglDupNativeFenceFDANDROID", + (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID }, +}; + +/* + * These extensions entry-points should not be exposed to applications. + * They're used internally by the Android EGL layer. + */ +#define FILTER_EXTENSIONS(procname) \ + (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \ + !strcmp((procname), "eglHibernateProcessIMG") || \ + !strcmp((procname), "eglAwakenProcessIMG")) + +// accesses protected by sExtensionMapMutex +static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtentionMap; + +static int sGLExtentionSlot = 0; +static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; + +static void(*findProcAddress(const char* name, + const extention_map_t* map, size_t n))() { + for (uint32_t i=0 ; i<n ; i++) { + if (!strcmp(name, map[i].name)) { + return map[i].address; + } + } + return nullptr; +} + +// ---------------------------------------------------------------------------- + +extern void setGLHooksThreadSpecific(gl_hooks_t const *value); +extern EGLBoolean egl_init_drivers(); +extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; +extern gl_hooks_t gHooksTrace; + +// ---------------------------------------------------------------------------- + +static inline EGLContext getContext() { return egl_tls_t::getContext(); } + +// ---------------------------------------------------------------------------- + +static EGLDisplay eglGetPlatformDisplayTmpl(EGLenum platform, EGLNativeDisplayType display, + const EGLAttrib* attrib_list) { + if (platform != EGL_PLATFORM_ANDROID_KHR) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } + + uintptr_t index = reinterpret_cast<uintptr_t>(display); + if (index >= NUM_DISPLAYS) { + return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); + } + + EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display, attrib_list); + return dpy; +} + +EGLDisplay eglGetDisplayImpl(EGLNativeDisplayType display) { + return eglGetPlatformDisplayTmpl(EGL_PLATFORM_ANDROID_KHR, display, nullptr); +} + +EGLDisplay eglGetPlatformDisplayImpl(EGLenum platform, void* native_display, + const EGLAttrib* attrib_list) { + return eglGetPlatformDisplayTmpl(platform, static_cast<EGLNativeDisplayType>(native_display), + attrib_list); +} + +// ---------------------------------------------------------------------------- +// Initialization +// ---------------------------------------------------------------------------- + +EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor) +{ + egl_display_ptr dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + EGLBoolean res = dp->initialize(major, minor); + + return res; +} + +EGLBoolean eglTerminateImpl(EGLDisplay dpy) +{ + // NOTE: don't unload the drivers b/c some APIs can be called + // after eglTerminate() has been called. eglTerminate() only + // terminates an EGLDisplay, not a EGL itself. + + egl_display_ptr dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + EGLBoolean res = dp->terminate(); + + return res; +} + +// ---------------------------------------------------------------------------- +// configuration +// ---------------------------------------------------------------------------- + +EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, + EGLConfig *configs, + EGLint config_size, EGLint *num_config) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + if (num_config==nullptr) { + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + + EGLBoolean res = EGL_FALSE; + *num_config = 0; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + res = cnx->egl.eglGetConfigs( + dp->disp.dpy, configs, config_size, num_config); + } + + return res; +} + +EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list, + EGLConfig *configs, EGLint config_size, + EGLint *num_config) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + if (num_config==nullptr) { + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + + EGLBoolean res = EGL_FALSE; + *num_config = 0; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + if (attrib_list) { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.egl.force_msaa", value, "false"); + + if (!strcmp(value, "true")) { + size_t attribCount = 0; + EGLint attrib = attrib_list[0]; + + // Only enable MSAA if the context is OpenGL ES 2.0 and + // if no caveat is requested + const EGLint *attribRendererable = nullptr; + const EGLint *attribCaveat = nullptr; + + // Count the number of attributes and look for + // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT + while (attrib != EGL_NONE) { + attrib = attrib_list[attribCount]; + switch (attrib) { + case EGL_RENDERABLE_TYPE: + attribRendererable = &attrib_list[attribCount]; + break; + case EGL_CONFIG_CAVEAT: + attribCaveat = &attrib_list[attribCount]; + break; + default: + break; + } + attribCount++; + } + + if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && + (!attribCaveat || attribCaveat[1] != EGL_NONE)) { + + // Insert 2 extra attributes to force-enable MSAA 4x + EGLint aaAttribs[attribCount + 4]; + aaAttribs[0] = EGL_SAMPLE_BUFFERS; + aaAttribs[1] = 1; + aaAttribs[2] = EGL_SAMPLES; + aaAttribs[3] = 4; + + memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); + + EGLint numConfigAA; + EGLBoolean resAA = cnx->egl.eglChooseConfig( + dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); + + if (resAA == EGL_TRUE && numConfigAA > 0) { + ALOGD("Enabling MSAA 4x"); + *num_config = numConfigAA; + return resAA; + } + } + } + } + + res = cnx->egl.eglChooseConfig( + dp->disp.dpy, attrib_list, configs, config_size, num_config); + } + return res; +} + +EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, + EGLint attribute, EGLint *value) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (!dp) return EGL_FALSE; + + return cnx->egl.eglGetConfigAttrib( + dp->disp.dpy, config, attribute, value); +} + +// ---------------------------------------------------------------------------- +// surfaces +// ---------------------------------------------------------------------------- + +// Translates EGL color spaces to Android data spaces. +static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { + if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { + return HAL_DATASPACE_UNKNOWN; + } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { + return HAL_DATASPACE_V0_SRGB; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { + return HAL_DATASPACE_DISPLAY_P3; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT) { + return HAL_DATASPACE_DISPLAY_P3_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT) { + return HAL_DATASPACE_DISPLAY_P3; + } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_EXT) { + return HAL_DATASPACE_V0_SCRGB; + } else if (colorspace == EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT) { + return HAL_DATASPACE_V0_SCRGB_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_BT2020_LINEAR_EXT) { + return HAL_DATASPACE_BT2020_LINEAR; + } else if (colorspace == EGL_GL_COLORSPACE_BT2020_PQ_EXT) { + return HAL_DATASPACE_BT2020_PQ; + } + return HAL_DATASPACE_UNKNOWN; +} + +// Get the colorspace value that should be reported from queries. When the colorspace +// is unknown (no attribute passed), default to reporting LINEAR. +static EGLint getReportedColorSpace(EGLint colorspace) { + return colorspace == EGL_UNKNOWN ? EGL_GL_COLORSPACE_LINEAR_KHR : colorspace; +} + +// Returns a list of color spaces understood by the vendor EGL driver. +static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { + std::vector<EGLint> colorSpaces; + + // sRGB and linear are always supported when color space support is present. + colorSpaces.push_back(EGL_GL_COLORSPACE_SRGB_KHR); + colorSpaces.push_back(EGL_GL_COLORSPACE_LINEAR_KHR); + + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_scrgb_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_LINEAR_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_bt2020_pq")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_BT2020_PQ_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT); + } + if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) { + colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT); + } + return colorSpaces; +} + +// Cleans up color space related parameters that the driver does not understand. +// If there is no color space attribute in attrib_list, colorSpace is left +// unmodified. +template <typename AttrType> +static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window, + const AttrType* attrib_list, EGLint* colorSpace, + std::vector<AttrType>* strippedAttribList) { + for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { + bool copyAttribute = true; + if (attr[0] == EGL_GL_COLORSPACE_KHR) { + switch (attr[1]) { + case EGL_GL_COLORSPACE_LINEAR_KHR: + case EGL_GL_COLORSPACE_SRGB_KHR: + case EGL_GL_COLORSPACE_DISPLAY_P3_EXT: + case EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT: + case EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT: + case EGL_GL_COLORSPACE_SCRGB_EXT: + case EGL_GL_COLORSPACE_BT2020_LINEAR_EXT: + case EGL_GL_COLORSPACE_BT2020_PQ_EXT: + case EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT: + // Fail immediately if the driver doesn't have color space support at all. + if (!dp->hasColorSpaceSupport) return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); + break; + default: + // BAD_ATTRIBUTE if attr is not any of the EGL_GL_COLORSPACE_* + return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); + } + *colorSpace = static_cast<EGLint>(attr[1]); + + // Strip the attribute if the driver doesn't understand it. + copyAttribute = false; + std::vector<EGLint> driverColorSpaces = getDriverColorSpaces(dp); + for (auto driverColorSpace : driverColorSpaces) { + if (static_cast<EGLint>(attr[1]) == driverColorSpace) { + copyAttribute = true; + break; + } + } + + // If the driver doesn't understand it, we should map sRGB-encoded P3 to + // sRGB rather than just dropping the colorspace on the floor. + // For this format, the driver is expected to apply the sRGB + // transfer function during framebuffer operations. + if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) { + strippedAttribList->push_back(attr[0]); + strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR); + } + } + if (copyAttribute) { + strippedAttribList->push_back(attr[0]); + strippedAttribList->push_back(attr[1]); + } + } + // Terminate the attribute list. + strippedAttribList->push_back(EGL_NONE); + + // If the passed color space has wide color gamut, check whether the target native window + // supports wide color. + const bool colorSpaceIsNarrow = *colorSpace == EGL_GL_COLORSPACE_SRGB_KHR || + *colorSpace == EGL_GL_COLORSPACE_LINEAR_KHR || *colorSpace == EGL_UNKNOWN; + if (window && !colorSpaceIsNarrow) { + bool windowSupportsWideColor = true; + // Ordinarily we'd put a call to native_window_get_wide_color_support + // at the beginning of the function so that we'll have the + // result when needed elsewhere in the function. + // However, because eglCreateWindowSurface is called by SurfaceFlinger and + // SurfaceFlinger is required to answer the call below we would + // end up in a deadlock situation. By moving the call to only happen + // if the application has specifically asked for wide-color we avoid + // the deadlock with SurfaceFlinger since it will not ask for a + // wide-color surface. + int err = native_window_get_wide_color_support(window, &windowSupportsWideColor); + + if (err) { + ALOGE("processAttributes: invalid window (win=%p) " + "failed (%#x) (already connected to another API?)", + window, err); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_FALSE); + } + if (!windowSupportsWideColor) { + // Application has asked for a wide-color colorspace but + // wide-color support isn't available on the display the window is on. + return setError(EGL_BAD_MATCH, EGL_FALSE); + } + } + return true; +} + +// Note: This only works for existing GLenum's that are all 32bits. +// If you have 64bit attributes (e.g. pointers) you shouldn't be calling this. +void convertAttribs(const EGLAttrib* attribList, std::vector<EGLint>& newList) { + for (const EGLAttrib* attr = attribList; attr && attr[0] != EGL_NONE; attr += 2) { + newList.push_back(static_cast<EGLint>(attr[0])); + newList.push_back(static_cast<EGLint>(attr[1])); + } + newList.push_back(EGL_NONE); +} + +// Gets the native pixel format corrsponding to the passed EGLConfig. +void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config, + android_pixel_format* format) { + // Set the native window's buffers format to match what this config requests. + // Whether to use sRGB gamma is not part of the EGLconfig, but is part + // of our native format. So if sRGB gamma is requested, we have to + // modify the EGLconfig's format before setting the native window's + // format. + + EGLint componentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_COLOR_COMPONENT_TYPE_EXT, &componentType); + + EGLint a = 0; + EGLint r, g, b; + r = g = b = 0; + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b); + cnx->egl.eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a); + EGLint colorDepth = r + g + b; + + // Today, the driver only understands sRGB and linear on 888X + // formats. Strip other colorspaces from the attribute list and + // only use them to set the dataspace via + // native_window_set_buffers_dataspace + // if pixel format is RGBX 8888 + // TBD: Can test for future extensions that indicate that driver + // handles requested color space and we can let it through. + // allow SRGB and LINEAR. All others need to be stripped. + // else if 565, 4444 + // TBD: Can we assume these are supported if 8888 is? + // else if FP16 or 1010102 + // strip colorspace from attribs. + // endif + if (a == 0) { + if (colorDepth <= 16) { + *format = HAL_PIXEL_FORMAT_RGB_565; + } else { + if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { + if (colorDepth > 24) { + *format = HAL_PIXEL_FORMAT_RGBA_1010102; + } else { + *format = HAL_PIXEL_FORMAT_RGBX_8888; + } + } else { + *format = HAL_PIXEL_FORMAT_RGBA_FP16; + } + } + } else { + if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) { + if (colorDepth > 24) { + *format = HAL_PIXEL_FORMAT_RGBA_1010102; + } else { + *format = HAL_PIXEL_FORMAT_RGBA_8888; + } + } else { + *format = HAL_PIXEL_FORMAT_RGBA_FP16; + } + } +} + +EGLBoolean sendSurfaceMetadata(egl_surface_t* s) { + android_smpte2086_metadata smpteMetadata; + if (s->getSmpte2086Metadata(smpteMetadata)) { + int err = + native_window_set_buffers_smpte2086_metadata(s->getNativeWindow(), &smpteMetadata); + s->resetSmpte2086Metadata(); + if (err != 0) { + ALOGE("error setting native window smpte2086 metadata: %s (%d)", strerror(-err), err); + return EGL_FALSE; + } + } + android_cta861_3_metadata cta8613Metadata; + if (s->getCta8613Metadata(cta8613Metadata)) { + int err = + native_window_set_buffers_cta861_3_metadata(s->getNativeWindow(), &cta8613Metadata); + s->resetCta8613Metadata(); + if (err != 0) { + ALOGE("error setting native window CTS 861.3 metadata: %s (%d)", strerror(-err), err); + return EGL_FALSE; + } + } + return EGL_TRUE; +} + +template <typename AttrType, typename CreateFuncType> +EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config, + ANativeWindow* window, const AttrType* attrib_list, + CreateFuncType createWindowSurfaceFunc) { + const AttrType* origAttribList = attrib_list; + + if (!window) { + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + int value = 0; + window->query(window, NATIVE_WINDOW_IS_VALID, &value); + if (!value) { + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + // NOTE: When using Vulkan backend, the Vulkan runtime makes all the + // native_window_* calls, so don't do them here. + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); + if (result < 0) { + ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) " + "failed (%#x) (already connected to another API?)", + window, result); + return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); + } + } + + EGLDisplay iDpy = dp->disp.dpy; + android_pixel_format format; + getNativePixelFormat(iDpy, cnx, config, &format); + + // now select correct colorspace and dataspace based on user's attribute list + EGLint colorSpace = EGL_UNKNOWN; + std::vector<AttrType> strippedAttribList; + if (!processAttributes<AttrType>(dp, window, attrib_list, &colorSpace, &strippedAttribList)) { + ALOGE("error invalid colorspace: %d", colorSpace); + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + } + return EGL_NO_SURFACE; + } + attrib_list = strippedAttribList.data(); + + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + int err = native_window_set_buffers_format(window, format); + if (err != 0) { + ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + + android_dataspace dataSpace = dataSpaceFromEGLColorSpace(colorSpace); + // Set dataSpace even if it could be HAL_DATASPACE_UNKNOWN. + // HAL_DATASPACE_UNKNOWN is the default value, but it may have changed + // at this point. + err = native_window_set_buffers_data_space(window, dataSpace); + if (err != 0) { + ALOGE("error setting native window pixel dataSpace: %s (%d)", strerror(-err), err); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); + } + } + + // the EGL spec requires that a new EGLSurface default to swap interval + // 1, so explicitly set that on the window here. + window->setSwapInterval(window, 1); + + EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface, + getReportedColorSpace(colorSpace), cnx); + return s; + } + + // EGLSurface creation failed + if (cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_set_buffers_format(window, 0); + native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL); + } + return EGL_NO_SURFACE; +} + +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay dpy, EGLConfig config, + NativeWindowType window, + const EGLint* attrib_list); +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)( + EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list); + +EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window, + const EGLint* attrib_list) { + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return eglCreateWindowSurfaceTmpl< + EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list, + cnx->egl.eglCreateWindowSurface); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window, + const EGLAttrib* attrib_list) { + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreatePlatformWindowSurface) { + return eglCreateWindowSurfaceTmpl<EGLAttrib, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC>( + dp, cnx, config, static_cast<ANativeWindow*>(native_window), attrib_list, + cnx->egl.eglCreatePlatformWindowSurface); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have " + "eglCreatePlatformWindowSurface"); + return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + if (cnx->egl.eglCreatePlatformWindowSurfaceEXT) { + return eglCreateWindowSurfaceTmpl<EGLint, PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>( + dp, cnx, config, static_cast<ANativeWindow*>(native_window), + convertedAttribs.data(), cnx->egl.eglCreatePlatformWindowSurfaceEXT); + } else { + return eglCreateWindowSurfaceTmpl< + EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, + static_cast<ANativeWindow*>( + native_window), + convertedAttribs.data(), + cnx->egl.eglCreateWindowSurface); + } + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/, + void* /*native_pixmap*/, + const EGLAttrib* /*attrib_list*/) { + // Per EGL_KHR_platform_android: + // It is not valid to call eglCreatePlatformPixmapSurface with a <dpy> that + // belongs to the Android platform. Any such call fails and generates + // an EGL_BAD_PARAMETER error. + + egl_connection_t* cnx = NULL; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/, + NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) { + egl_connection_t* cnx = nullptr; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + return EGL_NO_SURFACE; +} + +EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config, + const EGLint* attrib_list) { + egl_connection_t* cnx = nullptr; + egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + EGLDisplay iDpy = dp->disp.dpy; + android_pixel_format format; + getNativePixelFormat(iDpy, cnx, config, &format); + + // Select correct colorspace based on user's attribute list + EGLint colorSpace = EGL_UNKNOWN; + std::vector<EGLint> strippedAttribList; + if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) { + ALOGE("error invalid colorspace: %d", colorSpace); + return EGL_NO_SURFACE; + } + attrib_list = strippedAttribList.data(); + + EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface, + getReportedColorSpace(colorSpace), cnx); + return s; + } + } + return EGL_NO_SURFACE; +} + +EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t* const s = get_surface(surface); + EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface); + if (result == EGL_TRUE) { + _s.terminate(); + } + return result; +} + +EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute, + EGLint* value) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const* const s = get_surface(surface); + if (s->getColorSpaceAttribute(attribute, value)) { + return EGL_TRUE; + } else if (s->getSmpte2086Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->getCta8613Attribute(attribute, value)) { + return EGL_TRUE; + } + return s->cnx->egl.eglQuerySurface(dp->disp.dpy, s->surface, attribute, value); +} + +void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + } +} + +// ---------------------------------------------------------------------------- +// Contexts +// ---------------------------------------------------------------------------- + +EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, + EGLContext share_list, const EGLint *attrib_list) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (dp) { + if (share_list != EGL_NO_CONTEXT) { + if (!ContextRef(dp.get(), share_list).get()) { + return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT); + } + egl_context_t* const c = get_context(share_list); + share_list = c->context; + } + EGLContext context = cnx->egl.eglCreateContext( + dp->disp.dpy, config, share_list, attrib_list); + if (context != EGL_NO_CONTEXT) { + // figure out if it's a GLESv1 or GLESv2 + int version = 0; + if (attrib_list) { + while (*attrib_list != EGL_NONE) { + GLint attr = *attrib_list++; + GLint value = *attrib_list++; + if (attr == EGL_CONTEXT_CLIENT_VERSION) { + if (value == 1) { + version = egl_connection_t::GLESv1_INDEX; + } else if (value == 2 || value == 3) { + version = egl_connection_t::GLESv2_INDEX; + } + } + }; + } + egl_context_t* c = new egl_context_t(dpy, context, config, cnx, + version); + return c; + } + } + return EGL_NO_CONTEXT; +} + +EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) + return EGL_FALSE; + + ContextRef _c(dp.get(), ctx); + if (!_c.get()) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + egl_context_t * const c = get_context(ctx); + EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); + if (result == EGL_TRUE) { + _c.terminate(); + } + return result; +} + +EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw, + EGLSurface read, EGLContext ctx) +{ + egl_display_ptr dp = validate_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + + // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not + // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is + // a valid but uninitialized display. + if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || + (draw != EGL_NO_SURFACE) ) { + if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } + + // get a reference to the object passed in + ContextRef _c(dp.get(), ctx); + SurfaceRef _d(dp.get(), draw); + SurfaceRef _r(dp.get(), read); + + // validate the context (if not EGL_NO_CONTEXT) + if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { + // EGL_NO_CONTEXT is valid + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + } + + // these are the underlying implementation's object + EGLContext impl_ctx = EGL_NO_CONTEXT; + EGLSurface impl_draw = EGL_NO_SURFACE; + EGLSurface impl_read = EGL_NO_SURFACE; + + // these are our objects structs passed in + egl_context_t * c = nullptr; + egl_surface_t const * d = nullptr; + egl_surface_t const * r = nullptr; + + // these are the current objects structs + egl_context_t * cur_c = get_context(getContext()); + + if (ctx != EGL_NO_CONTEXT) { + c = get_context(ctx); + impl_ctx = c->context; + } else { + // no context given, use the implementation of the current context + if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) { + // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT); + return setError(EGL_BAD_MATCH, (EGLBoolean)EGL_FALSE); + } + if (cur_c == nullptr) { + // no current context + // not an error, there is just no current context. + return EGL_TRUE; + } + } + + // retrieve the underlying implementation's draw EGLSurface + if (draw != EGL_NO_SURFACE) { + if (!_d.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + d = get_surface(draw); + impl_draw = d->surface; + } + + // retrieve the underlying implementation's read EGLSurface + if (read != EGL_NO_SURFACE) { + if (!_r.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + r = get_surface(read); + impl_read = r->surface; + } + + + EGLBoolean result = dp->makeCurrent(c, cur_c, + draw, read, ctx, + impl_draw, impl_read, impl_ctx); + + if (result == EGL_TRUE) { + if (c) { + setGLHooksThreadSpecific(c->cnx->hooks[c->version]); + egl_tls_t::setContext(ctx); + _c.acquire(); + _r.acquire(); + _d.acquire(); + } else { + setGLHooksThreadSpecific(&gHooksNoContext); + egl_tls_t::setContext(EGL_NO_CONTEXT); + } + } else { + // this will ALOGE the error + egl_connection_t* const cnx = &gEGLImpl; + result = setError(cnx->egl.eglGetError(), (EGLBoolean)EGL_FALSE); + } + return result; +} + +EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx, + EGLint attribute, EGLint *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + ContextRef _c(dp.get(), ctx); + if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + egl_context_t * const c = get_context(ctx); + return c->cnx->egl.eglQueryContext( + dp->disp.dpy, c->context, attribute, value); + +} + +EGLContext eglGetCurrentContextImpl(void) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_CONTEXT. + EGLContext ctx = getContext(); + return ctx; +} + +EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_SURFACE. + + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); + switch (readdraw) { + case EGL_READ: return c->read; + case EGL_DRAW: return c->draw; + default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + } + } + return EGL_NO_SURFACE; +} + +EGLDisplay eglGetCurrentDisplayImpl(void) +{ + // could be called before eglInitialize(), but we wouldn't have a context + // then, and this function would correctly return EGL_NO_DISPLAY. + + EGLContext ctx = getContext(); + if (ctx) { + egl_context_t const * const c = get_context(ctx); + if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); + return c->dpy; + } + return EGL_NO_DISPLAY; +} + +EGLBoolean eglWaitGLImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + return cnx->egl.eglWaitGL(); +} + +EGLBoolean eglWaitNativeImpl(EGLint engine) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + return cnx->egl.eglWaitNative(engine); +} + +EGLint eglGetErrorImpl(void) +{ + EGLint err = EGL_SUCCESS; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso) { + err = cnx->egl.eglGetError(); + } + if (err == EGL_SUCCESS) { + err = egl_tls_t::getError(); + } + return err; +} + +static __eglMustCastToProperFunctionPointerType findBuiltinWrapper( + const char* procname) { + const egl_connection_t* cnx = &gEGLImpl; + void* proc = nullptr; + + proc = dlsym(cnx->libEgl, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + proc = dlsym(cnx->libGles2, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + proc = dlsym(cnx->libGles1, procname); + if (proc) return (__eglMustCastToProperFunctionPointerType)proc; + + return nullptr; +} + +__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname) +{ + if (FILTER_EXTENSIONS(procname)) { + return nullptr; + } + + __eglMustCastToProperFunctionPointerType addr; + addr = findProcAddress(procname, sExtensionMap, NELEM(sExtensionMap)); + if (addr) return addr; + + addr = findBuiltinWrapper(procname); + if (addr) return addr; + + // this protects accesses to sGLExtentionMap and sGLExtentionSlot + pthread_mutex_lock(&sExtensionMapMutex); + + /* + * Since eglGetProcAddress() is not associated to anything, it needs + * to return a function pointer that "works" regardless of what + * the current context is. + * + * For this reason, we return a "forwarder", a small stub that takes + * care of calling the function associated with the context + * currently bound. + * + * We first look for extensions we've already resolved, if we're seeing + * this extension for the first time, we go through all our + * implementations and call eglGetProcAddress() and record the + * result in the appropriate implementation hooks and return the + * address of the forwarder corresponding to that hook set. + * + */ + + const std::string name(procname); + + auto& extentionMap = sGLExtentionMap; + auto pos = extentionMap.find(name); + addr = (pos != extentionMap.end()) ? pos->second : nullptr; + const int slot = sGLExtentionSlot; + + ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS, + "no more slots for eglGetProcAddress(\"%s\")", + procname); + + egl_connection_t* const cnx = &gEGLImpl; + LayerLoader& layer_loader(LayerLoader::getInstance()); + + if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) { + + if (cnx->dso && cnx->egl.eglGetProcAddress) { + + // Extensions are independent of the bound context + addr = cnx->egl.eglGetProcAddress(procname); + if (addr) { + + // purposefully track the bottom of the stack in extensionMap + extentionMap[name] = addr; + + // Apply layers + addr = layer_loader.ApplyLayers(procname, addr); + + // Track the top most entry point + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + addr = gExtensionForwarders[slot]; + sGLExtentionSlot++; + } + } + + } else if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) { + + // We've seen this func before, but we tracked the bottom, so re-apply layers + // More layers might have been enabled + addr = layer_loader.ApplyLayers(procname, addr); + + // Track the top most entry point + cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + addr = gExtensionForwarders[slot]; + } + + pthread_mutex_unlock(&sExtensionMapMutex); + return addr; +} + +class FrameCompletionThread { +public: + + static void queueSync(EGLSyncKHR sync) { + static FrameCompletionThread thread; + + char name[64]; + + std::lock_guard<std::mutex> lock(thread.mMutex); + snprintf(name, sizeof(name), "kicked off frame %u", (unsigned int)thread.mFramesQueued); + ATRACE_NAME(name); + + thread.mQueue.push_back(sync); + thread.mCondition.notify_one(); + thread.mFramesQueued++; + ATRACE_INT("GPU Frames Outstanding", int32_t(thread.mQueue.size())); + } + +private: + + FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) { + std::thread thread(&FrameCompletionThread::loop, this); + thread.detach(); + } + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" + void loop() { + while (true) { + threadLoop(); + } + } +#pragma clang diagnostic pop + + void threadLoop() { + EGLSyncKHR sync; + uint32_t frameNum; + { + std::unique_lock<std::mutex> lock(mMutex); + while (mQueue.empty()) { + mCondition.wait(lock); + } + sync = mQueue[0]; + frameNum = mFramesCompleted; + } + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + { + char name[64]; + snprintf(name, sizeof(name), "waiting for frame %u", (unsigned int)frameNum); + ATRACE_NAME(name); + + EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR); + if (result == EGL_FALSE) { + ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("FrameCompletion: timeout waiting for fence"); + } + eglDestroySyncKHR(dpy, sync); + } + { + std::lock_guard<std::mutex> lock(mMutex); + mQueue.pop_front(); + mFramesCompleted++; + ATRACE_INT("GPU Frames Outstanding", int32_t(mQueue.size())); + } + } + + uint32_t mFramesQueued; + uint32_t mFramesCompleted; + std::deque<EGLSyncKHR> mQueue; + std::condition_variable mCondition; + std::mutex mMutex; +}; + +EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, + EGLint *rects, EGLint n_rects) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), draw); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t* const s = get_surface(draw); + + if (CC_UNLIKELY(dp->traceGpuCompletion)) { + EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr); + if (sync != EGL_NO_SYNC_KHR) { + FrameCompletionThread::queueSync(sync); + } + } + + if (CC_UNLIKELY(dp->finishOnSwap)) { + uint32_t pixel; + egl_context_t * const c = get_context( egl_tls_t::getContext() ); + if (c) { + // glReadPixels() ensures that the frame is complete + s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1, + GL_RGBA,GL_UNSIGNED_BYTE,&pixel); + } + } + + if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + if (!sendSurfaceMetadata(s)) { + native_window_api_disconnect(s->getNativeWindow(), NATIVE_WINDOW_API_EGL); + return setError(EGL_BAD_NATIVE_WINDOW, (EGLBoolean)EGL_FALSE); + } + } + + if (n_rects == 0) { + return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); + } + + std::vector<android_native_rect_t> androidRects((size_t)n_rects); + for (int r = 0; r < n_rects; ++r) { + int offset = r * 4; + int x = rects[offset]; + int y = rects[offset + 1]; + int width = rects[offset + 2]; + int height = rects[offset + 3]; + android_native_rect_t androidRect; + androidRect.left = x; + androidRect.top = y + height; + androidRect.right = x + width; + androidRect.bottom = y; + androidRects.push_back(androidRect); + } + if (s->cnx->angleBackend != EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { + native_window_set_surface_damage(s->getNativeWindow(), androidRects.data(), + androidRects.size()); + } + + if (s->cnx->egl.eglSwapBuffersWithDamageKHR) { + return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, + rects, n_rects); + } else { + return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); + } +} + +EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) +{ + return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0); +} + +EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface, + NativePixmapType target) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); +} + +const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) +{ + if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) { + // Return list of client extensions + return gClientExtensionString; + } + + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return (const char *) nullptr; + + switch (name) { + case EGL_VENDOR: + return dp->getVendorString(); + case EGL_VERSION: + return dp->getVersionString(); + case EGL_EXTENSIONS: + return dp->getExtensionString(); + case EGL_CLIENT_APIS: + return dp->getClientApiString(); + default: + break; + } + return setError(EGL_BAD_PARAMETER, (const char *)nullptr); +} + +EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return (const char *) nullptr; + + switch (name) { + case EGL_VENDOR: + return dp->disp.queryString.vendor; + case EGL_VERSION: + return dp->disp.queryString.version; + case EGL_EXTENSIONS: + return dp->disp.queryString.extensions; + case EGL_CLIENT_APIS: + return dp->disp.queryString.clientApi; + default: + break; + } + return setError(EGL_BAD_PARAMETER, (const char *)nullptr); +} + +// ---------------------------------------------------------------------------- +// EGL 1.1 +// ---------------------------------------------------------------------------- + +EGLBoolean eglSurfaceAttribImpl( + EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t * const s = get_surface(surface); + + if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) { + if (!s->getNativeWindow()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + } + int err = native_window_set_auto_refresh(s->getNativeWindow(), value != 0); + return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + if (attribute == EGL_TIMESTAMPS_ANDROID) { + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + int err = native_window_enable_frame_timestamps(s->getNativeWindow(), value != 0); + return (err == 0) ? EGL_TRUE : setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + if (s->setSmpte2086Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->setCta8613Attribute(attribute, value)) { + return EGL_TRUE; + } else if (s->cnx->egl.eglSurfaceAttrib) { + return s->cnx->egl.eglSurfaceAttrib( + dp->disp.dpy, s->surface, attribute, value); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglBindTexImageImpl( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglBindTexImage) { + return s->cnx->egl.eglBindTexImage( + dp->disp.dpy, s->surface, buffer); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglReleaseTexImageImpl( + EGLDisplay dpy, EGLSurface surface, EGLint buffer) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglReleaseTexImage) { + return s->cnx->egl.eglReleaseTexImage( + dp->disp.dpy, s->surface, buffer); + } + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean res = EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglSwapInterval) { + res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval); + } + + return res; +} + + +// ---------------------------------------------------------------------------- +// EGL 1.2 +// ---------------------------------------------------------------------------- + +EGLBoolean eglWaitClientImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (!cnx->dso) + return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + + EGLBoolean res; + if (cnx->egl.eglWaitClient) { + res = cnx->egl.eglWaitClient(); + } else { + res = cnx->egl.eglWaitGL(); + } + return res; +} + +EGLBoolean eglBindAPIImpl(EGLenum api) +{ + // bind this API on all EGLs + EGLBoolean res = EGL_TRUE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglBindAPI) { + res = cnx->egl.eglBindAPI(api); + } + return res; +} + +EGLenum eglQueryAPIImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryAPI) { + return cnx->egl.eglQueryAPI(); + } + + // or, it can only be OpenGL ES + return EGL_OPENGL_ES_API; +} + +EGLBoolean eglReleaseThreadImpl(void) +{ + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglReleaseThread) { + cnx->egl.eglReleaseThread(); + } + + // If there is context bound to the thread, release it + egl_display_t::loseCurrent(get_context(getContext())); + + egl_tls_t::clearTLS(); + return EGL_TRUE; +} + +EGLSurface eglCreatePbufferFromClientBufferImpl( + EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, + EGLConfig config, const EGLint *attrib_list) +{ + egl_connection_t* cnx = nullptr; + const egl_display_ptr dp = validate_display_connection(dpy, cnx); + if (!dp) return EGL_FALSE; + if (cnx->egl.eglCreatePbufferFromClientBuffer) { + return cnx->egl.eglCreatePbufferFromClientBuffer( + dp->disp.dpy, buftype, buffer, config, attrib_list); + } + return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 3 +// ---------------------------------------------------------------------------- + +EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, + const EGLint *attrib_list) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglLockSurfaceKHR) { + return s->cnx->egl.eglLockSurfaceKHR( + dp->disp.dpy, s->surface, attrib_list); + } + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); +} + +EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglUnlockSurfaceKHR) { + return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); + } + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); +} + +// Note: EGLImageKHR and EGLImage are the same thing so no need +// to templatize that. +template <typename AttrType, typename FuncType> +EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const AttrType* attrib_list, + FuncType eglCreateImageFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_IMAGE_KHR; + + ContextRef _c(dp.get(), ctx); + egl_context_t* const c = _c.get(); + + EGLImageKHR result = EGL_NO_IMAGE_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglCreateImageFunc) { + result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer, + attrib_list); + } + return result; +} + +typedef EGLImage(EGLAPIENTRYP PFNEGLCREATEIMAGE)(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, + const EGLAttrib* attrib_list); + +EGLImageKHR eglCreateImageKHRImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, + EGLClientBuffer buffer, const EGLint* attrib_list) { + return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer, + attrib_list, + gEGLImpl.egl.eglCreateImageKHR); +} + +EGLImage eglCreateImageImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, + const EGLAttrib* attrib_list) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreateImage) { + return eglCreateImageTmpl<EGLAttrib, PFNEGLCREATEIMAGE>(dpy, ctx, target, buffer, + attrib_list, + cnx->egl.eglCreateImage); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateImage"); + return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + return eglCreateImageTmpl<EGLint, PFNEGLCREATEIMAGEKHRPROC>(dpy, ctx, target, buffer, + convertedAttribs.data(), + gEGLImpl.egl.eglCreateImageKHR); +} + +EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img, + PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && destroyImageFunc) { + result = destroyImageFunc(dp->disp.dpy, img); + } + return result; +} + +EGLBoolean eglDestroyImageKHRImpl(EGLDisplay dpy, EGLImageKHR img) { + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR); +} + +EGLBoolean eglDestroyImageImpl(EGLDisplay dpy, EGLImageKHR img) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglDestroyImage) { + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImage); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroyImage"); + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + return eglDestroyImageTmpl(dpy, img, gEGLImpl.egl.eglDestroyImageKHR); +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 5 +// ---------------------------------------------------------------------------- + +// NOTE: EGLSyncKHR and EGLSync are identical, no need to templatize +template <typename AttrType, typename FuncType> +EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list, + FuncType eglCreateSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_SYNC_KHR; + + egl_connection_t* const cnx = &gEGLImpl; + EGLSyncKHR result = EGL_NO_SYNC_KHR; + if (cnx->dso && eglCreateSyncFunc) { + result = eglCreateSyncFunc(dp->disp.dpy, type, attrib_list); + } + return result; +} + +typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATESYNC)(EGLDisplay dpy, EGLenum type, + const EGLAttrib* attrib_list); + +EGLSyncKHR eglCreateSyncKHRImpl(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list) { + return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, attrib_list, + gEGLImpl.egl.eglCreateSyncKHR); +} + +EGLSync eglCreateSyncImpl(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_list) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglCreateSync) { + return eglCreateSyncTmpl<EGLAttrib, PFNEGLCREATESYNC>(dpy, type, attrib_list, + cnx->egl.eglCreateSync); + } + // driver doesn't support native function, return EGL_BAD_DISPLAY + ALOGE("Driver indicates EGL 1.5 support, but does not have eglCreateSync"); + return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC); + } + + std::vector<EGLint> convertedAttribs; + convertAttribs(attrib_list, convertedAttribs); + return eglCreateSyncTmpl<EGLint, PFNEGLCREATESYNCKHRPROC>(dpy, type, convertedAttribs.data(), + cnx->egl.eglCreateSyncKHR); +} + +EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, + PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglDestroySyncFunc) { + result = eglDestroySyncFunc(dp->disp.dpy, sync); + } + return result; +} + +EGLBoolean eglDestroySyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync) { + return eglDestroySyncTmpl(dpy, sync, gEGLImpl.egl.eglDestroySyncKHR); +} + +EGLBoolean eglDestroySyncImpl(EGLDisplay dpy, EGLSyncKHR sync) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglDestroySync) { + return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySync); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglDestroySync"); + return setError(EGL_BAD_DISPLAY, EGL_FALSE); + } + + return eglDestroySyncTmpl(dpy, sync, cnx->egl.eglDestroySyncKHR); +} + +EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && gEGLImpl.egl.eglSignalSyncKHR) { + result = gEGLImpl.egl.eglSignalSyncKHR(dp->disp.dpy, sync, mode); + } + return result; +} + +EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout, + PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLint result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglClientWaitSyncFunc) { + result = eglClientWaitSyncFunc(dp->disp.dpy, sync, flags, timeout); + } + return result; +} + +EGLint eglClientWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { + egl_connection_t* const cnx = &gEGLImpl; + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR); +} + +EGLint eglClientWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglClientWaitSync) { + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSync); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglClientWaitSync"); + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + return eglClientWaitSyncTmpl(dpy, sync, flags, timeout, cnx->egl.eglClientWaitSyncKHR); +} + +template <typename AttrType, typename FuncType> +EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value, + FuncType eglGetSyncAttribFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglGetSyncAttribFunc) { + result = eglGetSyncAttribFunc(dp->disp.dpy, sync, attribute, value); + } + return result; +} + +typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETSYNCATTRIB)(EGLDisplay dpy, EGLSync sync, EGLint attribute, + EGLAttrib* value); + +EGLBoolean eglGetSyncAttribImpl(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib* value) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglGetSyncAttrib) { + return eglGetSyncAttribTmpl<EGLAttrib, PFNEGLGETSYNCATTRIB>(dpy, sync, attribute, value, + cnx->egl.eglGetSyncAttrib); + } + ALOGE("Driver indicates EGL 1.5 support, but does not have eglGetSyncAttrib"); + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + // Fallback to KHR, ask for EGLint attribute and cast back to EGLAttrib + EGLint attribValue; + EGLBoolean ret = + eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, + &attribValue, + gEGLImpl.egl + .eglGetSyncAttribKHR); + if (ret) { + *value = static_cast<EGLAttrib>(attribValue); + } + return ret; +} + +EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, + EGLint* value) { + return eglGetSyncAttribTmpl<EGLint, PFNEGLGETSYNCATTRIBKHRPROC>(dpy, sync, attribute, value, + gEGLImpl.egl + .eglGetSyncAttribKHR); +} + +EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_STREAM_KHR; + + EGLStreamKHR result = EGL_NO_STREAM_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamKHR) { + result = cnx->egl.eglCreateStreamKHR( + dp->disp.dpy, attrib_list); + } + return result; +} + +EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglDestroyStreamKHR) { + result = cnx->egl.eglDestroyStreamKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLint value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamAttribKHR) { + result = cnx->egl.eglStreamAttribKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLint *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamKHR) { + result = cnx->egl.eglQueryStreamKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLuint64KHR *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) { + result = cnx->egl.eglQueryStreamu64KHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, + EGLenum attribute, EGLTimeKHR *value) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) { + result = cnx->egl.eglQueryStreamTimeKHR( + dp->disp.dpy, stream, attribute, value); + } + return result; +} + +EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config, + EGLStreamKHR stream, const EGLint *attrib_list) +{ + egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_SURFACE; + + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) { + EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR( + dp->disp.dpy, config, stream, attrib_list); + if (surface != EGL_NO_SURFACE) { + egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface, + EGL_GL_COLORSPACE_LINEAR_KHR, cnx); + return s; + } + } + return EGL_NO_SURFACE; +} + +EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) { + result = cnx->egl.eglStreamConsumerGLTextureExternalKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) { + result = cnx->egl.eglStreamConsumerAcquireKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, + EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + + EGLBoolean result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) { + result = cnx->egl.eglStreamConsumerReleaseKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl( + EGLDisplay dpy, EGLStreamKHR stream) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR; + + EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) { + result = cnx->egl.eglGetStreamFileDescriptorKHR( + dp->disp.dpy, stream); + } + return result; +} + +EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl( + EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_STREAM_KHR; + + EGLStreamKHR result = EGL_NO_STREAM_KHR; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) { + result = cnx->egl.eglCreateStreamFromFileDescriptorKHR( + dp->disp.dpy, file_descriptor); + } + return result; +} + +// ---------------------------------------------------------------------------- +// EGL_EGLEXT_VERSION 15 +// ---------------------------------------------------------------------------- + +// Need to template function type because return type is different +template <typename ReturnType, typename FuncType> +ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, + FuncType eglWaitSyncFunc) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_FALSE; + ReturnType result = EGL_FALSE; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && eglWaitSyncFunc) { + result = eglWaitSyncFunc(dp->disp.dpy, sync, flags); + } + return result; +} + +typedef EGLBoolean(EGLAPIENTRYP PFNEGLWAITSYNC)(EGLDisplay dpy, EGLSync sync, EGLint flags); + +EGLint eglWaitSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags) { + egl_connection_t* const cnx = &gEGLImpl; + return eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags, + cnx->egl.eglWaitSyncKHR); +} + +EGLBoolean eglWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags) { + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { + if (cnx->egl.eglWaitSync) { + return eglWaitSyncTmpl<EGLBoolean, PFNEGLWAITSYNC>(dpy, sync, flags, + cnx->egl.eglWaitSync); + } + return setError(EGL_BAD_DISPLAY, (EGLint)EGL_FALSE); + } + + return static_cast<EGLBoolean>( + eglWaitSyncTmpl<EGLint, PFNEGLWAITSYNCKHRPROC>(dpy, sync, flags, + cnx->egl.eglWaitSyncKHR)); +} + +// ---------------------------------------------------------------------------- +// ANDROID extensions +// ---------------------------------------------------------------------------- + +EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; + + EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; + egl_connection_t* const cnx = &gEGLImpl; + if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) { + result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync); + } + return result; +} + +EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLnsecsANDROID time) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + egl_surface_t const * const s = get_surface(surface); + native_window_set_buffers_timestamp(s->getNativeWindow(), time); + + return EGL_TRUE; +} + +EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) { + // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus + // this function cannot be implemented when this libEGL is built for + // vendors. +#ifndef __ANDROID_VNDK__ + if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); + return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer)); +#else + return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); +#endif +} + +// ---------------------------------------------------------------------------- +// NVIDIA extensions +// ---------------------------------------------------------------------------- +EGLuint64NV eglGetSystemTimeFrequencyNVImpl() +{ + EGLuint64NV ret = 0; + egl_connection_t* const cnx = &gEGLImpl; + + if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) { + return cnx->egl.eglGetSystemTimeFrequencyNV(); + } + + return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); +} + +EGLuint64NV eglGetSystemTimeNVImpl() +{ + EGLuint64NV ret = 0; + egl_connection_t* const cnx = &gEGLImpl; + + if (cnx->dso && cnx->egl.eglGetSystemTimeNV) { + return cnx->egl.eglGetSystemTimeNV(); + } + + return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); +} + +// ---------------------------------------------------------------------------- +// Partial update extension +// ---------------------------------------------------------------------------- +EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, + EGLint *rects, EGLint n_rects) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + setError(EGL_BAD_DISPLAY, EGL_FALSE); + return EGL_FALSE; + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + setError(EGL_BAD_SURFACE, EGL_FALSE); + return EGL_FALSE; + } + + egl_surface_t const * const s = get_surface(surface); + if (s->cnx->egl.eglSetDamageRegionKHR) { + return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, + rects, n_rects); + } + + return EGL_FALSE; +} + +EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLuint64KHR *frameId) { + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + uint64_t nextFrameId = 0; + int ret = native_window_get_next_frame_id(s->getNativeWindow(), &nextFrameId); + + if (ret != 0) { + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetNextFrameId: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } + + *frameId = nextFrameId; + return EGL_TRUE; +} + +EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + nsecs_t* compositeDeadline = nullptr; + nsecs_t* compositeInterval = nullptr; + nsecs_t* compositeToPresentLatency = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (names[i]) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + compositeDeadline = &values[i]; + break; + case EGL_COMPOSITE_INTERVAL_ANDROID: + compositeInterval = &values[i]; + break; + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + compositeToPresentLatency = &values[i]; + break; + default: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + } + + int ret = native_window_get_compositor_timing(s->getNativeWindow(), + compositeDeadline, compositeInterval, compositeToPresentLatency); + + switch (ret) { + case 0: + return EGL_TRUE; + case -ENOSYS: + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetCompositorTiming: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } +} + +EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl( + EGLDisplay dpy, EGLSurface surface, EGLint name) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + ANativeWindow* window = s->getNativeWindow(); + if (!window) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + switch (name) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + return EGL_TRUE; + default: + return EGL_FALSE; + } +} + +EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, + EGLnsecsANDROID *values) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + if (!s->getNativeWindow()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + nsecs_t* requestedPresentTime = nullptr; + nsecs_t* acquireTime = nullptr; + nsecs_t* latchTime = nullptr; + nsecs_t* firstRefreshStartTime = nullptr; + nsecs_t* gpuCompositionDoneTime = nullptr; + nsecs_t* lastRefreshStartTime = nullptr; + nsecs_t* displayPresentTime = nullptr; + nsecs_t* dequeueReadyTime = nullptr; + nsecs_t* releaseTime = nullptr; + + for (int i = 0; i < numTimestamps; i++) { + switch (timestamps[i]) { + case EGL_REQUESTED_PRESENT_TIME_ANDROID: + requestedPresentTime = &values[i]; + break; + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + acquireTime = &values[i]; + break; + case EGL_COMPOSITION_LATCH_TIME_ANDROID: + latchTime = &values[i]; + break; + case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: + firstRefreshStartTime = &values[i]; + break; + case EGL_LAST_COMPOSITION_START_TIME_ANDROID: + lastRefreshStartTime = &values[i]; + break; + case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: + gpuCompositionDoneTime = &values[i]; + break; + case EGL_DISPLAY_PRESENT_TIME_ANDROID: + displayPresentTime = &values[i]; + break; + case EGL_DEQUEUE_READY_TIME_ANDROID: + dequeueReadyTime = &values[i]; + break; + case EGL_READS_DONE_TIME_ANDROID: + releaseTime = &values[i]; + break; + default: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + } + } + + int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId, + requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime, + lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime, + dequeueReadyTime, releaseTime); + + switch (ret) { + case 0: + return EGL_TRUE; + case -ENOENT: + return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE); + case -ENOSYS: + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + case -EINVAL: + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetFrameTimestamps: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + } +} + +EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl( + EGLDisplay dpy, EGLSurface surface, EGLint timestamp) +{ + const egl_display_ptr dp = validate_display(dpy); + if (!dp) { + return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); + } + + SurfaceRef _s(dp.get(), surface); + if (!_s.get()) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + egl_surface_t const * const s = get_surface(surface); + + ANativeWindow* window = s->getNativeWindow(); + if (!window) { + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + } + + switch (timestamp) { + case EGL_COMPOSITE_DEADLINE_ANDROID: + case EGL_COMPOSITE_INTERVAL_ANDROID: + case EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID: + case EGL_REQUESTED_PRESENT_TIME_ANDROID: + case EGL_RENDERING_COMPLETE_TIME_ANDROID: + case EGL_COMPOSITION_LATCH_TIME_ANDROID: + case EGL_FIRST_COMPOSITION_START_TIME_ANDROID: + case EGL_LAST_COMPOSITION_START_TIME_ANDROID: + case EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID: + case EGL_DEQUEUE_READY_TIME_ANDROID: + case EGL_READS_DONE_TIME_ANDROID: + return EGL_TRUE; + case EGL_DISPLAY_PRESENT_TIME_ANDROID: { + int value = 0; + window->query(window, + NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value); + return value == 0 ? EGL_FALSE : EGL_TRUE; + } + default: + return EGL_FALSE; + } +} + +const GLubyte * glGetStringImpl(GLenum name) { + const GLubyte * ret = egl_get_string_for_current_context(name); + if (ret == NULL) { + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if(_c) ret = _c->glGetString(name); + } + return ret; +} + +const GLubyte * glGetStringiImpl(GLenum name, GLuint index) { + const GLubyte * ret = egl_get_string_for_current_context(name, index); + if (ret == NULL) { + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if(_c) ret = _c->glGetStringi(name, index); + } + return ret; +} + +void glGetBooleanvImpl(GLenum pname, GLboolean * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = num_exts > 0 ? GL_TRUE : GL_FALSE; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetBooleanv(pname, data); +} + +void glGetFloatvImpl(GLenum pname, GLfloat * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLfloat)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetFloatv(pname, data); +} + +void glGetIntegervImpl(GLenum pname, GLint * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLint)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetIntegerv(pname, data); +} + +void glGetInteger64vImpl(GLenum pname, GLint64 * data) { + if (pname == GL_NUM_EXTENSIONS) { + int num_exts = egl_get_num_extensions_for_current_context(); + if (num_exts >= 0) { + *data = (GLint64)num_exts; + return; + } + } + + gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + if (_c) _c->glGetInteger64v(pname, data); +} + +struct implementation_map_t { + const char* name; + EGLFuncPointer address; +}; + +static const implementation_map_t sPlatformImplMap[] = { + // clang-format off + { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl }, + { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl }, + { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl }, + { "eglTerminate", (EGLFuncPointer)&eglTerminateImpl }, + { "eglGetConfigs", (EGLFuncPointer)&eglGetConfigsImpl }, + { "eglChooseConfig", (EGLFuncPointer)&eglChooseConfigImpl }, + { "eglGetConfigAttrib", (EGLFuncPointer)&eglGetConfigAttribImpl }, + { "eglCreateWindowSurface", (EGLFuncPointer)&eglCreateWindowSurfaceImpl }, + { "eglCreatePixmapSurface", (EGLFuncPointer)&eglCreatePixmapSurfaceImpl }, + { "eglCreatePlatformWindowSurface", (EGLFuncPointer)&eglCreatePlatformWindowSurfaceImpl }, + { "eglCreatePlatformPixmapSurface", (EGLFuncPointer)&eglCreatePlatformPixmapSurfaceImpl }, + { "eglCreatePbufferSurface", (EGLFuncPointer)&eglCreatePbufferSurfaceImpl }, + { "eglDestroySurface", (EGLFuncPointer)&eglDestroySurfaceImpl }, + { "eglQuerySurface", (EGLFuncPointer)&eglQuerySurfaceImpl }, + { "eglBeginFrame", (EGLFuncPointer)&eglBeginFrameImpl }, + { "eglCreateContext", (EGLFuncPointer)&eglCreateContextImpl }, + { "eglDestroyContext", (EGLFuncPointer)&eglDestroyContextImpl }, + { "eglMakeCurrent", (EGLFuncPointer)&eglMakeCurrentImpl }, + { "eglQueryContext", (EGLFuncPointer)&eglQueryContextImpl }, + { "eglGetCurrentContext", (EGLFuncPointer)&eglGetCurrentContextImpl }, + { "eglGetCurrentSurface", (EGLFuncPointer)&eglGetCurrentSurfaceImpl }, + { "eglGetCurrentDisplay", (EGLFuncPointer)&eglGetCurrentDisplayImpl }, + { "eglWaitGL", (EGLFuncPointer)&eglWaitGLImpl }, + { "eglWaitNative", (EGLFuncPointer)&eglWaitNativeImpl }, + { "eglGetError", (EGLFuncPointer)&eglGetErrorImpl }, + { "eglSwapBuffersWithDamageKHR", (EGLFuncPointer)&eglSwapBuffersWithDamageKHRImpl }, + { "eglGetProcAddress", (EGLFuncPointer)&eglGetProcAddressImpl }, + { "eglSwapBuffers", (EGLFuncPointer)&eglSwapBuffersImpl }, + { "eglCopyBuffers", (EGLFuncPointer)&eglCopyBuffersImpl }, + { "eglQueryString", (EGLFuncPointer)&eglQueryStringImpl }, + { "eglQueryStringImplementationANDROID", (EGLFuncPointer)&eglQueryStringImplementationANDROIDImpl }, + { "eglSurfaceAttrib", (EGLFuncPointer)&eglSurfaceAttribImpl }, + { "eglBindTexImage", (EGLFuncPointer)&eglBindTexImageImpl }, + { "eglReleaseTexImage", (EGLFuncPointer)&eglReleaseTexImageImpl }, + { "eglSwapInterval", (EGLFuncPointer)&eglSwapIntervalImpl }, + { "eglWaitClient", (EGLFuncPointer)&eglWaitClientImpl }, + { "eglBindAPI", (EGLFuncPointer)&eglBindAPIImpl }, + { "eglQueryAPI", (EGLFuncPointer)&eglQueryAPIImpl }, + { "eglReleaseThread", (EGLFuncPointer)&eglReleaseThreadImpl }, + { "eglCreatePbufferFromClientBuffer", (EGLFuncPointer)&eglCreatePbufferFromClientBufferImpl }, + { "eglLockSurfaceKHR", (EGLFuncPointer)&eglLockSurfaceKHRImpl }, + { "eglUnlockSurfaceKHR", (EGLFuncPointer)&eglUnlockSurfaceKHRImpl }, + { "eglCreateImageKHR", (EGLFuncPointer)&eglCreateImageKHRImpl }, + { "eglDestroyImageKHR", (EGLFuncPointer)&eglDestroyImageKHRImpl }, + { "eglCreateImage", (EGLFuncPointer)&eglCreateImageImpl }, + { "eglDestroyImage", (EGLFuncPointer)&eglDestroyImageImpl }, + { "eglCreateSync", (EGLFuncPointer)&eglCreateSyncImpl }, + { "eglDestroySync", (EGLFuncPointer)&eglDestroySyncImpl }, + { "eglClientWaitSync", (EGLFuncPointer)&eglClientWaitSyncImpl }, + { "eglGetSyncAttrib", (EGLFuncPointer)&eglGetSyncAttribImpl }, + { "eglCreateSyncKHR", (EGLFuncPointer)&eglCreateSyncKHRImpl }, + { "eglDestroySyncKHR", (EGLFuncPointer)&eglDestroySyncKHRImpl }, + { "eglSignalSyncKHR", (EGLFuncPointer)&eglSignalSyncKHRImpl }, + { "eglClientWaitSyncKHR", (EGLFuncPointer)&eglClientWaitSyncKHRImpl }, + { "eglGetSyncAttribKHR", (EGLFuncPointer)&eglGetSyncAttribKHRImpl }, + { "eglCreateStreamKHR", (EGLFuncPointer)&eglCreateStreamKHRImpl }, + { "eglDestroyStreamKHR", (EGLFuncPointer)&eglDestroyStreamKHRImpl }, + { "eglStreamAttribKHR", (EGLFuncPointer)&eglStreamAttribKHRImpl }, + { "eglQueryStreamKHR", (EGLFuncPointer)&eglQueryStreamKHRImpl }, + { "eglQueryStreamu64KHR", (EGLFuncPointer)&eglQueryStreamu64KHRImpl }, + { "eglQueryStreamTimeKHR", (EGLFuncPointer)&eglQueryStreamTimeKHRImpl }, + { "eglCreateStreamProducerSurfaceKHR", (EGLFuncPointer)&eglCreateStreamProducerSurfaceKHRImpl }, + { "eglStreamConsumerGLTextureExternalKHR", (EGLFuncPointer)&eglStreamConsumerGLTextureExternalKHRImpl }, + { "eglStreamConsumerAcquireKHR", (EGLFuncPointer)&eglStreamConsumerAcquireKHRImpl }, + { "eglStreamConsumerReleaseKHR", (EGLFuncPointer)&eglStreamConsumerReleaseKHRImpl }, + { "eglGetStreamFileDescriptorKHR", (EGLFuncPointer)&eglGetStreamFileDescriptorKHRImpl }, + { "eglCreateStreamFromFileDescriptorKHR", (EGLFuncPointer)&eglCreateStreamFromFileDescriptorKHRImpl }, + { "eglWaitSync", (EGLFuncPointer)&eglWaitSyncImpl }, + { "eglWaitSyncKHR", (EGLFuncPointer)&eglWaitSyncKHRImpl }, + { "eglDupNativeFenceFDANDROID", (EGLFuncPointer)&eglDupNativeFenceFDANDROIDImpl }, + { "eglPresentationTimeANDROID", (EGLFuncPointer)&eglPresentationTimeANDROIDImpl }, + { "eglGetNativeClientBufferANDROID", (EGLFuncPointer)&eglGetNativeClientBufferANDROIDImpl }, + { "eglGetSystemTimeFrequencyNV", (EGLFuncPointer)&eglGetSystemTimeFrequencyNVImpl }, + { "eglGetSystemTimeNV", (EGLFuncPointer)&eglGetSystemTimeNVImpl }, + { "eglSetDamageRegionKHR", (EGLFuncPointer)&eglSetDamageRegionKHRImpl }, + { "eglGetNextFrameIdANDROID", (EGLFuncPointer)&eglGetNextFrameIdANDROIDImpl }, + { "eglGetCompositorTimingANDROID", (EGLFuncPointer)&eglGetCompositorTimingANDROIDImpl }, + { "eglGetCompositorTimingSupportedANDROID", (EGLFuncPointer)&eglGetCompositorTimingSupportedANDROIDImpl }, + { "eglGetFrameTimestampsANDROID", (EGLFuncPointer)&eglGetFrameTimestampsANDROIDImpl }, + { "eglGetFrameTimestampSupportedANDROID", (EGLFuncPointer)&eglGetFrameTimestampSupportedANDROIDImpl }, + { "glGetString", (EGLFuncPointer)&glGetStringImpl }, + { "glGetStringi", (EGLFuncPointer)&glGetStringiImpl }, + { "glGetBooleanv", (EGLFuncPointer)&glGetBooleanvImpl }, + { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl }, + { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl }, + { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl }, + // clang-format on +}; + +EGLFuncPointer FindPlatformImplAddr(const char* name) +{ + static const bool DEBUG = false; + + if (name == nullptr) { + ALOGV("FindPlatformImplAddr called with null name"); + return nullptr; + } + + for (int i = 0; i < NELEM(sPlatformImplMap); i++) { + if (sPlatformImplMap[i].name == nullptr) { + ALOGV("FindPlatformImplAddr found nullptr for sPlatformImplMap[%i].name (%s)", i, name); + return nullptr; + } + if (!strcmp(name, sPlatformImplMap[i].name)) { + ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name); + return sPlatformImplMap[i].address; + } + } + + ALOGV("FindPlatformImplAddr did not find an entry for %s", name); + return nullptr; +} +} // namespace android diff --git a/opengl/libs/EGL/egl_platform_entries.h b/opengl/libs/EGL/egl_platform_entries.h new file mode 100644 index 0000000000..85b1db3fae --- /dev/null +++ b/opengl/libs/EGL/egl_platform_entries.h @@ -0,0 +1,32 @@ +/* + * 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_EGLAPI_H +#define ANDROID_EGLAPI_H + +#include <EGL/egl.h> + +typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; + +namespace android { + +EGLint eglGetErrorImpl(); +EGLFuncPointer FindPlatformImplAddr(const char* name); + +}; // namespace android + +#endif // ANDROID_EGLAPI_H + diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp index 8508c5fe9d..aaecb62194 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_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED; 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 @@ const char *egl_tls_t::egl_strerror(EGLint err) { 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 @@ bool egl_tls_t::logNoContextCall() { 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 @@ void egl_tls_t::clearTLS() { 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::clearTLS() { 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 9feae681bd..86a375c002 100644 --- a/opengl/libs/EGL/egl_tls.h +++ b/opengl/libs/EGL/egl_tls.h @@ -38,6 +38,7 @@ class egl_tls_t { 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 985827655a..9e112cc034 100644 --- a/opengl/libs/EGL/egldefs.h +++ b/opengl/libs/EGL/egldefs.h @@ -18,9 +18,13 @@ #define ANDROID_EGLDEFS_H #include "../hooks.h" +#include "egl_platform_entries.h" + +#include <log/log.h> #define VERSION_MAJOR 1 #define VERSION_MINOR 4 +#define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch)) // ---------------------------------------------------------------------------- namespace android { @@ -31,23 +35,54 @@ const unsigned int NUM_DISPLAYS = 1; // ---------------------------------------------------------------------------- +extern char const * const platform_names[]; + +// clang-format off struct egl_connection_t { enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 }; - inline egl_connection_t() : dso(0) { } + inline egl_connection_t() : dso(nullptr) { + + char const* const* entries = platform_names; + EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform); + while (*entries) { + const char* name = *entries; + EGLFuncPointer f = FindPlatformImplAddr(name); + + if (f == nullptr) { + // If no entry found, update the lookup table: sPlatformImplMap + ALOGE("No entry found in platform lookup table for %s", name); + } + + *curr++ = f; + entries++; + } + } + void * dso; gl_hooks_t * hooks[2]; EGLint major; EGLint minor; + EGLint driverVersion; egl_t egl; + // Functions implemented or redirected by platform libraries + platform_impl_t platform; + void* libEgl; void* libGles1; void* libGles2; + + bool shouldUseAngle; // Should we attempt to load ANGLE + bool angleDecided; // Have we tried to load ANGLE + bool useAngle; // Was ANGLE successfully loaded + EGLint angleBackend; + void* vendorEGL; }; +// clang-format on // ---------------------------------------------------------------------------- @@ -58,6 +93,7 @@ extern "C" void gl_unimplemented(); extern "C" void gl_noop(); extern char const * const gl_names[]; +extern char const * const gl_names_1[]; extern char const * const egl_names[]; extern egl_connection_t gEGLImpl; diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp index f7fde9625f..65f50f54fb 100644 --- a/opengl/libs/GLES2/gl2.cpp +++ b/opengl/libs/GLES2/gl2.cpp @@ -301,71 +301,31 @@ extern "C" { } 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 a8855efa57..0af050175b 100644 --- a/opengl/libs/egl_impl.h +++ b/opengl/libs/egl_impl.h @@ -21,15 +21,18 @@ #include <EGL/eglext.h> #include <EGL/eglplatform.h> +#include "EGL/egldefs.h" #include "hooks.h" // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- + EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name); EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index); EGLAPI GLint egl_get_num_extensions_for_current_context(); +EGLAPI egl_connection_t* egl_get_connection(); // ---------------------------------------------------------------------------- }; // namespace android diff --git a/opengl/libs/entries_gles1.in b/opengl/libs/entries_gles1.in new file mode 100644 index 0000000000..c9be593dd9 --- /dev/null +++ b/opengl/libs/entries_gles1.in @@ -0,0 +1,298 @@ +GL_ENTRY(void, glActiveTexture, GLenum texture) +GL_ENTRY(void, glAlphaFunc, GLenum func, GLfloat ref) +GL_ENTRY(void, glAlphaFuncx, GLenum func, GLfixed ref) +GL_ENTRY(void, glAlphaFuncxOES, GLenum func, GLfixed ref) +GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer) +GL_ENTRY(void, glBindFramebufferOES, GLenum target, GLuint framebuffer) +GL_ENTRY(void, glBindRenderbufferOES, GLenum target, GLuint renderbuffer) +GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture) +GL_ENTRY(void, glBindVertexArrayOES, GLuint array) +GL_ENTRY(void, glBlendEquationOES, GLenum mode) +GL_ENTRY(void, glBlendEquationSeparateOES, GLenum modeRGB, GLenum modeAlpha) +GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor) +GL_ENTRY(void, glBlendFuncSeparateOES, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) +GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage) +GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data) +GL_ENTRY(GLenum, glCheckFramebufferStatusOES, GLenum target) +GL_ENTRY(void, glClear, GLbitfield mask) +GL_ENTRY(void, glClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +GL_ENTRY(void, glClearColorx, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glClearColorxOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glClearDepthf, GLfloat d) +GL_ENTRY(void, glClearDepthfOES, GLclampf depth) +GL_ENTRY(void, glClearDepthx, GLfixed depth) +GL_ENTRY(void, glClearDepthxOES, GLfixed depth) +GL_ENTRY(void, glClearStencil, GLint s) +GL_ENTRY(void, glClientActiveTexture, GLenum texture) +GL_ENTRY(GLenum, glClientWaitSyncAPPLE, GLsync sync, GLbitfield flags, GLuint64 timeout) +GL_ENTRY(void, glClipPlanef, GLenum p, const GLfloat *eqn) +GL_ENTRY(void, glClipPlanefIMG, GLenum p, const GLfloat *eqn) +GL_ENTRY(void, glClipPlanefOES, GLenum plane, const GLfloat *equation) +GL_ENTRY(void, glClipPlanex, GLenum plane, const GLfixed *equation) +GL_ENTRY(void, glClipPlanexIMG, GLenum p, const GLfixed *eqn) +GL_ENTRY(void, glClipPlanexOES, GLenum plane, const GLfixed *equation) +GL_ENTRY(void, glColor4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +GL_ENTRY(void, glColor4ub, GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) +GL_ENTRY(void, glColor4x, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glColor4xOES, GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) +GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) +GL_ENTRY(void, glColorPointer, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) +GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) +GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glCopyTextureLevelsAPPLE, GLuint destinationTexture, GLuint sourceTexture, GLint sourceBaseLevel, GLsizei sourceLevelCount) +GL_ENTRY(void, glCullFace, GLenum mode) +GL_ENTRY(void, glCurrentPaletteMatrixOES, GLuint matrixpaletteindex) +GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint *buffers) +GL_ENTRY(void, glDeleteFencesNV, GLsizei n, const GLuint *fences) +GL_ENTRY(void, glDeleteFramebuffersOES, GLsizei n, const GLuint *framebuffers) +GL_ENTRY(void, glDeleteRenderbuffersOES, GLsizei n, const GLuint *renderbuffers) +GL_ENTRY(void, glDeleteSyncAPPLE, GLsync sync) +GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint *textures) +GL_ENTRY(void, glDeleteVertexArraysOES, GLsizei n, const GLuint *arrays) +GL_ENTRY(void, glDepthFunc, GLenum func) +GL_ENTRY(void, glDepthMask, GLboolean flag) +GL_ENTRY(void, glDepthRangef, GLfloat n, GLfloat f) +GL_ENTRY(void, glDepthRangefOES, GLclampf n, GLclampf f) +GL_ENTRY(void, glDepthRangex, GLfixed n, GLfixed f) +GL_ENTRY(void, glDepthRangexOES, GLfixed n, GLfixed f) +GL_ENTRY(void, glDisable, GLenum cap) +GL_ENTRY(void, glDisableClientState, GLenum array) +GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl) +GL_ENTRY(void, glDiscardFramebufferEXT, GLenum target, GLsizei numAttachments, const GLenum *attachments) +GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count) +GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void *indices) +GL_ENTRY(void, glDrawTexfOES, GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloat height) +GL_ENTRY(void, glDrawTexfvOES, const GLfloat *coords) +GL_ENTRY(void, glDrawTexiOES, GLint x, GLint y, GLint z, GLint width, GLint height) +GL_ENTRY(void, glDrawTexivOES, const GLint *coords) +GL_ENTRY(void, glDrawTexsOES, GLshort x, GLshort y, GLshort z, GLshort width, GLshort height) +GL_ENTRY(void, glDrawTexsvOES, const GLshort *coords) +GL_ENTRY(void, glDrawTexxOES, GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfixed height) +GL_ENTRY(void, glDrawTexxvOES, const GLfixed *coords) +GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image) +GL_ENTRY(void, glEnable, GLenum cap) +GL_ENTRY(void, glEnableClientState, GLenum array) +GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl) +GL_ENTRY(void, glEndTilingQCOM, GLbitfield preserveMask) +GL_ENTRY(void, glExtGetBufferPointervQCOM, GLenum target, void **params) +GL_ENTRY(void, glExtGetBuffersQCOM, GLuint *buffers, GLint maxBuffers, GLint *numBuffers) +GL_ENTRY(void, glExtGetFramebuffersQCOM, GLuint *framebuffers, GLint maxFramebuffers, GLint *numFramebuffers) +GL_ENTRY(void, glExtGetProgramBinarySourceQCOM, GLuint program, GLenum shadertype, GLchar *source, GLint *length) +GL_ENTRY(void, glExtGetProgramsQCOM, GLuint *programs, GLint maxPrograms, GLint *numPrograms) +GL_ENTRY(void, glExtGetRenderbuffersQCOM, GLuint *renderbuffers, GLint maxRenderbuffers, GLint *numRenderbuffers) +GL_ENTRY(void, glExtGetShadersQCOM, GLuint *shaders, GLint maxShaders, GLint *numShaders) +GL_ENTRY(void, glExtGetTexLevelParameterivQCOM, GLuint texture, GLenum face, GLint level, GLenum pname, GLint *params) +GL_ENTRY(void, glExtGetTexSubImageQCOM, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, void *texels) +GL_ENTRY(void, glExtGetTexturesQCOM, GLuint *textures, GLint maxTextures, GLint *numTextures) +GL_ENTRY(GLboolean, glExtIsProgramBinaryQCOM, GLuint program) +GL_ENTRY(void, glExtTexObjectStateOverrideiQCOM, GLenum target, GLenum pname, GLint param) +GL_ENTRY(GLsync, glFenceSyncAPPLE, GLenum condition, GLbitfield flags) +GL_ENTRY(void, glFinish, void) +GL_ENTRY(void, glFinishFenceNV, GLuint fence) +GL_ENTRY(void, glFlush, void) +GL_ENTRY(void, glFlushMappedBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length) +GL_ENTRY(void, glFogf, GLenum pname, GLfloat param) +GL_ENTRY(void, glFogfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glFogx, GLenum pname, GLfixed param) +GL_ENTRY(void, glFogxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glFogxv, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glFogxvOES, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glFramebufferRenderbufferOES, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) +GL_ENTRY(void, glFramebufferTexture2DMultisampleEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) +GL_ENTRY(void, glFramebufferTexture2DMultisampleIMG, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples) +GL_ENTRY(void, glFramebufferTexture2DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) +GL_ENTRY(void, glFrontFace, GLenum mode) +GL_ENTRY(void, glFrustumf, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) +GL_ENTRY(void, glFrustumfOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) +GL_ENTRY(void, glFrustumx, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f) +GL_ENTRY(void, glFrustumxOES, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f) +GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint *buffers) +GL_ENTRY(void, glGenFencesNV, GLsizei n, GLuint *fences) +GL_ENTRY(void, glGenFramebuffersOES, GLsizei n, GLuint *framebuffers) +GL_ENTRY(void, glGenRenderbuffersOES, GLsizei n, GLuint *renderbuffers) +GL_ENTRY(void, glGenTextures, GLsizei n, GLuint *textures) +GL_ENTRY(void, glGenVertexArraysOES, GLsizei n, GLuint *arrays) +GL_ENTRY(void, glGenerateMipmapOES, GLenum target) +GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean *data) +GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void **params) +GL_ENTRY(void, glGetClipPlanef, GLenum plane, GLfloat *equation) +GL_ENTRY(void, glGetClipPlanefOES, GLenum plane, GLfloat *equation) +GL_ENTRY(void, glGetClipPlanex, GLenum plane, GLfixed *equation) +GL_ENTRY(void, glGetClipPlanexOES, GLenum plane, GLfixed *equation) +GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, GLchar *driverControlString) +GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls) +GL_ENTRY(GLenum, glGetError, void) +GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params) +GL_ENTRY(void, glGetFixedv, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetFixedvOES, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat *data) +GL_ENTRY(void, glGetFramebufferAttachmentParameterivOES, GLenum target, GLenum attachment, GLenum pname, GLint *params) +GL_ENTRY(GLenum, glGetGraphicsResetStatusEXT, void) +GL_ENTRY(void, glGetInteger64vAPPLE, GLenum pname, GLint64 *params) +GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint *data) +GL_ENTRY(void, glGetLightfv, GLenum light, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetLightxv, GLenum light, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetLightxvOES, GLenum light, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetMaterialfv, GLenum face, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetMaterialxv, GLenum face, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetMaterialxvOES, GLenum face, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetPointerv, GLenum pname, void **params) +GL_ENTRY(void, glGetRenderbufferParameterivOES, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(const GLubyte *, glGetString, GLenum name) +GL_ENTRY(void, glGetSyncivAPPLE, GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values) +GL_ENTRY(void, glGetTexEnvfv, GLenum target, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexEnviv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexEnvxv, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexEnvxvOES, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexGenfvOES, GLenum coord, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexGenivOES, GLenum coord, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexGenxvOES, GLenum coord, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat *params) +GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint *params) +GL_ENTRY(void, glGetTexParameterxv, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetTexParameterxvOES, GLenum target, GLenum pname, GLfixed *params) +GL_ENTRY(void, glGetnUniformfvEXT, GLuint program, GLint location, GLsizei bufSize, GLfloat *params) +GL_ENTRY(void, glGetnUniformivEXT, GLuint program, GLint location, GLsizei bufSize, GLint *params) +GL_ENTRY(void, glHint, GLenum target, GLenum mode) +GL_ENTRY(void, glInsertEventMarkerEXT, GLsizei length, const GLchar *marker) +GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer) +GL_ENTRY(GLboolean, glIsEnabled, GLenum cap) +GL_ENTRY(GLboolean, glIsFenceNV, GLuint fence) +GL_ENTRY(GLboolean, glIsFramebufferOES, GLuint framebuffer) +GL_ENTRY(GLboolean, glIsRenderbufferOES, GLuint renderbuffer) +GL_ENTRY(GLboolean, glIsSyncAPPLE, GLsync sync) +GL_ENTRY(GLboolean, glIsTexture, GLuint texture) +GL_ENTRY(GLboolean, glIsVertexArrayOES, GLuint array) +GL_ENTRY(void, glLightModelf, GLenum pname, GLfloat param) +GL_ENTRY(void, glLightModelfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glLightModelx, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightModelxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightModelxv, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glLightModelxvOES, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glLightf, GLenum light, GLenum pname, GLfloat param) +GL_ENTRY(void, glLightfv, GLenum light, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glLightx, GLenum light, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightxOES, GLenum light, GLenum pname, GLfixed param) +GL_ENTRY(void, glLightxv, GLenum light, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glLightxvOES, GLenum light, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glLineWidth, GLfloat width) +GL_ENTRY(void, glLineWidthx, GLfixed width) +GL_ENTRY(void, glLineWidthxOES, GLfixed width) +GL_ENTRY(void, glLoadIdentity, void) +GL_ENTRY(void, glLoadMatrixf, const GLfloat *m) +GL_ENTRY(void, glLoadMatrixx, const GLfixed *m) +GL_ENTRY(void, glLoadMatrixxOES, const GLfixed *m) +GL_ENTRY(void, glLoadPaletteFromModelViewMatrixOES, void) +GL_ENTRY(void, glLogicOp, GLenum opcode) +GL_ENTRY(void *, glMapBufferOES, GLenum target, GLenum access) +GL_ENTRY(void *, glMapBufferRangeEXT, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) +GL_ENTRY(void, glMaterialf, GLenum face, GLenum pname, GLfloat param) +GL_ENTRY(void, glMaterialfv, GLenum face, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glMaterialx, GLenum face, GLenum pname, GLfixed param) +GL_ENTRY(void, glMaterialxOES, GLenum face, GLenum pname, GLfixed param) +GL_ENTRY(void, glMaterialxv, GLenum face, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glMaterialxvOES, GLenum face, GLenum pname, const GLfixed *param) +GL_ENTRY(void, glMatrixIndexPointerOES, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glMatrixMode, GLenum mode) +GL_ENTRY(void, glMultMatrixf, const GLfloat *m) +GL_ENTRY(void, glMultMatrixx, const GLfixed *m) +GL_ENTRY(void, glMultMatrixxOES, const GLfixed *m) +GL_ENTRY(void, glMultiDrawArraysEXT, GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) +GL_ENTRY(void, glMultiDrawElementsEXT, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount) +GL_ENTRY(void, glMultiTexCoord4f, GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) +GL_ENTRY(void, glMultiTexCoord4x, GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q) +GL_ENTRY(void, glMultiTexCoord4xOES, GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q) +GL_ENTRY(void, glNormal3f, GLfloat nx, GLfloat ny, GLfloat nz) +GL_ENTRY(void, glNormal3x, GLfixed nx, GLfixed ny, GLfixed nz) +GL_ENTRY(void, glNormal3xOES, GLfixed nx, GLfixed ny, GLfixed nz) +GL_ENTRY(void, glNormalPointer, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glOrthof, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) +GL_ENTRY(void, glOrthofOES, GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f) +GL_ENTRY(void, glOrthox, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f) +GL_ENTRY(void, glOrthoxOES, GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f) +GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param) +GL_ENTRY(void, glPointParameterf, GLenum pname, GLfloat param) +GL_ENTRY(void, glPointParameterfv, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glPointParameterx, GLenum pname, GLfixed param) +GL_ENTRY(void, glPointParameterxOES, GLenum pname, GLfixed param) +GL_ENTRY(void, glPointParameterxv, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glPointParameterxvOES, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glPointSize, GLfloat size) +GL_ENTRY(void, glPointSizePointerOES, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glPointSizex, GLfixed size) +GL_ENTRY(void, glPointSizexOES, GLfixed size) +GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units) +GL_ENTRY(void, glPolygonOffsetx, GLfixed factor, GLfixed units) +GL_ENTRY(void, glPolygonOffsetxOES, GLfixed factor, GLfixed units) +GL_ENTRY(void, glPopGroupMarkerEXT, void) +GL_ENTRY(void, glPopMatrix, void) +GL_ENTRY(void, glPushGroupMarkerEXT, GLsizei length, const GLchar *marker) +GL_ENTRY(void, glPushMatrix, void) +GL_ENTRY(GLbitfield, glQueryMatrixxOES, GLfixed *mantissa, GLint *exponent) +GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels) +GL_ENTRY(void, glReadnPixelsEXT, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data) +GL_ENTRY(void, glRenderbufferStorageMultisampleAPPLE, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glRenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glRenderbufferStorageMultisampleIMG, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glRenderbufferStorageOES, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glResolveMultisampleFramebufferAPPLE, void) +GL_ENTRY(void, glRotatef, GLfloat angle, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glRotatex, GLfixed angle, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glRotatexOES, GLfixed angle, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glSampleCoverage, GLfloat value, GLboolean invert) +GL_ENTRY(void, glSampleCoveragex, GLclampx value, GLboolean invert) +GL_ENTRY(void, glSampleCoveragexOES, GLclampx value, GLboolean invert) +GL_ENTRY(void, glScalef, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glScalex, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glScalexOES, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition) +GL_ENTRY(void, glShadeModel, GLenum mode) +GL_ENTRY(void, glStartTilingQCOM, GLuint x, GLuint y, GLuint width, GLuint height, GLbitfield preserveMask) +GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask) +GL_ENTRY(void, glStencilMask, GLuint mask) +GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass) +GL_ENTRY(GLboolean, glTestFenceNV, GLuint fence) +GL_ENTRY(void, glTexCoordPointer, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glTexEnvf, GLenum target, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexEnvfv, GLenum target, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexEnvi, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glTexEnviv, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexEnvx, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexEnvxOES, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexEnvxv, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexEnvxvOES, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexGenfOES, GLenum coord, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexGenfvOES, GLenum coord, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexGeniOES, GLenum coord, GLenum pname, GLint param) +GL_ENTRY(void, glTexGenivOES, GLenum coord, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexGenxOES, GLenum coord, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexGenxvOES, GLenum coord, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param) +GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat *params) +GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param) +GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint *params) +GL_ENTRY(void, glTexParameterx, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexParameterxOES, GLenum target, GLenum pname, GLfixed param) +GL_ENTRY(void, glTexParameterxv, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexParameterxvOES, GLenum target, GLenum pname, const GLfixed *params) +GL_ENTRY(void, glTexStorage1DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) +GL_ENTRY(void, glTexStorage2DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glTexStorage3DEXT, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) +GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) +GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) +GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) +GL_ENTRY(void, glTranslatef, GLfloat x, GLfloat y, GLfloat z) +GL_ENTRY(void, glTranslatex, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(void, glTranslatexOES, GLfixed x, GLfixed y, GLfixed z) +GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target) +GL_ENTRY(void, glVertexPointer, GLint size, GLenum type, GLsizei stride, const void *pointer) +GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height) +GL_ENTRY(void, glWaitSyncAPPLE, GLsync sync, GLbitfield flags, GLuint64 timeout) +GL_ENTRY(void, glWeightPointerOES, GLint size, GLenum type, GLsizei stride, const void *pointer) diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h index 81dbe0e34b..63a0e140cc 100644 --- a/opengl/libs/hooks.h +++ b/opengl/libs/hooks.h @@ -59,6 +59,10 @@ namespace android { #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 fa26e33f39..b2d795745f 100644 --- a/opengl/libs/libEGL.map.txt +++ b/opengl/libs/libEGL.map.txt @@ -3,23 +3,30 @@ LIBEGL { 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 @@ LIBEGL { 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 @@ LIBEGL { 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 0000000000..46734112d3 --- /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 feef3186a8..0000000000 --- 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 4d8334f90f..0000000000 --- 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 170f04131a..0000000000 --- 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 2ae5fbfa25..0000000000 --- a/opengl/libs/tools/glenumsgen +++ /dev/null @@ -1,38 +0,0 @@ -#! /usr/bin/perl -# -# Copyright (C) 2010 Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -use strict; - -my %enumHash = (); - -while (my $line = <STDIN>) { - next if $line =~ /^\//; - # Skip bitfield definitions. - next if $line =~ /_BIT(\d+_|\s+)/; - if ($line !~ /^#define\s+(\S+)\s+(0x\S+)/) { - next; - } - my $enumName = $1; - my $enumValue = $2; - next if exists($enumHash { $enumValue }); - $enumHash { $enumValue } = $enumName; - printf("GL_ENUM(%s,%s)\n", $enumValue, $enumName); -} - - - - - diff --git a/opengl/specs/README b/opengl/specs/README index fdafb1bffb..6d597d5519 100644 --- a/opengl/specs/README +++ b/opengl/specs/README @@ -19,10 +19,7 @@ Khronos. 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 459b1356b8..cca91c3a6f 100644 --- a/opengl/tests/EGLTest/EGL_test.cpp +++ b/opengl/tests/EGLTest/EGL_test.cpp @@ -217,6 +217,7 @@ TEST_F(EGLTest, EGLDisplayP3) { // 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 @@ TEST_F(EGLTest, EGLDisplayP3) { 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_F(EGLTest, EGLDisplayP31010102) { // 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 2b76279801..b06422a98c 100644 --- a/opengl/tests/lib/WindowSurface.cpp +++ b/opengl/tests/lib/WindowSurface.cpp @@ -57,7 +57,7 @@ WindowSurface::WindowSurface() { 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 213dffd50f..290d7a0de2 100644 --- a/opengl/tests/lib/glTestLib.cpp +++ b/opengl/tests/lib/glTestLib.cpp @@ -37,7 +37,7 @@ void glTestPrintGLString(const char *name, GLenum s) { 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 9dc6bcf56a..cfa378f354 100644 --- a/opengl/tests/lib/include/EGLUtils.h +++ b/opengl/tests/lib/include/EGLUtils.h @@ -100,11 +100,11 @@ status_t EGLUtils::selectConfigForPixelFormat( 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 @@ status_t EGLUtils::selectConfigForPixelFormat( } 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 @@ String8 EGLUtils::printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { 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 @@ String8 EGLUtils::decodeColorSpace(EGLint colorSpace) { 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 f9e96eacb6..9fa58e2b58 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -93,6 +93,7 @@ rm src/*.class 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 \ @@ -155,13 +156,13 @@ do 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 0000000000..e0aad30f3c --- /dev/null +++ b/opengl/tools/glgen/specs/egl/EGL15.spec @@ -0,0 +1,14 @@ +EGLSync eglCreateSync ( EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list ) +EGLBoolean eglDestroySync ( EGLDisplay dpy, EGLSync sync ) +EGLint eglClientWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout ) +EGLBoolean eglGetSyncAttrib ( EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value ) +// NOTE: native_display isn't actually an EGLAttrib. Using EGLAttrib +// so that the generate creates mostly correct code (do not want a buffer) +// have to manually change cast to (void *) in generated code that calls +// the native function. +EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) +EGLSurface eglCreatePlatformWindowSurface ( EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list ) +EGLSurface eglCreatePlatformPixmapSurface ( EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list ) +EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) +EGLImage eglCreateImage ( EGLDisplay dpy, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list ) +EGLBoolean eglDestroyImage ( EGLDisplay dpy, EGLImage image ) diff --git a/opengl/tools/glgen/specs/egl/checks.spec b/opengl/tools/glgen/specs/egl/checks.spec index ae531eef78..e2bae482d5 100644 --- a/opengl/tools/glgen/specs/egl/checks.spec +++ b/opengl/tools/glgen/specs/egl/checks.spec @@ -11,3 +11,5 @@ eglQuerySurface check value 1 //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 aba98afb95..b1f8e0580d 100644 --- a/opengl/tools/glgen/src/CType.java +++ b/opengl/tools/glgen/src/CType.java @@ -57,7 +57,9 @@ public class CType { 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 2ef3970299..57958c6ef2 100644 --- a/opengl/tools/glgen/src/GenerateEGL.java +++ b/opengl/tools/glgen/src/GenerateEGL.java @@ -84,7 +84,7 @@ public class GenerateEGL { 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 7f08503fa6..0b4401ad99 100644 --- a/opengl/tools/glgen/src/JType.java +++ b/opengl/tools/glgen/src/JType.java @@ -60,12 +60,16 @@ public class JType { 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 @@ public class JType { 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 e8691bb7f8..6697189695 100644 --- a/opengl/tools/glgen/src/JniCodeEmitter.java +++ b/opengl/tools/glgen/src/JniCodeEmitter.java @@ -103,6 +103,12 @@ public class JniCodeEmitter { 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); diff --git a/opengl/tools/glgen/static/egl/EGLImage.java b/opengl/tools/glgen/static/egl/EGLImage.java new file mode 100644 index 0000000000..731ce72aa0 --- /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 0000000000..472f9e7125 --- /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/EGL15Header.java-if b/opengl/tools/glgen/stubs/egl/EGL15Header.java-if new file mode 100644 index 0000000000..859380f6fc --- /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 0000000000..70b46f7585 --- /dev/null +++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp @@ -0,0 +1,205 @@ +/* +** 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 GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-function" + +#include <android_runtime/AndroidRuntime.h> +#include <nativehelper/JNIHelp.h> +#include <utils/misc.h> +#include "jni.h" + +#include <EGL/egl.h> +#include <assert.h> + +#include <ui/ANativeObjectBase.h> + +static int initialized = 0; + +// classes from EGL 1.4 +static jclass egldisplayClass; +static jclass eglsurfaceClass; +static jclass eglconfigClass; +static jclass eglcontextClass; +static jclass bufferClass; +static jclass nioAccessClass; + +static jfieldID positionID; +static jfieldID limitID; +static jfieldID elementSizeShiftID; + +static jmethodID getBasePointerID; +static jmethodID getBaseArrayID; +static jmethodID getBaseArrayOffsetID; + +static jmethodID egldisplayGetHandleID; +static jmethodID eglconfigGetHandleID; +static jmethodID eglcontextGetHandleID; +static jmethodID eglsurfaceGetHandleID; + +static jmethodID egldisplayConstructor; +static jmethodID eglcontextConstructor; +static jmethodID eglsurfaceConstructor; +static jmethodID eglconfigConstructor; + +static jobject eglNoContextObject; +static jobject eglNoDisplayObject; +static jobject eglNoSurfaceObject; + +// classes from EGL 1.5 +static jclass eglimageClass; +static jclass eglsyncClass; + +static jmethodID eglimageGetHandleID; +static jmethodID eglsyncGetHandleID; + +static jmethodID eglimageConstructor; +static jmethodID eglsyncConstructor; + +static jobject eglNoImageObject; +static jobject eglNoSyncObject; + +/* Cache method IDs each time the class is loaded. */ + +static void nativeClassInit(JNIEnv *_env, jclass glImplClass) { + // EGL 1.4 Init + jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig"); + eglconfigClass = (jclass)_env->NewGlobalRef(eglconfigClassLocal); + jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext"); + eglcontextClass = (jclass)_env->NewGlobalRef(eglcontextClassLocal); + jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay"); + egldisplayClass = (jclass)_env->NewGlobalRef(egldisplayClassLocal); + jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface"); + eglsurfaceClass = (jclass)_env->NewGlobalRef(eglsurfaceClassLocal); + + eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J"); + eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J"); + egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J"); + eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J"); + + eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V"); + eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V"); + egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V"); + eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V"); + + jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, + reinterpret_cast<jlong>(EGL_NO_CONTEXT)); + eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject); + jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, + reinterpret_cast<jlong>(EGL_NO_DISPLAY)); + eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject); + jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, + reinterpret_cast<jlong>(EGL_NO_SURFACE)); + eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject); + + jclass eglClass = _env->FindClass("android/opengl/EGL15"); + jfieldID noContextFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;"); + _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject); + + jfieldID noDisplayFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;"); + _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject); + + jfieldID noSurfaceFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;"); + _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject); + + // EGL 1.5 init + jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); + nioAccessClass = (jclass)_env->NewGlobalRef(nioAccessClassLocal); + + jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); + bufferClass = (jclass)_env->NewGlobalRef(bufferClassLocal); + + getBasePointerID = + _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J"); + getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray", + "(Ljava/nio/Buffer;)Ljava/lang/Object;"); + getBaseArrayOffsetID = + _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); + + positionID = _env->GetFieldID(bufferClass, "position", "I"); + limitID = _env->GetFieldID(bufferClass, "limit", "I"); + elementSizeShiftID = _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); + + jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage"); + eglimageClass = (jclass)_env->NewGlobalRef(eglimageClassLocal); + jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync"); + eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal); + + eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); + eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); + + eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V"); + eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V"); + + jfieldID noImageFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;"); + _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject); + + jfieldID noSyncFieldID = + _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;"); + _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject); +} + +static void *getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, + jint *offset) { + jint position; + jint limit; + jint elementSizeShift; + jlong pointer; + + position = _env->GetIntField(buffer, positionID); + limit = _env->GetIntField(buffer, limitID); + elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); + *remaining = (limit - position) << elementSizeShift; + pointer = _env->CallStaticLongMethod(nioAccessClass, getBasePointerID, buffer); + if (pointer != 0L) { + *array = NULL; + return reinterpret_cast<void *>(pointer); + } + eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); + eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); + + *array = (jarray)_env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); + *offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer); + + return NULL; +} + +static void releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) { + _env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT); +} + +static void *fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { + if (obj == NULL) { + jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); + } + + jlong handle = _env->CallLongMethod(obj, mid); + return reinterpret_cast<void *>(handle); +} + +static jobject toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) { + if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) { + return eglNoImageObject; + } + + return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle)); +} + +// -------------------------------------------------------------------------- diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp new file mode 100644 index 0000000000..fd44498e43 --- /dev/null +++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp @@ -0,0 +1,46 @@ +/* EGLDisplay eglGetPlatformDisplay ( EGLenum platform, EGLAttrib native_display, const EGLAttrib *attrib_list ) */ +static jobject +android_eglGetPlatformDisplay + (JNIEnv *_env, jobject _this, jint platform, jlong native_display, jlongArray attrib_list_ref, jint offset) { + jint _exception = 0; + const char * _exceptionType = NULL; + const char * _exceptionMessage = NULL; + EGLDisplay _returnValue = (EGLDisplay) 0; + EGLAttrib *attrib_list_base = (EGLAttrib *) 0; + jint _remaining; + EGLAttrib *attrib_list = (EGLAttrib *) 0; + + if (!attrib_list_ref) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "attrib_list == null"; + goto exit; + } + if (offset < 0) { + _exception = 1; + _exceptionType = "java/lang/IllegalArgumentException"; + _exceptionMessage = "offset < 0"; + goto exit; + } + _remaining = _env->GetArrayLength(attrib_list_ref) - offset; + attrib_list_base = (EGLAttrib *) + _env->GetLongArrayElements(attrib_list_ref, (jboolean *)0); + attrib_list = attrib_list_base + offset; + + _returnValue = eglGetPlatformDisplay( + (EGLenum)platform, + (void *)native_display, + (EGLAttrib *)attrib_list + ); + +exit: + if (attrib_list_base) { + _env->ReleaseLongArrayElements(attrib_list_ref, (jlong*)attrib_list_base, + JNI_ABORT); + } + if (_exception) { + jniThrowException(_env, _exceptionType, _exceptionMessage); + } + return 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 0000000000..28945e8247 --- /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 0000000000..8a309bf0a2 --- /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/glgen2/glgen.py b/opengl/tools/glgen2/glgen.py index fa981a89a7..86fa28fb1f 100755 --- a/opengl/tools/glgen2/glgen.py +++ b/opengl/tools/glgen2/glgen.py @@ -250,6 +250,27 @@ if __name__ == '__main__': for opts in TRAMPOLINE_OPTIONS: registry.apiGen(opts) + # Generate a GLESv1_CM entries separately to avoid extra driver loading time + apigen = ApiGenerator() + registry.setGenerator(apigen) + API_OPTIONS = [ + # Generate non-extension versions of each API first, then extensions, + # so that if an extension enum was later standardized, we see the non- + # suffixed version first. + reg.GeneratorOptions( + apiname = 'gles1', + profile = 'common'), + reg.GeneratorOptions( + apiname = 'gles1', + profile = 'common', + emitversions = None, + defaultExtensions = 'gles1')] + for opts in API_OPTIONS: + registry.apiGen(opts) + apigen.finish() + with open('../../libs/entries_gles1.in', 'w') as f: + apigen.writeEntries(f) + apigen = ApiGenerator() registry.setGenerator(apigen) API_OPTIONS = [ diff --git a/opengl/tools/glgen2/registry/egl.xml b/opengl/tools/glgen2/registry/egl.xml index e422e96557..8d661aea43 100644 --- a/opengl/tools/glgen2/registry/egl.xml +++ b/opengl/tools/glgen2/registry/egl.xml @@ -801,7 +801,9 @@ <enum value="0x3361" name="EGL_CTA861_3_MAX_FRAME_AVERAGE_LEVEL_EXT"/> <enum value="0x3362" name="EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT"/> <enum value="0x3363" name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/> - <unused start="0x3364" end="0x339F"/> + <enum value="0x3364" name="EGL_SYNC_CLIENT_EXT"/> + <enum value="0x3365" name="EGL_SYNC_CLIENT_SIGNAL_EXT"/> + <unused start="0x3366" end="0x339F"/> </enums> <enums namespace="EGL" start="0x33A0" end="0x33AF" vendor="ANGLE" comment="Reserved for Shannon Woods (Bug 13175)"> @@ -887,6 +889,15 @@ <enum value="0x3471" name="EGL_IMPORT_IMPLICIT_SYNC_EXT"/> <enum value="0x3472" name="EGL_IMPORT_EXPLICIT_SYNC_EXT"/> </enums> + <enums namespace="EGL" start="0x3480" end="0x348F" vendor="ANGLE" comment="Reserved for Courtney Goeltzenleuchter - ANGLE (gitlab EGL bug 7)"> + <enum value="0x3480" name="EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE"/> + <unused start="0x3481" end="0x348F"/> + </enums> + + <enums namespace="EGL" start="0x3490" end="0x349F" vendor="EXT" comment="Reserved for Courtney Goeltzenleuchter - Android (gitlab EGL bug 69)"> + <enum value="0x3490" name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/> + <unused start="0x3491" end="0x349F"/> + </enums> <!-- Please remember that new enumerant allocations must be obtained by request to the Khronos API registrar (see comments at the top of this @@ -897,8 +908,8 @@ <!-- Reservable for future use. To generate a new range, allocate multiples of 16 starting at the lowest available point in this block. --> - <enums namespace="EGL" start="0x3480" end="0x3FFF" vendor="KHR" comment="Reserved for future use"> - <unused start="0x3480" end="0x3FFF"/> + <enums namespace="EGL" start="0x34A0" end="0x3FFF" vendor="KHR" comment="Reserved for future use"> + <unused start="0x34A0" end="0x3FFF"/> </enums> <enums namespace="EGL" start="0x8F70" end="0x8F7F" vendor="HI" comment="For Mark Callow, Khronos bug 4055. Shared with GL."> @@ -930,6 +941,12 @@ <param><ptype>EGLint</ptype> *<name>num_config</name></param> </command> <command> + <proto><ptype>EGLBoolean</ptype> <name>eglClientSignalSyncEXT</name></proto> + <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> + <param><ptype>EGLSync</ptype> <name>sync</name></param> + <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param> + </command> + <command> <proto><ptype>EGLint</ptype> <name>eglClientWaitSync</name></proto> <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> <param><ptype>EGLSync</ptype> <name>sync</name></param> @@ -1647,6 +1664,11 @@ <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param> </command> <command> + <proto><ptype>EGLBoolean</ptype> <name>eglStreamFlushNV</name></proto> + <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> + <param><ptype>EGLStreamKHR</ptype> <name>stream</name></param> + </command> + <command> <proto><ptype>EGLBoolean</ptype> <name>eglSurfaceAttrib</name></proto> <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> <param><ptype>EGLSurface</ptype> <name>surface</name></param> @@ -1701,6 +1723,12 @@ <param><ptype>EGLSurface</ptype> <name>surface</name></param> </command> <command> + <proto><ptype>EGLBoolean</ptype> <name>eglUnsignalSyncEXT</name></proto> + <param><ptype>EGLDisplay</ptype> <name>dpy</name></param> + <param><ptype>EGLSync</ptype> <name>sync</name></param> + <param>const <ptype>EGLAttrib</ptype> *<name>attrib_list</name></param> + </command> + <command> <proto><ptype>EGLBoolean</ptype> <name>eglWaitClient</name></proto> </command> <command> @@ -2146,6 +2174,13 @@ </require> </extension> <extension name="EGL_EXT_client_extensions" supported="egl"/> + <extension name="EGL_EXT_client_sync" supported="egl"> + <require> + <enum name="EGL_SYNC_CLIENT_EXT"/> + <enum name="EGL_SYNC_CLIENT_SIGNAL_EXT"/> + <command name="eglClientSignalSyncEXT"/> + </require> + </extension> <extension name="EGL_EXT_create_context_robustness" supported="egl"> <require> <enum name="EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT"/> @@ -2220,6 +2255,11 @@ <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_EXT"/> </require> </extension> + <extension name="EGL_EXT_gl_colorspace_display_p3_passthrough" supported="egl"> + <require> + <enum name="EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT"/> + </require> + </extension> <extension name="EGL_EXT_image_dma_buf_import" supported="egl"> <require> <enum name="EGL_LINUX_DMA_BUF_EXT"/> @@ -2371,6 +2411,11 @@ <command name="eglSwapBuffersWithDamageEXT"/> </require> </extension> + <extension name="EGL_EXT_sync_reuse" supported="egl"> + <require> + <command name="eglUnsignalSyncEXT"/> + </require> + </extension> <extension name="EGL_EXT_yuv_surface" supported="egl"> <require> <enum name="EGL_YUV_ORDER_EXT"/> @@ -2932,6 +2977,11 @@ <enum name="EGL_STREAM_FIFO_SYNCHRONOUS_NV"/> </require> </extension> + <extension name="EGL_NV_stream_flush" supported="egl"> + <require> + <command name="eglStreamFlushNV"/> + </require> + </extension> <extension name="EGL_NV_stream_frame_limits" supported="egl"> <require> <enum name="EGL_PRODUCER_MAX_FRAME_HINT_NV"/> diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp new file mode 100644 index 0000000000..72d210cbb0 --- /dev/null +++ b/services/bufferhub/Android.bp @@ -0,0 +1,78 @@ +// +// 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", + "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", + "libcutils", + "libhidltransport", + "libhwbinder", + "liblog", + "libui", + "libutils", + ], + cflags: [ + "-DLOG_TAG=\"bufferhub\"", + "-Wall", + "-Werror", + "-Wextra", + ], + init_rc: ["android.frameworks.bufferhub@1.0-service.rc"], + vintf_fragments: ["android.frameworks.bufferhub@1.0-service.xml"], +} diff --git a/services/bufferhub/BufferClient.cpp b/services/bufferhub/BufferClient.cpp new file mode 100644 index 0000000000..e312011696 --- /dev/null +++ b/services/bufferhub/BufferClient.cpp @@ -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. + */ + +#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() { + 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 0000000000..6444a033e1 --- /dev/null +++ b/services/bufferhub/BufferHubIdGenerator.cpp @@ -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. + */ + +#include <bufferhub/BufferHubIdGenerator.h> + +namespace android { +namespace frameworks { +namespace bufferhub { +namespace V1_0 { +namespace implementation { + +constexpr uint32_t BufferHubIdGenerator::kInvalidId; + +BufferHubIdGenerator& BufferHubIdGenerator::getInstance() { + static BufferHubIdGenerator generator; + + return generator; +} + +uint32_t BufferHubIdGenerator::getId() { + std::lock_guard<std::mutex> lock(mIdsInUseMutex); + + do { + if (++mLastId >= std::numeric_limits<uint32_t>::max()) { + mLastId = kInvalidId + 1; + } + } while (mIdsInUse.find(mLastId) != mIdsInUse.end()); + + mIdsInUse.insert(mLastId); + return mLastId; +} + +bool BufferHubIdGenerator::freeId(uint32_t id) { + std::lock_guard<std::mutex> lock(mIdsInUseMutex); + auto iter = mIdsInUse.find(id); + if (iter != mIdsInUse.end()) { + mIdsInUse.erase(iter); + return true; + } + + return false; +} + +} // 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 0000000000..26638126d3 --- /dev/null +++ b/services/bufferhub/BufferHubService.cpp @@ -0,0 +1,174 @@ +/* + * 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 <android/hardware_buffer.h> +#include <bufferhub/BufferHubService.h> +#include <cutils/native_handle.h> +#include <log/log.h> + +namespace android { +namespace frameworks { +namespace bufferhub { +namespace V1_0 { +namespace implementation { + +using hardware::Void; + +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); + + BufferTraits bufferTraits = {/*bufferDesc=*/description, + /*bufferHandle=*/hidl_handle(node->buffer_handle()), + // TODO(b/116681016): return real data to client + /*bufferInfo=*/hidl_handle()}; + + _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client, + /*bufferTraits=*/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(); + } + + uint32_t token = tokenHandle->data[0]; + + wp<BufferClient> originClientWp; + { + std::lock_guard<std::mutex> lock(mTokenMapMutex); + auto iter = mTokenMap.find(token); + if (iter == mTokenMap.end()) { + // Invalid token + _hidl_cb(/*status=*/BufferHubStatus::INVALID_TOKEN, /*bufferClient=*/nullptr, + /*bufferTraits=*/{}); + return Void(); + } + + originClientWp = iter->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->buffer_desc(), sizeof(HardwareBufferDescription)); + + BufferTraits bufferTraits = {/*bufferDesc=*/bufferDesc, + /*bufferHandle=*/hidl_handle(node->buffer_handle()), + // TODO(b/116681016): return real data to client + /*bufferInfo=*/hidl_handle()}; + + _hidl_cb(/*status=*/BufferHubStatus::NO_ERROR, /*bufferClient=*/client, + /*bufferTraits=*/bufferTraits); + return Void(); +} + +hidl_handle BufferHubService::registerToken(const wp<BufferClient>& client) { + uint32_t token; + std::lock_guard<std::mutex> lock(mTokenMapMutex); + do { + token = mTokenEngine(); + } while (mTokenMap.find(token) != mTokenMap.end()); + + // native_handle_t use int[], so here need one slots to fit in uint32_t + native_handle_t* handle = native_handle_create(/*numFds=*/0, /*numInts=*/1); + handle->data[0] = token; + + // returnToken owns the native_handle_t* thus doing lifecycle management + hidl_handle returnToken; + returnToken.setTo(handle, /*shoudOwn=*/true); + + mTokenMap.emplace(token, 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); + } +} + +void BufferHubService::removeTokenByClient(const BufferClient* client) { + std::lock_guard<std::mutex> lock(mTokenMapMutex); + auto iter = mTokenMap.begin(); + while (iter != mTokenMap.end()) { + if (iter->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 0000000000..da19a6fb1d --- /dev/null +++ b/services/bufferhub/BufferNode.cpp @@ -0,0 +1,118 @@ +#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* metadata_header = metadata_.metadata_header(); + buffer_state_ = new (&metadata_header->buffer_state) std::atomic<uint32_t>(0); + fence_state_ = new (&metadata_header->fence_state) std::atomic<uint32_t>(0); + active_clients_bit_mask_ = + new (&metadata_header->active_clients_bit_mask) 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(buffer_state_) || + !std::atomic_is_lock_free(fence_state_) || + !std::atomic_is_lock_free(active_clients_bit_mask_), + "Atomic variables in ashmen are not lock free."); +} + +// Allocates a new BufferNode. +BufferNode::BufferNode(uint32_t width, uint32_t height, uint32_t layer_count, uint32_t format, + uint64_t usage, size_t user_metadata_size, uint32_t id) + : mId(id) { + uint32_t out_stride = 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, layer_count, usage, + const_cast<const native_handle_t**>( + &buffer_handle_), + &out_stride, + /*graphicBufferId=*/0, + /*requestor=*/"bufferhub"); + + if (ret != OK || buffer_handle_ == nullptr) { + ALOGE("%s: Failed to allocate buffer: %s", __FUNCTION__, strerror(-ret)); + return; + } + + buffer_desc_.width = width; + buffer_desc_.height = height; + buffer_desc_.layers = layer_count; + buffer_desc_.format = format; + buffer_desc_.usage = usage; + buffer_desc_.stride = out_stride; + + metadata_ = BufferHubMetadata::Create(user_metadata_size); + if (!metadata_.IsValid()) { + ALOGE("%s: Failed to allocate metadata.", __FUNCTION__); + return; + } + InitializeMetadata(); +} + +BufferNode::~BufferNode() { + // Free the handle + if (buffer_handle_ != nullptr) { + status_t ret = GraphicBufferAllocator::get().free(buffer_handle_); + if (ret != OK) { + ALOGE("%s: Failed to free handle; Got error: %d", __FUNCTION__, ret); + } + } + + // Free the id, if valid + if (id() != BufferHubIdGenerator::kInvalidId) { + if (BufferHubIdGenerator::getInstance().freeId(id())) { + ALOGI("%s: id #%u is freed.", __FUNCTION__, id()); + } else { + ALOGE("%s: Cannot free nonexistent id #%u", __FUNCTION__, id()); + } + } +} + +uint32_t BufferNode::GetActiveClientsBitMask() const { + return active_clients_bit_mask_->load(std::memory_order_acquire); +} + +uint32_t BufferNode::AddNewActiveClientsBitToMask() { + uint32_t current_active_clients_bit_mask = GetActiveClientsBitMask(); + uint32_t client_state_mask = 0U; + uint32_t updated_active_clients_bit_mask = 0U; + do { + client_state_mask = + BufferHubDefs::FindNextAvailableClientStateMask(current_active_clients_bit_mask); + if (client_state_mask == 0U) { + ALOGE("%s: reached the maximum number of channels per buffer node: %d.", __FUNCTION__, + BufferHubDefs::kMaxNumberOfClients); + errno = E2BIG; + return 0U; + } + updated_active_clients_bit_mask = current_active_clients_bit_mask | client_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))); + return client_state_mask; +} + +void BufferNode::RemoveClientsBitFromMask(const uint32_t& value) { + active_clients_bit_mask_->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 0000000000..36fbedee15 --- /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 0000000000..bd958d3954 --- /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 0000000000..66ed4bd13f --- /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
\ No newline at end of file diff --git a/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h b/services/bufferhub/include/bufferhub/BufferHubIdGenerator.h new file mode 100644 index 0000000000..b51fcda3cd --- /dev/null +++ b/services/bufferhub/include/bufferhub/BufferHubIdGenerator.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. + */ + +#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 incremental uint32_t id generator. +class BufferHubIdGenerator { +public: + // 0 is considered invalid + static constexpr uint32_t kInvalidId = 0U; + + // Get the singleton instance of this class + static BufferHubIdGenerator& getInstance(); + + // Gets next available id. If next id is greater than std::numeric_limits<uint32_t>::max() (2 ^ + // 32 - 1), it will try to get an id start from 1 again. + uint32_t getId(); + + // Free a specific id. Return true on freed, false on not found. + bool freeId(uint32_t id); + +private: + BufferHubIdGenerator() = default; + ~BufferHubIdGenerator() = default; + + std::mutex mIdsInUseMutex; + // Start from kInvalidID to avoid generating it. + uint32_t mLastId = kInvalidId; + std::set<uint32_t> 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 0000000000..f2c8ef8965 --- /dev/null +++ b/services/bufferhub/include/bufferhub/BufferHubService.h @@ -0,0 +1,72 @@ +/* + * 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 <mutex> +#include <random> + +#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::Return; +using hardware::graphics::common::V1_2::HardwareBufferDescription; + +class BufferHubService : public IBufferHub { +public: + 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; + + // 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 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); + + // TODO(b/118180214): use a more secure implementation + std::mt19937 mTokenEngine; + // The mapping from token to the client creates it. + std::mutex mTokenMapMutex; + std::map<uint32_t, const wp<BufferClient>> mTokenMap GUARDED_BY(mTokenMapMutex); +}; + +} // 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 0000000000..cf56c33ec0 --- /dev/null +++ b/services/bufferhub/include/bufferhub/BufferNode.h @@ -0,0 +1,94 @@ +#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/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 layer_count, uint32_t format, + uint64_t usage, size_t user_metadata_size, + uint32_t id = BufferHubIdGenerator::kInvalidId); + + ~BufferNode(); + + // Returns whether the object holds a valid metadata. + bool IsValid() const { return metadata_.IsValid(); } + + uint32_t id() const { return mId; } + + size_t user_metadata_size() const { return metadata_.user_metadata_size(); } + + // Accessors of the buffer description and handle + const native_handle_t* buffer_handle() const { return buffer_handle_; } + const AHardwareBuffer_Desc& buffer_desc() const { return buffer_desc_; } + + // Accessors of metadata. + const BufferHubMetadata& metadata() const { return metadata_; } + + // Gets the current value of active_clients_bit_mask in metadata_ with + // std::memory_order_acquire, so that all previous releases of + // active_clients_bit_mask from all threads will be returned here. + uint32_t GetActiveClientsBitMask() const; + + // Find and add a new client_state_mask to active_clients_bit_mask in + // metadata_. + // Return the new client_state_mask that is added to active_clients_bit_mask. + // Return 0U if there are already 16 clients of the buffer. + uint32_t AddNewActiveClientsBitToMask(); + + // Removes the value from active_clients_bit_mask in metadata_ with + // std::memory_order_release, so that the change will be visible to any + // acquire of active_clients_bit_mask_ 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* buffer_handle_; + AHardwareBuffer_Desc buffer_desc_; + + // Metadata in shared memory. + BufferHubMetadata metadata_; + + // A system-unique id generated by bufferhub from 1 to std::numeric_limits<uint32_t>::max(). + // BufferNodes not created by bufferhub will have id = 0, meaning "not specified". + // TODO(b/118891412): remove default id = 0 and update comments after pdx is no longer in use + const uint32_t mId = 0; + + // The following variables are atomic variables in metadata_ that are visible + // to Bn object and Bp objects. Please find more info in + // BufferHubDefs::MetadataHeader. + + // buffer_state_ tracks the state of the buffer. Buffer can be in one of these + // four states: gained, posted, acquired, released. + std::atomic<uint32_t>* buffer_state_ = nullptr; + + // TODO(b/112012161): add comments to fence_state_. + std::atomic<uint32_t>* fence_state_ = nullptr; + + // active_clients_bit_mask_ 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>* active_clients_bit_mask_ = 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 0000000000..084460d993 --- /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 0000000000..8d29923069 --- /dev/null +++ b/services/bufferhub/tests/Android.bp @@ -0,0 +1,25 @@ +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", + ], + 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 0000000000..fe010137fd --- /dev/null +++ b/services/bufferhub/tests/BufferHubIdGenerator_test.cpp @@ -0,0 +1,45 @@ +#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) { + uint32_t id = mIdGenerator->getId(); + EXPECT_NE(id, BufferHubIdGenerator::kInvalidId); + + EXPECT_TRUE(mIdGenerator->freeId(id)); + EXPECT_FALSE(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 size_t kTestSize = 10U; + uint32_t ids[kTestSize]; + for (size_t i = 0UL; i < kTestSize; ++i) { + ids[i] = mIdGenerator->getId(); + EXPECT_NE(ids[i], BufferHubIdGenerator::kInvalidId); + if (i >= 1) { + EXPECT_GT(ids[i], ids[i - 1]); + } + } +} + +} // 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 0000000000..ccb1197498 --- /dev/null +++ b/services/bufferhub/tests/BufferNode_test.cpp @@ -0,0 +1,111 @@ +#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 { + buffer_node = + new BufferNode(kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize); + ASSERT_TRUE(buffer_node->IsValid()); + } + + void TearDown() override { + if (buffer_node != nullptr) { + delete buffer_node; + } + } + + BufferNode* buffer_node = nullptr; +}; + +TEST_F(BufferNodeTest, TestCreateBufferNode) { + EXPECT_EQ(buffer_node->user_metadata_size(), 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(buffer_node->buffer_handle(), buffer_node->buffer_desc().width, + buffer_node->buffer_desc().height, + buffer_node->buffer_desc().layers, + buffer_node->buffer_desc().format, buffer_node->buffer_desc().usage, + buffer_node->buffer_desc().stride, &outHandle); + EXPECT_EQ(ret, OK); + EXPECT_THAT(outHandle, NotNull()); +} + +TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_twoNewClients) { + uint32_t new_client_state_mask_1 = buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), new_client_state_mask_1); + + // 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 new_client_state_mask_2 = buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), + new_client_state_mask_1 | new_client_state_mask_2); +} + +TEST_F(BufferNodeTest, TestAddNewActiveClientsBitToMask_32NewClients) { + uint32_t new_client_state_mask = 0U; + uint32_t current_mask = 0U; + uint32_t expected_mask = 0U; + + for (int i = 0; i < BufferHubDefs::kMaxNumberOfClients; ++i) { + new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_NE(new_client_state_mask, 0U); + EXPECT_FALSE(new_client_state_mask & current_mask); + expected_mask = current_mask | new_client_state_mask; + current_mask = buffer_node->GetActiveClientsBitMask(); + EXPECT_EQ(current_mask, expected_mask); + } + + // Method should fail upon requesting for more than maximum allowable clients. + new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_EQ(new_client_state_mask, 0U); + EXPECT_EQ(errno, E2BIG); +} + +TEST_F(BufferNodeTest, TestRemoveActiveClientsBitFromMask) { + buffer_node->AddNewActiveClientsBitToMask(); + uint32_t current_mask = buffer_node->GetActiveClientsBitMask(); + uint32_t new_client_state_mask = buffer_node->AddNewActiveClientsBitToMask(); + EXPECT_NE(buffer_node->GetActiveClientsBitMask(), current_mask); + + buffer_node->RemoveClientsBitFromMask(new_client_state_mask); + EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask); + + // Remove the test_mask again to the active client bit mask should not modify + // the value of active clients bit mask. + buffer_node->RemoveClientsBitFromMask(new_client_state_mask); + EXPECT_EQ(buffer_node->GetActiveClientsBitMask(), current_mask); +} + +} // namespace + +} // namespace implementation +} // namespace V1_0 +} // namespace bufferhub +} // namespace frameworks +} // namespace android diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 887119992c..0d8d34ff5d 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -16,39 +16,113 @@ cc_library_shared { name: "libinputflinger", srcs: [ - "EventHub.cpp", - "InputApplication.cpp", "InputDispatcher.cpp", - "InputListener.cpp", "InputManager.cpp", - "InputReader.cpp", - "InputWindow.cpp", ], shared_libs: [ + "libinputflinger_base", + "libinputreader", "libbase", "libbinder", - "libcrypto", "libcutils", "libinput", "liblog", "libutils", "libui", - "libhardware_legacy", ], 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 //-fvisibility=hidden ], - export_include_dirs: ["."], + export_include_dirs: [ + ".", + "include", + ], + +} + + +cc_library_headers { + name: "libinputflinger_headers", + + export_include_dirs: ["include"], +} + +cc_library_shared { + name: "libinputreader", + + srcs: [ + "EventHub.cpp", + "InputReader.cpp", + "InputReaderFactory.cpp", + "TouchVideoDevice.cpp", + ], + + shared_libs: [ + "libinputflinger_base", + "libbase", + "libcrypto", + "libcutils", + "libinput", + "liblog", + "libutils", + "libui", + "libhardware_legacy", + "libutils" + ], + + header_libs: [ + "libinputflinger_headers", + ], + + export_header_lib_headers: [ + "libinputflinger_headers", + ], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wno-unused-parameter", + ], +} + +cc_library_shared { + name: "libinputflinger_base", + + srcs: [ + "InputListener.cpp", + "InputReaderBase.cpp", + ], + + shared_libs: [ + "libbase", + "libinput", + "liblog", + "libutils", + ], + + header_libs: [ + "libinputflinger_headers", + ], + + export_header_lib_headers: [ + "libinputflinger_headers", + ], + + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wno-unused-parameter", + ], } subdirs = [ diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp index ccc24b950e..b2d2f14bf3 100644 --- a/services/inputflinger/EventHub.cpp +++ b/services/inputflinger/EventHub.cpp @@ -40,7 +40,6 @@ #include <hardware_legacy/power.h> #include <android-base/stringprintf.h> -#include <cutils/properties.h> #include <openssl/sha.h> #include <utils/Log.h> #include <utils/Timers.h> @@ -69,23 +68,27 @@ using android::base::StringPrintf; 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 +101,31 @@ static void getLinuxRelease(int* major, int* minor) { } } +/** + * Return true if name matches "v4l-touch*" + */ +static bool isV4lTouchNode(const char* name) { + return strstr(name, "v4l-touch") == name; +} + +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 +169,13 @@ uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { // --- 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)); @@ -172,9 +199,9 @@ void EventHub::Device::close() { } 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 +220,33 @@ bool EventHub::Device::hasValidFd() { // --- 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)); + 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)); 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 +264,7 @@ EventHub::EventHub(void) : 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 +295,21 @@ EventHub::~EventHub(void) { 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 +335,7 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, struct input_absinfo info; if(ioctl(device->fd, EVIOCGABS(axis), &info)) { ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", - axis, device->identifier.name.string(), device->fd, errno); + axis, device->identifier.name.c_str(), device->fd, errno); return -errno; } @@ -416,7 +444,7 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* 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; } @@ -465,7 +493,7 @@ status_t EventHub::mapKey(int32_t deviceId, 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 +502,13 @@ status_t EventHub::mapKey(int32_t deviceId, // 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 +539,7 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis 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; @@ -581,7 +608,7 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const { if (device) { return device->getKeyCharacterMap(); } - return NULL; + return nullptr; } bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, @@ -599,16 +626,16 @@ bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, 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 +643,12 @@ static String8 generateDescriptor(InputDeviceIdentifier& identifier) { // 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 +664,17 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { // 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 +691,7 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { 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 +704,7 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { 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,22 +726,22 @@ void EventHub::cancelVibrate(int32_t deviceId) { 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 { @@ -732,7 +759,17 @@ EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const return device; } } - return NULL; + return nullptr; +} + +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) { + return device; + } + } + return nullptr; } size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { @@ -763,7 +800,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz 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; @@ -782,10 +819,10 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz 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 +848,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz 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 +857,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz 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 +873,13 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz 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 == nullptr) { + ALOGW("Received unexpected epoll event 0x%08x for unknown device fd %d.", + eventItem.events, eventItem.data.fd); continue; } - Device* device = mDevices.valueAt(deviceIndex); if (eventItem.events & EPOLLIN) { int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity); @@ -866,86 +902,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz 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 +919,12 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } } 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()); } } @@ -1046,14 +1003,18 @@ void EventHub::wake() { } 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); + } + result = scanVideoDirLocked(VIDEO_DEVICE_PATH); + if (result != OK) { + ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH); } if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) { createVirtualKeyboardLocked(); @@ -1082,26 +1043,45 @@ static const int32_t GAMEPAD_KEYCODES[] = { 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; +status_t EventHub::registerFdForEpoll(int fd) { + 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; } - 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); + return OK; +} + +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::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; +} + 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 result = unregisterFdFromEpoll(device->fd); + if (result != OK) { + ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id); + return result; } } return OK; @@ -1122,17 +1102,17 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { // 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 +1143,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { //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 +1151,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { //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 +1159,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { // 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 +1167,10 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { " 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); @@ -1343,7 +1323,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { // 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 +1344,17 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { setLedForControllerLocked(device); } + // Find a matching video device by comparing device names + 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 +1365,11 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { 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 +1383,11 @@ void EventHub::configureFd(Device* device) { 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 +1407,36 @@ void EventHub::configureFd(Device* device) { // 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); + 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 +1446,7 @@ bool EventHub::isDeviceEnabled(int32_t deviceId) { 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 +1468,7 @@ status_t EventHub::enableDevice(int32_t deviceId) { 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 +1486,7 @@ void EventHub::createVirtualKeyboardLocked() { identifier.uniqueId = "<virtual>"; assignDescriptorLocked(identifier); - Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), identifier); + Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, "<virtual>", identifier); device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY | INPUT_DEVICE_CLASS_DPAD @@ -1491,26 +1504,26 @@ void EventHub::addDeviceLocked(Device* device) { 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) { // 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)) { + std::string path; + path += "/sys/board_properties/virtualkeys."; + path += device->identifier.name; + if (access(path.c_str(), R_OK)) { return NAME_NOT_FOUND; } return VirtualKeyMap::load(path, &device->virtualKeyMap); @@ -1543,7 +1556,7 @@ bool EventHub::deviceHasMicLocked(Device* device) { 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 @@ -1599,34 +1612,58 @@ status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) con 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) { + 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) { + mUnattachedVideoDevices.push_back(std::move(device->videoDevice)); + } releaseControllerNumberLocked(device); @@ -1634,9 +1671,9 @@ void EventHub::closeDeviceLocked(Device* device) { 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 +1685,7 @@ void EventHub::closeDeviceLocked(Device* device) { // 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 +1702,6 @@ void EventHub::closeDeviceLocked(Device* device) { 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 +1715,32 @@ status_t EventHub::readNotifyLocked() { 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 +1757,7 @@ status_t EventHub::scanDirLocked(const char *dirname) 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 +1774,30 @@ status_t EventHub::scanDirLocked(const char *dirname) 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 +1819,44 @@ void EventHub::dump(std::string& dump) { 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/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 9a449fa6d1..7fa9cb60d6 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/InputDispatcher.cpp @@ -58,6 +58,7 @@ #include <utils/Trace.h> #include <powermanager/PowerManager.h> #include <ui/Region.h> +#include <binder/Binder.h> #define INDENT " " #define INDENT2 " " @@ -96,6 +97,9 @@ constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; // 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); @@ -235,19 +239,26 @@ static void dumpRegion(std::string& dump, const Region& region) { } } +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); - mKeyRepeatState.lastKeyEntry = NULL; + mKeyRepeatState.lastKeyEntry = nullptr; policy->getDispatcherConfiguration(&mConfig); } @@ -360,7 +371,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // 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 +381,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } if (mNextUnblockedEvent == mPendingEvent) { - mNextUnblockedEvent = NULL; + mNextUnblockedEvent = nullptr; } switch (mPendingEvent->type) { @@ -481,16 +492,16 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { 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; @@ -515,9 +526,10 @@ void InputDispatcher::addRecentEventLocked(EventEntry* entry) { sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y) { // Traverse windows from front to back to find touched window. - size_t numWindows = mWindowHandles.size(); + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId == displayId) { int32_t flags = windowInfo->layoutParamsFlags; @@ -534,7 +546,7 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display } } } - return NULL; + return nullptr; } void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { @@ -663,7 +675,7 @@ void InputDispatcher::releasePendingEventLocked() { if (mPendingEvent) { resetANRTimeoutsLocked(); releaseInboundEventLocked(mPendingEvent); - mPendingEvent = NULL; + mPendingEvent = nullptr; } } @@ -676,7 +688,7 @@ void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); } if (entry == mNextUnblockedEvent) { - mNextUnblockedEvent = NULL; + mNextUnblockedEvent = nullptr; } addRecentEventLocked(entry); entry->release(); @@ -685,7 +697,7 @@ void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { void InputDispatcher::resetKeyRepeatLocked() { if (mKeyRepeatState.lastKeyEntry) { mKeyRepeatState.lastKeyEntry->release(); - mKeyRepeatState.lastKeyEntry = NULL; + mKeyRepeatState.lastKeyEntry = nullptr; } } @@ -701,8 +713,8 @@ InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cu 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); @@ -807,8 +819,11 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, 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; @@ -842,7 +857,8 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, return true; } - addMonitoringTargetsLocked(inputTargets); + // Add monitor channels from event's or focused display. + addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); @@ -851,11 +867,11 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, void InputDispatcher::logOutboundKeyDetailsLocked(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 @@ -909,7 +925,8 @@ bool InputDispatcher::dispatchMotionLocked( return true; } - addMonitoringTargetsLocked(inputTargets); + // Add monitor channels from event's or focused display. + addMonitoringTargetsLocked(inputTargets, getTargetDisplayId(entry)); // Dispatch the motion. if (conflictingPointerActions) { @@ -924,12 +941,13 @@ bool InputDispatcher::dispatchMotionLocked( void InputDispatcher::logOutboundMotionDetailsLocked(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, @@ -987,7 +1005,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, 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,7 +1014,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, mInputTargetWaitStartTime = currentTime; mInputTargetWaitTimeoutTime = LONG_LONG_MAX; mInputTargetWaitTimeoutExpired = false; - mInputTargetWaitApplicationHandle.clear(); + mInputTargetWaitApplicationToken.clear(); } } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { @@ -1006,9 +1024,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, 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 +1037,13 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, 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 +1069,13 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, } } +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 +1090,10 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); if (connectionIndex >= 0) { sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); - sp<InputWindowHandle> windowHandle = connection->inputWindowHandle; - - 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); - } - } + sp<IBinder> token = connection->inputChannel->getToken(); + + if (token != nullptr) { + removeWindowByTokenLocked(token); } if (connection->status == Connection::STATUS_NORMAL) { @@ -1103,7 +1121,33 @@ void InputDispatcher::resetANRTimeoutsLocked() { // 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, @@ -1111,41 +1155,48 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, 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); @@ -1186,7 +1237,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // 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 +1260,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, 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 +1277,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, 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; @@ -1246,9 +1299,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, bool isTouchModal = false; // Traverse windows from front to back to find touched window and outside targets. - size_t numWindows = mWindowHandles.size(); + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId != displayId) { continue; // wrong display @@ -1274,22 +1328,23 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Figure out whether splitting will be allowed for this window. - if (newTouchedWindowHandle != NULL + if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { // New window supports splitting. isSplit = true; } else if (isSplit) { // New window does not support splitting but we have already split events. // Ignore the new window. - newTouchedWindowHandle = NULL; + newTouchedWindowHandle = nullptr; } // Handle the case where we did not find a window. - if (newTouchedWindowHandle == NULL) { + if (newTouchedWindowHandle == nullptr) { // Try to assign the pointer to the first foreground window we find, if there is one. newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); - if (newTouchedWindowHandle == NULL) { - ALOGI("Dropping event because there is no touchable window at (%d, %d).", x, y); + if (newTouchedWindowHandle == nullptr) { + ALOGI("Dropping event because there is no touchable window at (%d, %d) in display " + "%" PRId32 ".", x, y, displayId); injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } @@ -1327,7 +1382,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, 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 +1400,12 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); if (oldTouchedWindowHandle != newTouchedWindowHandle - && newTouchedWindowHandle != NULL) { + && newTouchedWindowHandle != nullptr) { #if DEBUG_FOCUS - ALOGD("Touch is slipping out of window %s into window %s.", + ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, oldTouchedWindowHandle->getName().c_str(), - newTouchedWindowHandle->getName().c_str()); + newTouchedWindowHandle->getName().c_str(), + displayId); #endif // Make a slippery exit from the old window. mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, @@ -1380,7 +1436,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, 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 +1446,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // 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()); @@ -1418,7 +1474,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } if (! haveForegroundWindow) { #if DEBUG_FOCUS - ALOGD("Dropping event because there is no touched foreground window to receive it."); + ALOGD("Dropping event because there is no touched foreground window in display %" PRId32 + " to receive it.", displayId); #endif injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; @@ -1455,7 +1512,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, touchedWindow.windowHandle, entry, "touched"); if (!reason.empty()) { injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.windowHandle, nextWakeupTime, reason.c_str()); + nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str()); goto Unresponsive; } } @@ -1471,8 +1528,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, sp<InputWindowHandle> foregroundWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle->getInfo()->hasWallpaper) { - for (size_t i = 0; i < mWindowHandles.size(); i++) { - sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i); + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + sp<InputWindowHandle> windowHandle = windowHandles.itemAt(i); const InputWindowInfo* info = windowHandle->getInfo(); if (info->displayId == displayId && windowHandle->getInfo()->layoutParamsType @@ -1503,7 +1562,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, 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; @@ -1607,39 +1666,59 @@ Unresponsive: void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) { + sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); + if (inputChannel == nullptr) { + ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); + return; + } + inputTargets.push(); const InputWindowInfo* windowInfo = windowHandle->getInfo(); InputTarget& target = inputTargets.editTop(); - target.inputChannel = windowInfo->inputChannel; + 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; } -void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) { - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - inputTargets.push(); +void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, + int32_t displayId) { + std::unordered_map<int32_t, Vector<sp<InputChannel>>>::const_iterator it = + mMonitoringChannelsByDisplay.find(displayId); + + if (it != mMonitoringChannelsByDisplay.end()) { + const Vector<sp<InputChannel>>& monitoringChannels = it->second; + const size_t numChannels = monitoringChannels.size(); + for (size_t i = 0; i < numChannels; i++) { + inputTargets.push(); - 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; + InputTarget& target = inputTargets.editTop(); + target.inputChannel = monitoringChannels[i]; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.xOffset = 0; + target.yOffset = 0; + target.pointerIds.clear(); + target.globalScaleFactor = 1.0f; + } + } else { + // If there is no monitor channel registered or all monitor channel unregistered, + // the display can't detect the extra system gesture by a copy of input events. + ALOGW("There is no monitor channel found in display %" PRId32, displayId); } } bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, const InjectionState* injectionState) { if (injectionState - && (windowHandle == NULL + && (windowHandle == nullptr || windowHandle->getInfo()->ownerUid != injectionState->injectorUid) && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { - if (windowHandle != NULL) { + if (windowHandle != nullptr) { ALOGW("Permission denied: injecting event from pid %d uid %d to window %s " "owned by uid %d", injectionState->injectorPid, injectionState->injectorUid, @@ -1657,9 +1736,10 @@ bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& wind 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(); + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i); + sp<InputWindowHandle> otherHandle = windowHandles.itemAt(i); if (otherHandle == windowHandle) { break; } @@ -1677,10 +1757,11 @@ bool InputDispatcher::isWindowObscuredAtPointLocked( bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const { int32_t displayId = windowHandle->getInfo()->displayId; + const Vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - size_t numWindows = mWindowHandles.size(); + size_t numWindows = windowHandles.size(); for (size_t i = 0; i < numWindows; i++) { - sp<InputWindowHandle> otherHandle = mWindowHandles.itemAt(i); + sp<InputWindowHandle> otherHandle = windowHandles.itemAt(i); if (otherHandle == windowHandle) { break; } @@ -1704,7 +1785,8 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT } // 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 " @@ -1778,8 +1860,8 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT std::string InputDispatcher::getApplicationWindowLabelLocked( 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 +1869,7 @@ std::string InputDispatcher::getApplicationWindowLabelLocked( } else { return applicationHandle->getName(); } - } else if (windowHandle != NULL) { + } else if (windowHandle != nullptr) { return windowHandle->getName(); } else { return "<unknown application or window>"; @@ -1795,8 +1877,11 @@ std::string InputDispatcher::getApplicationWindowLabelLocked( } void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { - if (mFocusedWindowHandle != NULL) { - const InputWindowInfo* info = mFocusedWindowHandle->getInfo(); + int32_t displayId = getTargetDisplayId(eventEntry); + sp<InputWindowHandle> focusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + if (focusedWindowHandle != nullptr) { + const InputWindowInfo* info = focusedWindowHandle->getInfo(); if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) { #if DEBUG_DISPATCH_CYCLE ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); @@ -1838,11 +1923,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " - "xOffset=%f, yOffset=%f, scaleFactor=%f, " - "pointerIds=0x%x", + "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " + "windowScaleFactor=(%f, %f), pointerIds=0x%x", connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, - inputTarget->scaleFactor, inputTarget->pointerIds.value); + inputTarget->globalScaleFactor, + inputTarget->windowXScale, inputTarget->windowYScale, + inputTarget->pointerIds.value); #endif // Skip this event if the connection status is not normal. @@ -1919,7 +2006,8 @@ void InputDispatcher::enqueueDispatchEntryLocked( // 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) { @@ -2017,7 +2105,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // 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 +2123,15 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, 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; } @@ -2243,8 +2333,12 @@ void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - synthesizeCancelationEventsForInputChannelLocked(mMonitoringChannels[i], options); + for (auto& it : mMonitoringChannelsByDisplay) { + const Vector<sp<InputChannel>>& monitoringChannels = it.second; + const size_t numChannels = monitoringChannels.size(); + for (size_t i = 0; i < numChannels; i++) { + synthesizeCancelationEventsForInputChannelLocked(monitoringChannels[i], options); + } } } @@ -2291,15 +2385,17 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( InputTarget target; sp<InputWindowHandle> windowHandle = getWindowHandleLocked(connection->inputChannel); - if (windowHandle != NULL) { + 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 +2445,7 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet "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,9 +2477,11 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet } MotionEntry* splitMotionEntry = new MotionEntry( + originalMotionEntry->sequenceNum, originalMotionEntry->eventTime, originalMotionEntry->deviceId, originalMotionEntry->source, + originalMotionEntry->displayId, originalMotionEntry->policyFlags, action, originalMotionEntry->actionButton, @@ -2394,7 +2492,6 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, originalMotionEntry->downTime, - originalMotionEntry->displayId, splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry->injectionState) { @@ -2414,7 +2511,8 @@ void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChange { // acquire lock AutoMutex _l(mLock); - ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->eventTime); + ConfigurationChangedEntry* newEntry = + new ConfigurationChangedEntry(args->sequenceNum, args->eventTime); needWake = enqueueInboundEventLocked(newEntry); } // release lock @@ -2423,12 +2521,49 @@ void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChange } } +/** + * 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) { + AutoMutex _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. + AutoMutex _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 +2574,9 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { 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 +2588,11 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { 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 +2617,8 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { 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 +2637,11 @@ bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args 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++) { @@ -2573,7 +2685,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { 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->downTime, args->eventTime, @@ -2588,12 +2701,11 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { } // 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->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, - args->displayId, args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); needWake = enqueueInboundEventLocked(newEntry); @@ -2633,7 +2745,8 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { { // acquire lock AutoMutex _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 +2755,13 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { } } -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 +2775,29 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ 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 +2805,11 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ } 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; } @@ -2716,26 +2837,29 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ 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->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->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), displayId, + motionEvent->getDownTime(), uint32_t(pointerCount), pointerProperties, samplePointerCoords, motionEvent->getXOffset(), motionEvent->getYOffset()); lastInjectedEntry->next = nextInjectedEntry; @@ -2758,7 +2882,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t displ 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; @@ -2886,94 +3010,186 @@ void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* ent } } +Vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(int32_t displayId) const { + std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>>::const_iterator it = + mWindowHandlesByDisplay.find(displayId); + if(it != mWindowHandlesByDisplay.end()) { + return it->second; + } + + // Return an empty one if nothing found. + return 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; + for (auto& it : mWindowHandlesByDisplay) { + const Vector<sp<InputWindowHandle>> windowHandles = it.second; + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i); + if (windowHandle->getToken() == inputChannel->getToken()) { + 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; + for (auto& it : mWindowHandlesByDisplay) { + const Vector<sp<InputWindowHandle>> windowHandles = it.second; + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + if (windowHandles.itemAt(i)->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 Vector<sp<InputWindowHandle>>& inputWindowHandles, + int32_t displayId) { #if DEBUG_FOCUS - ALOGD("setInputWindows"); + ALOGD("setInputWindows displayId=%" PRId32, displayId); #endif { // acquire lock AutoMutex _l(mLock); - Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles; - mWindowHandles = inputWindowHandles; + // Copy old handles for release if they are no longer present. + const 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.isEmpty()) { + // 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 Vector<sp<InputWindowHandle>>& oldHandles = mWindowHandlesByDisplay[displayId]; + std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens; + for (size_t i = 0; i < oldHandles.size(); i++) { + const sp<InputWindowHandle>& handle = oldHandles.itemAt(i); + oldHandlesByTokens[handle->getToken()] = handle; } - if (windowHandle->getInfo()->hasFocus) { - newFocusedWindowHandle = windowHandle; + + const size_t numWindows = inputWindowHandles.size(); + Vector<sp<InputWindowHandle>> newHandles; + for (size_t i = 0; i < numWindows; i++) { + const sp<InputWindowHandle>& handle = inputWindowHandles.itemAt(i); + if (!handle->updateInfo() || getInputChannelLocked(handle->getToken()) == nullptr) { + ALOGE("Window handle %s has no registered input channel", + handle->getName().c_str()); + continue; + } + + if (handle->getInfo()->displayId != displayId) { + ALOGE("Window %s updated by wrong display %d, should belong to display %d", + handle->getName().c_str(), displayId, + handle->getInfo()->displayId); + continue; + } + + if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { + const sp<InputWindowHandle> oldHandle = + oldHandlesByTokens.at(handle->getToken()); + oldHandle->updateFrom(handle); + newHandles.push_back(oldHandle); + } else { + newHandles.push_back(handle); + } } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; + + for (size_t i = 0; i < newHandles.size(); i++) { + const sp<InputWindowHandle>& windowHandle = newHandles.itemAt(i); + if (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; + } + + if (mFocusedDisplayId == displayId) { + onFocusChangedLocked(newFocusedWindowHandle); } - mFocusedWindowHandle = 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); 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( @@ -2990,13 +3206,14 @@ void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inpu // 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++) { + size_t numWindows = oldWindowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { const sp<InputWindowHandle>& oldWindowHandle = oldWindowHandles.itemAt(i); if (!hasWindowHandleLocked(oldWindowHandle)) { #if DEBUG_FOCUS ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); #endif - oldWindowHandle->releaseInfo(); + oldWindowHandle->releaseChannel(); } } } // release lock @@ -3006,25 +3223,28 @@ void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inpu } 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); - if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) { - if (mFocusedApplicationHandle != inputApplicationHandle) { - if (mFocusedApplicationHandle != NULL) { + sp<InputApplicationHandle> oldFocusedApplicationHandle = + getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); + if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { + if (oldFocusedApplicationHandle != inputApplicationHandle) { + if (oldFocusedApplicationHandle != nullptr) { resetANRTimeoutsLocked(); - mFocusedApplicationHandle->releaseInfo(); + oldFocusedApplicationHandle->releaseInfo(); } - mFocusedApplicationHandle = inputApplicationHandle; + mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; } - } else if (mFocusedApplicationHandle != NULL) { + } else if (oldFocusedApplicationHandle != nullptr) { resetANRTimeoutsLocked(); - mFocusedApplicationHandle->releaseInfo(); - mFocusedApplicationHandle.clear(); + oldFocusedApplicationHandle->releaseInfo(); + oldFocusedApplicationHandle.clear(); + mFocusedApplicationHandlesByDisplay.erase(displayId); } #if DEBUG_FOCUS @@ -3036,6 +3256,65 @@ void InputDispatcher::setFocusedApplication( 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 + AutoMutex _l(mLock); + + if (mFocusedDisplayId != displayId) { + sp<InputWindowHandle> oldFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); + if (oldFocusedWindowHandle != nullptr) { + sp<InputChannel> inputChannel = + getInputChannelLocked(oldFocusedWindowHandle->getToken()); + if (inputChannel != nullptr) { + CancelationOptions options( + CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS, + "The display which contains this window no longer has focus."); + synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); + } + } + mFocusedDisplayId = displayId; + + // Sanity check + sp<InputWindowHandle> newFocusedWindowHandle = + getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + onFocusChangedLocked(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); @@ -3062,7 +3341,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { } #if DEBUG_FOCUS - //logDispatchStateLocked(); + logDispatchStateLocked(); #endif } // release lock @@ -3103,7 +3382,7 @@ bool InputDispatcher::transferTouchFocus(const sp<InputChannel>& fromChannel, sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromChannel); sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toChannel); - if (fromWindowHandle == NULL || toWindowHandle == NULL) { + if (fromWindowHandle == nullptr || toWindowHandle == nullptr) { #if DEBUG_FOCUS ALOGD("Cannot transfer focus because from or to window not found."); #endif @@ -3207,17 +3486,35 @@ void InputDispatcher::logDispatchStateLocked() { void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT "DispatchEnabled: %d\n", mDispatchEnabled); dump += StringPrintf(INDENT "DispatchFrozen: %d\n", mDispatchFrozen); + dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId); + + if (!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 "FocusedApplications: <none>\n"); + } - 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 (!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 "FocusedApplication: <null>\n"); + dump += StringPrintf(INDENT "FocusedWindows: <none>\n"); } - dump += StringPrintf(INDENT "FocusedWindow: name='%s'\n", - mFocusedWindowHandle != NULL ? mFocusedWindowHandle->getName().c_str() : "<null>"); if (!mTouchStatesByDisplay.isEmpty()) { dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); @@ -3243,44 +3540,57 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { 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(); - - 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); + if (!mWindowHandlesByDisplay.empty()) { + for (auto& it : mWindowHandlesByDisplay) { + const Vector<sp<InputWindowHandle>> windowHandles = it.second; + dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first); + if (!windowHandles.isEmpty()) { + dump += INDENT2 "Windows:\n"; + for (size_t i = 0; i < windowHandles.size(); i++) { + const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i); + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + + dump += StringPrintf(INDENT3 "%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], globalScale=%f, windowScale=(%f,%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->globalScaleFactor, + windowInfo->windowXScale, windowInfo->windowYScale); + dumpRegion(dump, windowInfo->touchableRegion); + dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); + dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + windowInfo->ownerPid, windowInfo->ownerUid, + windowInfo->dispatchingTimeout / 1000000.0); + } + } else { + dump += INDENT2 "Windows: <none>\n"; + } } } else { - dump += INDENT "Windows: <none>\n"; + dump += INDENT "Displays: <none>\n"; } - if (!mMonitoringChannels.isEmpty()) { - dump += INDENT "MonitoringChannels:\n"; - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - const sp<InputChannel>& channel = mMonitoringChannels[i]; - dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str()); - } + if (!mMonitoringChannelsByDisplay.empty()) { + for (auto& it : mMonitoringChannelsByDisplay) { + const Vector<sp<InputChannel>>& monitoringChannels = it.second; + dump += StringPrintf(INDENT "MonitoringChannels in display %" PRId32 ":\n", it.first); + const size_t numChannels = monitoringChannels.size(); + for (size_t i = 0; i < numChannels; i++) { + const sp<InputChannel>& channel = monitoringChannels[i]; + dump += StringPrintf(INDENT2 "%zu: '%s'\n", i, channel->getName().c_str()); + } + } } else { dump += INDENT "MonitoringChannels: <none>\n"; } @@ -3397,29 +3707,39 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { mConfig.keyRepeatTimeout * 0.000001f); } -status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, - const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { +status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, int32_t displayId) { #if DEBUG_REGISTRATION - ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().c_str(), - toString(monitor)); + ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32, + inputChannel->getName().c_str(), displayId); #endif { // acquire lock AutoMutex _l(mLock); + // If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE, + // treat inputChannel as monitor channel for displayId. + bool monitor = inputChannel->getToken() == nullptr && displayId != ADISPLAY_ID_NONE; + if (monitor) { + inputChannel->setToken(new BBinder()); + } + if (getConnectionIndexLocked(inputChannel) >= 0) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().c_str()); return BAD_VALUE; } - sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor); + sp<Connection> connection = new Connection(inputChannel, monitor); int fd = inputChannel->getFd(); mConnectionsByFd.add(fd, connection); + mInputChannelsByToken[inputChannel->getToken()] = inputChannel; + // Store monitor channel by displayId. if (monitor) { - mMonitoringChannels.push(inputChannel); + Vector<sp<InputChannel>>& monitoringChannels = + mMonitoringChannelsByDisplay[displayId]; + monitoringChannels.push(inputChannel); } mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); @@ -3462,6 +3782,8 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& i sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); mConnectionsByFd.removeItemsAt(connectionIndex); + mInputChannelsByToken.erase(inputChannel->getToken()); + if (connection->monitor) { removeMonitorChannelLocked(inputChannel); } @@ -3476,20 +3798,33 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& i } void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) { - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - if (mMonitoringChannels[i] == inputChannel) { - mMonitoringChannels.removeAt(i); - break; - } + for (auto it = mMonitoringChannelsByDisplay.begin(); + it != mMonitoringChannelsByDisplay.end(); ) { + Vector<sp<InputChannel>>& monitoringChannels = it->second; + const size_t numChannels = monitoringChannels.size(); + for (size_t i = 0; i < numChannels; i++) { + if (monitoringChannels[i] == inputChannel) { + monitoringChannels.removeAt(i); + break; + } + } + if (monitoringChannels.empty()) { + it = mMonitoringChannelsByDisplay.erase(it); + } else { + ++it; + } } } ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) { - ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd()); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); - if (connection->inputChannel.get() == inputChannel.get()) { - return connectionIndex; + if (inputChannel == nullptr) { + return -1; + } + + for (size_t i = 0; i < mConnectionsByFd.size(); i++) { + sp<Connection> connection = mConnectionsByFd.valueAt(i); + if (connection->inputChannel->getToken() == inputChannel->getToken()) { + return i; } } @@ -3516,6 +3851,13 @@ void InputDispatcher::onDispatchCycleBrokenLocked( commandEntry->connection = connection; } +void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& newFocus) { + sp<IBinder> token = newFocus != nullptr ? newFocus->getToken() : nullptr; + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyFocusChangedLockedInterruptible); + commandEntry->token = token; +} + void InputDispatcher::onANRLocked( nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, @@ -3528,7 +3870,7 @@ void InputDispatcher::onANRLocked( 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]; @@ -3546,7 +3888,8 @@ void InputDispatcher::onANRLocked( CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doNotifyANRLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; - commandEntry->inputWindowHandle = windowHandle; + commandEntry->inputChannel = windowHandle != nullptr ? + getInputChannelLocked(windowHandle->getToken()) : nullptr; commandEntry->reason = reason; } @@ -3566,25 +3909,33 @@ void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( 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> token = commandEntry->token; + mLock.unlock(); + mPolicy->notifyFocusChanged(token); + 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 +3948,9 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( 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", @@ -3671,171 +4024,174 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( 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) { + 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); + 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); + KeyEvent event; + initializeKeyEvent(&event, keyEntry); + event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED); - mLock.unlock(); + mLock.unlock(); - mPolicy->dispatchUnhandledKey(connection->inputWindowHandle, - &event, keyEntry->policyFlags, &event); + mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), + &event, keyEntry->policyFlags, &event); - mLock.lock(); + 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); + // 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); } - } 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) { + 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); + 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; - } + return false; + } - // Dispatch the unhandled key to the policy. + // 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); + 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); + KeyEvent event; + initializeKeyEvent(&event, keyEntry); - mLock.unlock(); + mLock.unlock(); - bool fallback = mPolicy->dispatchUnhandledKey(connection->inputWindowHandle, - &event, keyEntry->policyFlags, &event); + bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), + &event, keyEntry->policyFlags, &event); - mLock.lock(); + mLock.lock(); - if (connection->status != Connection::STATUS_NORMAL) { - connection->inputState.removeFallbackKey(originalKeyCode); - return false; - } + 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); + // 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); + 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())) { + // 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); - } + 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); + 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); - } + fallback = false; + fallbackKeyCode = AKEYCODE_UNKNOWN; + if (keyEntry->action != AKEY_EVENT_ACTION_UP) { + connection->inputState.setFallbackKey(originalKeyCode, + fallbackKeyCode); } + } #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()); + { + 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->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 (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); + ALOGD("Unhandled key event: Dispatching fallback key. " + "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", + originalKeyCode, fallbackKeyCode, keyEntry->metaState); #endif - return true; // restart the event - } else { + return true; // restart the event + } else { #if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Unhandled key event: No fallback key."); + ALOGD("Unhandled key event: No fallback key."); #endif - } } } return false; @@ -3855,7 +4211,7 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman } 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); } @@ -3932,9 +4288,10 @@ void InputDispatcher::InjectionState::release() { // --- 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 +4310,16 @@ void InputDispatcher::EventEntry::release() { 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 +4332,9 @@ void InputDispatcher::ConfigurationChangedEntry::appendDescription(std::string& // --- 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 +4349,12 @@ void InputDispatcher::DeviceResetEntry::appendDescription(std::string& msg) cons // --- 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 +4365,10 @@ InputDispatcher::KeyEntry::~KeyEntry() { } 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 +4384,20 @@ void InputDispatcher::KeyEntry::recycle() { // --- InputDispatcher::MotionEntry --- -InputDispatcher::MotionEntry::MotionEntry(nsecs_t eventTime, int32_t deviceId, - uint32_t source, uint32_t policyFlags, int32_t action, int32_t actionButton, +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, int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime, - int32_t displayId, uint32_t pointerCount, + 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), + deviceId(deviceId), source(source), displayId(displayId), action(action), + actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState), edgeFlags(edgeFlags), xPrecision(xPrecision), yPrecision(yPrecision), - downTime(downTime), displayId(displayId), pointerCount(pointerCount) { + 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 +4411,12 @@ InputDispatcher::MotionEntry::~MotionEntry() { } 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, " + "edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, pointers=[", + deviceId, source, displayId, motionActionToString(action).c_str(), actionButton, flags, + metaState, buttonState, edgeFlags, xPrecision, yPrecision); + for (uint32_t i = 0; i < pointerCount; i++) { if (i) { msg += ", "; @@ -4072,10 +4433,12 @@ void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const { 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; } @@ -4184,8 +4547,8 @@ bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, } #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; } @@ -4237,8 +4600,8 @@ bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, } #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; } @@ -4250,8 +4613,9 @@ bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, 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; } @@ -4276,6 +4640,7 @@ ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const const KeyMemento& memento = mKeyMementos.itemAt(i); if (memento.deviceId == entry->deviceId && memento.source == entry->source + && memento.displayId == entry->displayId && memento.keyCode == entry->keyCode && memento.scanCode == entry->scanCode) { return i; @@ -4303,6 +4668,7 @@ void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t f KeyMemento& memento = mKeyMementos.editTop(); memento.deviceId = entry->deviceId; memento.source = entry->source; + memento.displayId = entry->displayId; memento.keyCode = entry->keyCode; memento.scanCode = entry->scanCode; memento.metaState = entry->metaState; @@ -4317,11 +4683,11 @@ void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, MotionMemento& memento = mMotionMementos.editTop(); 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; @@ -4340,8 +4706,8 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim for (size_t i = 0; i < mKeyMementos.size(); i++) { const KeyMemento& memento = mKeyMementos.itemAt(i); if (shouldCancelKey(memento, options)) { - outEvents.push(new KeyEntry(currentTime, - memento.deviceId, memento.source, memento.policyFlags, + outEvents.push(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)); } @@ -4350,14 +4716,13 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim for (size_t i = 0; i < mMotionMementos.size(); i++) { const MotionMemento& memento = mMotionMementos.itemAt(i); if (shouldCancelMotion(memento, options)) { - outEvents.push(new MotionEntry(currentTime, - memento.deviceId, memento.source, memento.policyFlags, + outEvents.push(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, + memento.deviceId, memento.source, memento.displayId, memento.policyFlags, memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL, memento.flags, 0, 0, 0, 0, memento.xPrecision, memento.yPrecision, memento.downTime, - memento.displayId, memento.pointerCount, memento.pointerProperties, memento.pointerCoords, 0, 0)); } @@ -4424,6 +4789,8 @@ bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento, return true; case CancelationOptions::CANCEL_FALLBACK_EVENTS: return memento.flags & AKEY_EVENT_FLAG_FALLBACK; + case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS: + return memento.displayId == ADISPLAY_ID_NONE; default: return false; } @@ -4442,6 +4809,8 @@ bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& mement return memento.source & AINPUT_SOURCE_CLASS_POINTER; case CancelationOptions::CANCEL_NON_POINTER_EVENTS: return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); + case CancelationOptions::CANCEL_DISPLAY_UNSPECIFIED_EVENTS: + return memento.displayId == ADISPLAY_ID_NONE; default: return false; } @@ -4450,9 +4819,8 @@ bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& mement // --- 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 +4829,8 @@ InputDispatcher::Connection::~Connection() { } const std::string InputDispatcher::Connection::getWindowName() const { - if (inputWindowHandle != NULL) { - return inputWindowHandle->getName(); + if (inputChannel != nullptr) { + return inputChannel->getName(); } if (monitor) { return "monitor"; @@ -4487,19 +4855,19 @@ const char* InputDispatcher::Connection::getStatusLabel() const { } InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { - for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) { + for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) { if (entry->seq == seq) { return entry; } } - return NULL; + return nullptr; } // --- InputDispatcher::CommandEntry --- InputDispatcher::CommandEntry::CommandEntry(Command command) : - command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0), + command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0), seq(0), handled(false) { } @@ -4510,7 +4878,7 @@ InputDispatcher::CommandEntry::~CommandEntry() { // --- 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,7 +4889,7 @@ void InputDispatcher::TouchState::reset() { split = false; deviceId = -1; source = 0; - displayId = -1; + displayId = ADISPLAY_ID_NONE; windows.clear(); } @@ -4569,6 +4937,15 @@ void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& wind } } +void InputDispatcher::TouchState::removeWindowByToken(const sp<IBinder>& token) { + for (size_t i = 0; i < windows.size(); i++) { + if (windows.itemAt(i).windowHandle->getToken() == token) { + windows.removeAt(i); + return; + } + } +} + void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { for (size_t i = 0 ; i < windows.size(); ) { TouchedWindow& window = windows.editItemAt(i); @@ -4590,7 +4967,7 @@ sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandl return window.windowHandle; } } - return NULL; + return nullptr; } bool InputDispatcher::TouchState::isSlippery() const { diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h index 8da8450380..05b5dadc66 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -18,7 +18,9 @@ #define _UI_INPUT_DISPATCHER_H #include <input/Input.h> +#include <input/InputApplication.h> #include <input/InputTransport.h> +#include <input/InputWindow.h> #include <utils/KeyedVector.h> #include <utils/Vector.h> #include <utils/threads.h> @@ -27,13 +29,13 @@ #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" @@ -158,7 +160,9 @@ struct InputTarget { // Scaling factor to apply to MotionEvent as it is delivered. // (ignored for KeyEvents) - float scaleFactor; + float globalScaleFactor; + float windowXScale = 1.0f; + float windowYScale = 1.0f; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. @@ -207,11 +211,12 @@ public: /* 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>& token) = 0; /* Gets the input dispatcher configuration. */ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; @@ -242,12 +247,12 @@ public: virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) = 0; /* Allows the policy a chance to intercept a key before dispatching. */ - virtual nsecs_t interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle, + virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent* keyEvent, uint32_t policyFlags) = 0; /* Allows the policy a chance to perform default processing for an unhandled key. * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */ - virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle, + virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0; /* Notifies the policy about switch events. @@ -299,7 +304,7 @@ public: * * 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 +312,21 @@ public: * * 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 Vector<sp<InputWindowHandle> >& inputWindowHandles, + int32_t displayId) = 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. * @@ -338,13 +350,19 @@ public: virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel, const sp<InputChannel>& toChannel) = 0; - /* Registers or unregister input channels that may be used as targets for input events. - * If monitor is true, the channel will receive a copy of all input events. + /* Registers input channels that may be used as targets for input events. + * If inputWindowHandle is null, and displayId is not ADISPLAY_ID_NONE, + * the channel will receive a copy of all input events form the specific displayId. * - * These methods may be called on any thread (usually by the input manager). + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t registerInputChannel( + const sp<InputChannel>& inputChannel, int32_t displayId) = 0; + + /* Unregister input channels that will no longer receive input events. + * + * This method may be called on any thread (usually by the input manager). */ - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, - const sp<InputWindowHandle>& inputWindowHandle, bool monitor) = 0; virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; }; @@ -383,12 +401,15 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args); virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args); - virtual int32_t injectInputEvent(const InputEvent* event, int32_t displayId, + virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, uint32_t policyFlags); - virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles); - virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle); + virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles, + int32_t displayId); + virtual void setFocusedApplication(int32_t displayId, + const sp<InputApplicationHandle>& inputApplicationHandle); + virtual void setFocusedDisplay(int32_t displayId); virtual void setInputDispatchMode(bool enabled, bool frozen); virtual void setInputFilterEnabled(bool enabled); @@ -396,7 +417,7 @@ public: const sp<InputChannel>& toChannel); virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, - const sp<InputWindowHandle>& inputWindowHandle, bool monitor); + int32_t displayId); virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); private: @@ -406,7 +427,7 @@ private: T* prev; protected: - inline Link() : next(NULL), prev(NULL) { } + inline Link() : next(nullptr), prev(nullptr) { } }; struct InjectionState { @@ -433,6 +454,7 @@ private: TYPE_MOTION }; + uint32_t sequenceNum; mutable int32_t refCount; int32_t type; nsecs_t eventTime; @@ -441,20 +463,20 @@ private: bool dispatchInProgress; // initially false, set to true while dispatching - inline bool isInjected() const { return injectionState != NULL; } + inline bool isInjected() const { return injectionState != nullptr; } void release(); virtual void appendDescription(std::string& msg) const = 0; protected: - EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags); + EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); void releaseInjectionState(); }; struct ConfigurationChangedEntry : EventEntry { - explicit ConfigurationChangedEntry(nsecs_t eventTime); + explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime); virtual void appendDescription(std::string& msg) const; protected: @@ -464,7 +486,7 @@ private: struct DeviceResetEntry : EventEntry { int32_t deviceId; - DeviceResetEntry(nsecs_t eventTime, int32_t deviceId); + DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); virtual void appendDescription(std::string& msg) const; protected: @@ -474,6 +496,7 @@ private: struct KeyEntry : EventEntry { int32_t deviceId; uint32_t source; + int32_t displayId; int32_t action; int32_t flags; int32_t keyCode; @@ -493,9 +516,9 @@ private: 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,6 +531,7 @@ private: nsecs_t eventTime; int32_t deviceId; uint32_t source; + int32_t displayId; int32_t action; int32_t actionButton; int32_t flags; @@ -517,17 +541,15 @@ private: 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, + 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 +566,9 @@ private: 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 +576,8 @@ private: 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 +625,12 @@ private: 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> token; }; // Generic queue implementation. @@ -614,7 +640,7 @@ private: 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 +655,7 @@ private: } else { head = entry; } - entry->next = NULL; + entry->next = nullptr; tail = entry; } @@ -641,7 +667,7 @@ private: } else { tail = entry; } - entry->prev = NULL; + entry->prev = nullptr; head = entry; } @@ -664,9 +690,9 @@ private: T* entry = head; head = entry->next; if (head) { - head->prev = NULL; + head->prev = nullptr; } else { - tail = NULL; + tail = nullptr; } return entry; } @@ -683,6 +709,10 @@ private: CANCEL_POINTER_EVENTS = 1, CANCEL_NON_POINTER_EVENTS = 2, CANCEL_FALLBACK_EVENTS = 3, + + /* Cancel events where the display not specified. These events would go to the focused + * display. */ + CANCEL_DISPLAY_UNSPECIFIED_EVENTS = 4, }; // The criterion to use to determine which events should be canceled. @@ -754,6 +784,7 @@ private: struct KeyMemento { int32_t deviceId; uint32_t source; + int32_t displayId; int32_t keyCode; int32_t scanCode; int32_t metaState; @@ -765,11 +796,11 @@ private: 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]; @@ -812,7 +843,6 @@ private: Status status; sp<InputChannel> inputChannel; // never null - sp<InputWindowHandle> inputWindowHandle; // may be null bool monitor; InputPublisher inputPublisher; InputState inputState; @@ -828,8 +858,7 @@ private: // 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(); } @@ -896,10 +925,17 @@ private: // All registered connections mapped by channel file descriptor. KeyedVector<int, sp<Connection> > mConnectionsByFd; + 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; + ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel); - // Input channels that will receive a copy of all input events. - Vector<sp<InputChannel> > mMonitoringChannels; + // Input channels that will receive a copy of all input events sent to the provided display. + std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay; // Event injection and synchronization. Condition mInjectionResultAvailableCondition; @@ -932,6 +968,9 @@ private: }; // Maps the key code replaced, device id tuple to the key code it was replaced with KeyedVector<KeyReplacement, int32_t> mReplacedKeys; + // 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; @@ -952,13 +991,15 @@ private: bool mDispatchFrozen; bool mInputFilterEnabled; - Vector<sp<InputWindowHandle> > mWindowHandles; - + std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay; + // Get window handles by display, return an empty vector if not found. + Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const; sp<InputWindowHandle> getWindowHandleLocked(const sp<InputChannel>& inputChannel) const; + sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const; bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const; // Focus tracking for keys, trackball, etc. - sp<InputWindowHandle> mFocusedWindowHandle; + std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay; // Focus tracking for touch. struct TouchedWindow { @@ -981,6 +1022,7 @@ private: void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds); void removeWindow(const sp<InputWindowHandle>& windowHandle); + void removeWindowByToken(const sp<IBinder>& token); void filterNonAsIsTouchWindows(); sp<InputWindowHandle> getFirstForegroundWindowHandle() const; bool isSlippery() const; @@ -989,8 +1031,11 @@ private: KeyedVector<int32_t, TouchState> mTouchStatesByDisplay; TouchState mTempTouchState; - // Focused application. - sp<InputApplicationHandle> mFocusedApplicationHandle; + // Focused applications. + std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay; + + // Top focused display. + int32_t mFocusedDisplayId; // Dispatcher state at time of last ANR. std::string mLastANRState; @@ -1023,7 +1068,7 @@ private: nsecs_t mInputTargetWaitStartTime; nsecs_t mInputTargetWaitTimeoutTime; bool mInputTargetWaitTimeoutExpired; - sp<InputApplicationHandle> mInputTargetWaitApplicationHandle; + sp<IBinder> mInputTargetWaitApplicationToken; // Contains the last window which received a hover event. sp<InputWindowHandle> mLastHoverWindowHandle; @@ -1033,11 +1078,15 @@ private: const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason); + + void removeWindowByTokenLocked(const sp<IBinder>& token); + void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, const sp<InputChannel>& inputChannel); nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime); void resetANRTimeoutsLocked(); + int32_t getTargetDisplayId(const EventEntry* entry); int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime); int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, @@ -1046,7 +1095,7 @@ private: void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets); - void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets); + void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId); void pokeUserActivityLocked(const EventEntry* eventEntry); bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, @@ -1111,6 +1160,7 @@ private: nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled); void onDispatchCycleBrokenLocked( nsecs_t currentTime, const sp<Connection>& connection); + void onFocusChangedLocked(const sp<InputWindowHandle>& newFocus); void onANRLocked( nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, @@ -1119,6 +1169,7 @@ private: // Outbound policy interactions. void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry); void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry); + void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry); void doNotifyANRLockedInterruptible(CommandEntry* commandEntry); void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry); void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry); diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 520fea4c95..b4eb37026b 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -26,13 +26,14 @@ namespace android { // --- NotifyConfigurationChangedArgs --- -NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs(nsecs_t eventTime) : - eventTime(eventTime) { +NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs( + uint32_t sequenceNum, nsecs_t eventTime) : + NotifyArgs(sequenceNum), eventTime(eventTime) { } NotifyConfigurationChangedArgs::NotifyConfigurationChangedArgs( const NotifyConfigurationChangedArgs& other) : - eventTime(other.eventTime) { + NotifyArgs(other.sequenceNum), eventTime(other.eventTime) { } void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& listener) const { @@ -42,18 +43,19 @@ void NotifyConfigurationChangedArgs::notify(const sp<InputListenerInterface>& li // --- 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(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), eventTime(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) { @@ -66,19 +68,22 @@ void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const { // --- 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, + int32_t buttonState, 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(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), + 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,13 +91,14 @@ NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t } NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : - eventTime(other.eventTime), deviceId(other.deviceId), source(other.source), - policyFlags(other.policyFlags), + NotifyArgs(other.sequenceNum), eventTime(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), + 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]); @@ -106,14 +112,14 @@ void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const // --- 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(eventTime), policyFlags(policyFlags), switchValues(switchValues), switchMask(switchMask) { } NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) : - eventTime(other.eventTime), policyFlags(other.policyFlags), + NotifyArgs(other.sequenceNum), eventTime(other.eventTime), policyFlags(other.policyFlags), switchValues(other.switchValues), switchMask(other.switchMask) { } @@ -124,12 +130,13 @@ void NotifySwitchArgs::notify(const sp<InputListenerInterface>& listener) const // --- 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(eventTime), deviceId(deviceId) { } NotifyDeviceResetArgs::NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other) : - eventTime(other.eventTime), deviceId(other.deviceId) { + NotifyArgs(other.sequenceNum), eventTime(other.eventTime), deviceId(other.deviceId) { } void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) const { diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 519faa6b5c..15d80703aa 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -19,25 +19,22 @@ //#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) { + mReader = createInputReader(readerPolicy, mDispatcher); initialize(); } @@ -90,4 +87,44 @@ 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 Vector<InputWindowInfo>& infos) { + std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> handlesPerDisplay; + + Vector<sp<InputWindowHandle>> handles; + for (const auto& info : infos) { + handlesPerDisplay.emplace(info.displayId, Vector<sp<InputWindowHandle>>()); + handlesPerDisplay[info.displayId].add(new BinderWindowHandle(info)); + } + for (auto const& i : handlesPerDisplay) { + mDispatcher->setInputWindows(i.second, i.first); + } +} + +// 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 a213b2dfaa..8f7551e5b2 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -22,18 +22,20 @@ */ #include "EventHub.h" -#include "InputReader.h" +#include "InputReaderBase.h" #include "InputDispatcher.h" #include <input/Input.h> #include <input/InputTransport.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,27 +75,26 @@ public: 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<InputDispatcherInterface> getDispatcher(); + virtual void setInputWindows(const Vector<InputWindowInfo>& handles); + + virtual void registerInputChannel(const sp<InputChannel>& channel); + virtual void unregisterInputChannel(const sp<InputChannel>& channel); + private: sp<InputReaderInterface> mReader; sp<InputReaderThread> mReaderThread; diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index efa6f88a87..73fcb1124d 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -226,7 +226,7 @@ static float calculateCommonVector(float a, float b) { } 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 +236,31 @@ static void synthesizeButtonKey(InputReaderContext* context, int32_t action, || (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); @@ -473,10 +398,10 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { 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 +413,7 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { } 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 +426,10 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { 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 +546,7 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) { updateGlobalMetaStateLocked(); // Enqueue configuration changed. - NotifyConfigurationChangedArgs args(when); + NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when); mQueuedListener->notifyConfigurationChanged(&args); } @@ -687,7 +612,7 @@ bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, 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; @@ -894,7 +819,7 @@ void InputReader::dump(std::string& dump) { if (i != 0) { dump += ", "; } - dump += mConfig.excludedDeviceNames.itemAt(i).string(); + dump += mConfig.excludedDeviceNames[i]; } dump += "]\n"; dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n", @@ -1019,22 +944,10 @@ EventHubInterface* InputReader::ContextImpl::getEventHub() { 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,12 +987,18 @@ void InputDevice::setEnabled(bool enabled, nsecs_t when) { 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()); @@ -1135,7 +1054,7 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config 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,6 +1068,20 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config setEnabled(enabled, when); } + 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); + } + } + } + size_t numMappers = mMappers.size(); for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; @@ -1196,7 +1129,7 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { #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 { @@ -1334,7 +1267,7 @@ void InputDevice::bumpGeneration() { } void InputDevice::notifyReset(nsecs_t when) { - NotifyDeviceResetArgs args(when, mId); + NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId); mContext->getListener()->notifyDeviceReset(&args); } @@ -1786,7 +1719,7 @@ void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { // --- 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 +2040,8 @@ void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { 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 +2174,7 @@ void VibratorInputMapper::dump(std::string& dump) { 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 +2184,20 @@ uint32_t KeyboardInputMapper::getSources() { 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 +2209,12 @@ void KeyboardInputMapper::dump(std::string& dump) { 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 +2225,8 @@ void KeyboardInputMapper::configure(nsecs_t when, } 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 +2249,7 @@ void KeyboardInputMapper::configureParameters() { 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 +2263,6 @@ void KeyboardInputMapper::configureParameters() { 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,8 +2357,8 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, 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. @@ -2461,7 +2395,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, // 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 +2423,8 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, 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 +2633,13 @@ void CursorInputMapper::configure(nsecs_t when, } 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; } bumpGeneration(); } @@ -2826,8 +2758,8 @@ void CursorInputMapper::sync(nsecs_t when) { 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); @@ -2874,7 +2806,7 @@ void CursorInputMapper::sync(nsecs_t when) { // 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 +2826,21 @@ void CursorInputMapper::sync(nsecs_t when) { 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); + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&releaseArgs); } } - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, - motionEventAction, 0, 0, metaState, currentButtonState, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime); + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); if (buttonsPressed) { @@ -2915,11 +2848,11 @@ void CursorInputMapper::sync(nsecs_t when) { 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, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&pressArgs); } } @@ -2929,11 +2862,11 @@ void CursorInputMapper::sync(nsecs_t when) { // 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, + NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime); + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&hoverArgs); } @@ -2942,18 +2875,19 @@ void CursorInputMapper::sync(nsecs_t when) { 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); + /* 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,7 +2902,7 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod } void CursorInputMapper::fadePointer() { - if (mPointerController != NULL) { + if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); } } @@ -3019,9 +2953,10 @@ void RotaryEncoderInputMapper::configure(nsecs_t when, 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 +3007,12 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { int32_t metaState = mContext->getGlobalMetaState(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, 0, AMOTION_EVENT_EDGE_FLAG_NONE, - displayId, /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, 0); + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + 0, 0, 0, /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } @@ -3394,10 +3330,15 @@ void TouchInputMapper::configureParameters() { 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 +3413,59 @@ bool TouchInputMapper::hasExternalStylus() const { 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 +3499,30 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // 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; } - // 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; - // Get associated display dimensions. - DisplayViewport newViewport; - if (mParameters.hasAssociatedDisplay) { - const String8* uniqueDisplayId = NULL; - ViewportType viewportTypeToUse; + 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; + } - 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; - } + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); - 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 +3574,12 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { 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; @@ -3632,7 +3615,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // Create pointer controller if needed. if (mDeviceMode == DEVICE_MODE_POINTER || (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { - if (mPointerController == NULL) { + if (mPointerController == nullptr) { mPointerController = getPolicy()->obtainPointerController(getDeviceId()); } } else { @@ -3642,7 +3625,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { 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 +3893,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { } 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); @@ -3946,8 +3919,8 @@ void TouchInputMapper::configureVirtualKeys() { 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 = @@ -4286,7 +4259,7 @@ void TouchInputMapper::reset(nsecs_t when) { mPointerSimple.reset(); resetExternalStylus(); - if (mPointerController != NULL) { + if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); mPointerController->clearSpots(); } @@ -4448,7 +4421,8 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when) { // 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,7 +4469,7 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when) { 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); @@ -4520,7 +4494,8 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when) { // 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 +4710,9 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, 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); } @@ -5428,11 +5404,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, mViewport.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); + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + 0, 0, mPointerGesture.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -5473,7 +5450,7 @@ void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) mPointerVelocityControl.reset(); // Remove any current spots. - if (mPointerController != NULL) { + if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); mPointerController->clearSpots(); } @@ -6336,7 +6313,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering) { int32_t metaState = getContext()->getGlobalMetaState(); - if (mPointerController != NULL) { + if (mPointerController != nullptr) { if (down || hovering) { mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); mPointerController->clearSpots(); @@ -6351,12 +6328,13 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, 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, mViewport.displayId, policyFlags, + AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, 0, + /* deviceTimestamp */ 0, + 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6364,12 +6342,13 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime); + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6379,22 +6358,24 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* 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, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime); + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6403,32 +6384,34 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* 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, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* 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 +6419,13 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, mViewport.displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mViewport.displayId, /* deviceTimestamp */ 0, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime); + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6499,10 +6483,11 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 } } - NotifyMotionArgs args(when, getDeviceId(), source, policyFlags, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + source, mViewport.displayId, policyFlags, action, actionButton, flags, metaState, buttonState, edgeFlags, - mViewport.displayId, deviceTimestamp, pointerCount, pointerProperties, pointerCoords, - xPrecision, yPrecision, downTime); + deviceTimestamp, pointerCount, pointerProperties, pointerCoords, + xPrecision, yPrecision, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -6535,7 +6520,7 @@ bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties } void TouchInputMapper::fadePointer() { - if (mPointerController != NULL) { + if (mPointerController != nullptr) { mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); } } @@ -6574,7 +6559,7 @@ const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit( } } - return NULL; + return nullptr; } void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { @@ -6923,7 +6908,7 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { #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 +6999,7 @@ void MultiTouchInputMapper::configureRawPointerAxes() { 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 +7242,7 @@ void JoystickInputMapper::configure(nsecs_t when, // 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 +7264,7 @@ void JoystickInputMapper::configure(nsecs_t when, } 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 +7293,7 @@ void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { 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 +7408,11 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { // TODO: Use the input device configuration to control this behavior more finely. uint32_t policyFlags = 0; - NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags, + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, 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); + /* 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 2f98e69800..35f3c232c7 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. */ @@ -390,6 +96,8 @@ public: virtual InputReaderPolicyInterface* getPolicy() = 0; virtual InputListenerInterface* getListener() = 0; virtual EventHubInterface* getEventHub() = 0; + + virtual uint32_t getNextSequenceNum() = 0; }; @@ -462,6 +170,7 @@ protected: virtual InputReaderPolicyInterface* getPolicy(); virtual InputListenerInterface* getListener(); virtual EventHubInterface* getEventHub(); + virtual uint32_t getNextSequenceNum(); } mContext; friend class ContextImpl; @@ -477,6 +186,9 @@ private: 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]; @@ -528,19 +240,6 @@ private: }; -/* 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,13 +251,16 @@ public: 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; } @@ -624,13 +326,14 @@ private: int32_t mGeneration; int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; - String8 mAlias; + std::string mAlias; uint32_t mClasses; Vector<InputMapper*> mMappers; uint32_t mSources; bool mIsExternal; + std::optional<uint8_t> mAssociatedDisplayPort; bool mHasMic; bool mDropUntilNextSync; @@ -773,6 +476,8 @@ struct RawPointerAxes { 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(); }; @@ -980,7 +685,7 @@ public: 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(); } @@ -1094,6 +799,9 @@ public: virtual void updateMetaState(int32_t keyCode); private: + // The current viewport. + std::optional<DisplayViewport> mViewport; + struct KeyDown { int32_t keyCode; int32_t scanCode; @@ -1102,8 +810,6 @@ private: uint32_t mSource; int32_t mKeyboardType; - int32_t mOrientation; // orientation for dpad keys - Vector<KeyDown> mKeyDowns; // keys that are down int32_t mMetaState; nsecs_t mDownTime; // time of most recent key down @@ -1120,7 +826,6 @@ private: // Immutable configuration parameters. struct Parameters { - bool hasAssociatedDisplay; bool orientationAware; bool handlesKeyRepeat; } mParameters; @@ -1128,6 +833,9 @@ private: void configureParameters(); void dumpParameters(std::string& dump); + int32_t getOrientation(); + int32_t getDisplayId(); + bool isKeyboardOrGamepadKey(int32_t scanCode); bool isMediaKey(int32_t keyCode); @@ -1305,7 +1013,7 @@ protected: bool associatedDisplayIsExternal; bool orientationAware; bool hasButtonUnderPad; - String8 uniqueDisplayId; + std::string uniqueDisplayId; enum GestureMode { GESTURE_MODE_SINGLE_TOUCH, @@ -1803,6 +1511,8 @@ private: VelocityControl mWheelXVelocityControl; VelocityControl mWheelYVelocityControl; + std::optional<DisplayViewport> findViewport(); + void resetExternalStylus(); void clearStylusDataPendingFlags(); diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp new file mode 100644 index 0000000000..f48a64551e --- /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/inputflinger/InputReaderFactory.cpp b/services/inputflinger/InputReaderFactory.cpp new file mode 100644 index 0000000000..3534f6b760 --- /dev/null +++ b/services/inputflinger/InputReaderFactory.cpp @@ -0,0 +1,28 @@ +/* + * 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 "InputReaderFactory.h" +#include "InputReader.h" + +namespace android { + +sp<InputReaderInterface> createInputReader( + const sp<InputReaderPolicyInterface>& policy, + const sp<InputListenerInterface>& listener) { + return new InputReader(new EventHub(), policy, listener); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/InputWindow.cpp b/services/inputflinger/InputWindow.cpp deleted file mode 100644 index 3ae7972779..0000000000 --- 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 0000000000..ad70ccc46d --- /dev/null +++ b/services/inputflinger/TouchVideoDevice.cpp @@ -0,0 +1,247 @@ +/* + * 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 width, uint32_t height, + const std::array<const int16_t*, NUM_BUFFERS>& readLocations) : + mFd(fd), mName(std::move(name)), mPath(std::move(devicePath)), + mWidth(width), mHeight(height), + 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; + 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 width = v4l2_fmt.fmt.pix.width; + const uint32_t height = v4l2_fmt.fmt.pix.height; + ALOGI("Frame dimensions: width = %" PRIu32 " height = %" PRIu32, width, height); + + struct v4l2_requestbuffers req; + req.count = NUM_BUFFERS; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + 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; + 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 != width * height * 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), width, height, 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(mWidth * mHeight); + const int16_t* readFrom = mReadLocations[buf.index]; + std::copy(readFrom, readFrom + mWidth * mHeight, data.begin()); + TouchVideoFrame frame(mWidth, mHeight, 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, mWidth * mHeight * 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) : width=%" PRIu32 ", height=%" PRIu32 + ", fd=%i, hasValidFd=%s", + mName.c_str(), mPath.c_str(), mWidth, mHeight, mFd.get(), + hasValidFd() ? "true" : "false"); +} + +} // namespace android diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp index 775dbdc658..cbe0190dfa 100644 --- a/services/inputflinger/host/Android.bp +++ b/services/inputflinger/host/Android.bp @@ -30,6 +30,9 @@ cc_library_shared { "libutils", "libhardware", ], + static_libs: [ + "libarect", + ], cflags: [ "-Wall", @@ -54,8 +57,9 @@ cc_binary { shared_libs: [ "libbinder", "libinputflingerhost", - "libutils", + "libutils" + ], + static_libs: [ + "libarect", ], - - init_rc: ["inputflinger.rc"], } diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp index bd11d5620b..2f046c3527 100644 --- a/services/inputflinger/host/InputDriver.cpp +++ b/services/inputflinger/host/InputDriver.cpp @@ -217,18 +217,18 @@ input_property_map_t* InputDriver::inputGetDevicePropertyMap(input_device_identi 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 39e69e5efc..82ff089e89 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -39,6 +39,9 @@ public: InputFlinger() ANDROID_API; virtual status_t dump(int fd, const Vector<String16>& args); + void setInputWindows(const Vector<InputWindowInfo>&) {} + void registerInputChannel(const sp<InputChannel>&) {} + void unregisterInputChannel(const sp<InputChannel>&) {} private: virtual ~InputFlinger(); diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/include/EventHub.h index 66bc29456b..ae70169a09 100644 --- a/services/inputflinger/EventHub.h +++ b/services/inputflinger/include/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,32 +31,19 @@ #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 { @@ -207,7 +196,7 @@ public: // 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. @@ -303,7 +292,7 @@ public: 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; @@ -344,9 +333,11 @@ private: 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,7 +348,7 @@ private: uint8_t ffBitmask[(FF_MAX + 1) / 8]; uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8]; - String8 configurationFile; + std::string configurationFile; PropertyMap* configuration; VirtualKeyMap* virtualKeyMap; KeyMap keyMap; @@ -370,10 +361,8 @@ private: 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,7 +374,7 @@ private: 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; @@ -393,11 +382,13 @@ private: }; 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,16 +397,20 @@ private: 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); status_t unregisterDeviceFromEpollLocked(Device* device); 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; + Device* getDeviceByFdLocked(int fd) const; bool hasKeycodeLocked(Device* device, int keycode) const; @@ -450,6 +445,14 @@ private: 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,16 +460,15 @@ private: 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; + int mInputWd; + int mVideoWd; // Epoll FD list size hint. static const int EPOLL_SIZE_HINT = 8; diff --git a/services/inputflinger/InputListener.h b/services/inputflinger/include/InputListener.h index 77afb344c2..2442cc052b 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,12 @@ class InputListenerInterface; /* Superclass of all input event argument objects */ struct NotifyArgs { + uint32_t sequenceNum; + + inline NotifyArgs() : sequenceNum(0) { } + + inline explicit NotifyArgs(uint32_t sequenceNum) : sequenceNum(sequenceNum) { } + virtual ~NotifyArgs() { } virtual void notify(const sp<InputListenerInterface>& listener) const = 0; @@ -40,7 +49,7 @@ struct NotifyConfigurationChangedArgs : public NotifyArgs { inline NotifyConfigurationChangedArgs() { } - explicit NotifyConfigurationChangedArgs(nsecs_t eventTime); + NotifyConfigurationChangedArgs(uint32_t sequenceNum, nsecs_t eventTime); NotifyConfigurationChangedArgs(const NotifyConfigurationChangedArgs& other); @@ -55,6 +64,7 @@ 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 +75,9 @@ struct NotifyKeyArgs : public NotifyArgs { 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); NotifyKeyArgs(const NotifyKeyArgs& other); @@ -82,6 +92,7 @@ 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; @@ -89,7 +100,6 @@ struct NotifyMotionArgs : public NotifyArgs { int32_t metaState; int32_t buttonState; 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,15 +113,18 @@ struct NotifyMotionArgs : public NotifyArgs { 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 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); @@ -130,7 +143,7 @@ struct NotifySwitchArgs : public NotifyArgs { 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); @@ -149,7 +162,7 @@ struct NotifyDeviceResetArgs : public NotifyArgs { inline NotifyDeviceResetArgs() { } - NotifyDeviceResetArgs(nsecs_t eventTime, int32_t deviceId); + NotifyDeviceResetArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); NotifyDeviceResetArgs(const NotifyDeviceResetArgs& other); diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h new file mode 100644 index 0000000000..fe1c50bc75 --- /dev/null +++ b/services/inputflinger/include/InputReaderBase.h @@ -0,0 +1,349 @@ +/* + * 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(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; +}; + +/* 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) { } + + 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 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/inputflinger/include/InputReaderFactory.h b/services/inputflinger/include/InputReaderFactory.h new file mode 100644 index 0000000000..9db6233d28 --- /dev/null +++ b/services/inputflinger/include/InputReaderFactory.h @@ -0,0 +1,29 @@ +/* + * 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 <utils/StrongPointer.h> + +namespace android { + +class InputReaderInterface; +class InputReaderPolicyInterface; +class InputListenerInterface; + +sp<InputReaderInterface> createInputReader( + const sp<InputReaderPolicyInterface>& policy, + const sp<InputListenerInterface>& listener); + +} // namespace android diff --git a/services/inputflinger/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index e94dd94868..e94dd94868 100644 --- a/services/inputflinger/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h diff --git a/services/inputflinger/include/TouchVideoDevice.h b/services/inputflinger/include/TouchVideoDevice.h new file mode 100644 index 0000000000..7ff265330f --- /dev/null +++ b/services/inputflinger/include/TouchVideoDevice.h @@ -0,0 +1,123 @@ +/* + * 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 width of the heatmap frame + */ + uint32_t getWidth() const { return mWidth; } + /** + * Get the height of the heatmap frame + */ + uint32_t getHeight() const { return mHeight; } + /** + * 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. + */ + 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 mWidth; + uint32_t mHeight; + + 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 width, uint32_t height, + 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/tests/Android.bp b/services/inputflinger/tests/Android.bp index afaf139d37..5b275fb9f6 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -9,10 +9,12 @@ cc_test { cflags: [ "-Wall", "-Werror", + "-Wextra", "-Wno-unused-parameter", ], shared_libs: [ "libbase", + "libbinder", "libcutils", "liblog", "libutils", @@ -21,6 +23,8 @@ cc_test { "libui", "libinput", "libinputflinger", + "libinputreader", + "libinputflinger_base", "libinputservice", ], } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index aa6df244e2..26f01b7eb5 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 nsecs_t ARBITRARY_TIME = 1234; 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; @@ -53,12 +55,15 @@ private: } 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>&) { } virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) { @@ -75,12 +80,12 @@ private: virtual void interceptMotionBeforeQueueing(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; } @@ -103,13 +108,20 @@ class InputDispatcherTest : public testing::Test { 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 +132,20 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { 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."; } @@ -149,108 +161,559 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { } // Rejects undefined motion actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, /*action*/ -1, 0, 0, 0, AMETA_NONE, 0, 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, AINPUT_SOURCE_TOUCHSCREEN, 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); 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, AINPUT_SOURCE_TOUCHSCREEN, 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); 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, AINPUT_SOURCE_TOUCHSCREEN, 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); 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, AINPUT_SOURCE_TOUCHSCREEN, 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); 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, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 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, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 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, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 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, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 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, + event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, 0, AMETA_NONE, 0, 0, 0, 0, 0, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( - &event, DISPLAY_ID, + &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with duplicate pointer ids."; } +// --- InputDispatcherTest SetInputWindowTest --- +static const int32_t INJECT_EVENT_TIMEOUT = 500; +static const int32_t DISPATCHING_TIMEOUT = 100; + +class FakeApplicationHandle : public InputApplicationHandle { +public: + FakeApplicationHandle() {} + virtual ~FakeApplicationHandle() {} + + virtual bool updateInfo() { + if (!mInfo) { + mInfo = new InputApplicationInfo(); + } + mInfo->dispatchingTimeout = DISPATCHING_TIMEOUT; + return true; + } +}; + +class FakeInputReceiver { +public: + void consumeEvent(int32_t expectedEventType, int32_t expectedDisplayId, + int32_t expectedFlags = 0) { + uint32_t consumeSeq; + InputEvent* event; + status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, + &consumeSeq, &event); + + ASSERT_EQ(OK, status) + << mName.c_str() << ": consumer consume should return OK."; + ASSERT_TRUE(event != nullptr) + << mName.c_str() << ": consumer should have returned non-NULL event."; + ASSERT_EQ(expectedEventType, event->getType()) + << mName.c_str() << ": event type should match."; + + ASSERT_EQ(expectedDisplayId, event->getDisplayId()) + << mName.c_str() << ": event displayId should be the same as expected."; + + int32_t flags; + switch (expectedEventType) { + case AINPUT_EVENT_TYPE_KEY: { + KeyEvent* typedEvent = static_cast<KeyEvent*>(event); + flags = typedEvent->getFlags(); + break; + } + case AINPUT_EVENT_TYPE_MOTION: { + MotionEvent* typedEvent = static_cast<MotionEvent*>(event); + flags = typedEvent->getFlags(); + break; + } + default: { + FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType; + } + } + ASSERT_EQ(expectedFlags, flags) + << mName.c_str() << ": event flags should be the same as expected."; + + status = mConsumer->sendFinishedSignal(consumeSeq, handled()); + ASSERT_EQ(OK, status) + << mName.c_str() << ": consumer sendFinishedSignal should return OK."; + } + + void assertNoEvents() { + uint32_t consumeSeq; + InputEvent* event; + status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, + &consumeSeq, &event); + ASSERT_NE(OK, status) + << mName.c_str() + << ": should not have received any events, so consume(..) should not return OK."; + } + +protected: + explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, + const std::string name, int32_t displayId) : + mDispatcher(dispatcher), mName(name), mDisplayId(displayId) { + InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel); + mConsumer = new InputConsumer(mClientChannel); + } + + virtual ~FakeInputReceiver() { + } + + // return true if the event has been handled. + virtual bool handled() { + return false; + } + + sp<InputDispatcher> mDispatcher; + sp<InputChannel> mServerChannel, mClientChannel; + sp<IBinder> mToken; + InputConsumer *mConsumer; + PreallocatedInputEventFactory mEventFactory; + + std::string mName; + int32_t mDisplayId; +}; + +class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver { +public: + static const int32_t WIDTH = 600; + static const int32_t HEIGHT = 800; + + FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) : + FakeInputReceiver(dispatcher, name, displayId), + mFocused(false) { + mServerChannel->setToken(new BBinder()); + mDispatcher->registerInputChannel(mServerChannel, displayId); + + inputApplicationHandle->updateInfo(); + mInfo.applicationInfo = *inputApplicationHandle->getInfo(); + } + + virtual bool updateInfo() { + mInfo.token = mServerChannel->getToken(); + mInfo.name = mName; + mInfo.layoutParamsFlags = 0; + mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + mInfo.frameLeft = 0; + mInfo.frameTop = 0; + mInfo.frameRight = WIDTH; + mInfo.frameBottom = HEIGHT; + mInfo.globalScaleFactor = 1.0; + mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT)); + mInfo.visible = true; + mInfo.canReceiveKeys = true; + mInfo.hasFocus = mFocused; + mInfo.hasWallpaper = false; + mInfo.paused = false; + mInfo.layer = 0; + mInfo.ownerPid = INJECTOR_PID; + mInfo.ownerUid = INJECTOR_UID; + mInfo.inputFeatures = 0; + mInfo.displayId = mDisplayId; + + return true; + } + + void setFocus() { + mFocused = true; + } + + void 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: + virtual bool handled() { + return true; + } + + bool mFocused; +}; + +static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { + KeyEvent event; + nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + + // Define a valid key down event. + event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, displayId, + AKEY_EVENT_ACTION_DOWN, /* flags */ 0, + AKEYCODE_A, KEY_A, AMETA_NONE, /* repeatCount */ 0, currentTime, currentTime); + + // Inject event until dispatch out. + return dispatcher->injectInputEvent( + &event, + INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); +} + +static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source, + int32_t displayId) { + MotionEvent event; + PointerProperties pointerProperties[1]; + PointerCoords pointerCoords[1]; + + pointerProperties[0].clear(); + pointerProperties[0].id = 0; + pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[0].clear(); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200); + + nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + // Define a valid motion down event. + event.initialize(DEVICE_ID, source, displayId, + AMOTION_EVENT_ACTION_DOWN, /* actionButton */0, /* flags */ 0, /* edgeFlags */ 0, + AMETA_NONE, /* buttonState */ 0, /* 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); +} + +TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", + ADISPLAY_ID_DEFAULT); + + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.add(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); + + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.add(windowTop); + inputWindowHandles.add(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 focus application. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + // Expect one focus window exist in display. + windowSecond->setFocus(); + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.add(windowTop); + inputWindowHandles.add(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_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); + + windowTop->setFocus(); + + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.add(windowTop); + inputWindowHandles.add(windowSecond); + + mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + + // Release channel for window is no longer valid. + windowTop->releaseChannel(); + + // Test inject a motion down, should timeout because of no target channel. + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT"; + + // Top window is invalid, so it should not receive any input event. + windowTop->assertNoEvents(); + windowSecond->assertNoEvents(); +} + +/* 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); + Vector<sp<InputWindowHandle>> inputWindowHandles; + inputWindowHandles.push(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. + Vector<sp<InputWindowHandle>> inputWindowHandles_Second; + inputWindowHandles_Second.push(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. + Vector<sp<InputWindowHandle>> noWindows; + mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID); + + // Expect old focus should receive a cancel event. + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE, + AKEY_EVENT_FLAG_CANCELED); + + // Test inject a key down, should timeout because of no target window. + ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT"; + windowInPrimary->assertNoEvents(); + windowInSecondary->assertNoEvents(); +} + +class FakeMonitorReceiver : public FakeInputReceiver, public RefBase { +public: + FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name, + int32_t displayId) : FakeInputReceiver(dispatcher, name, displayId) { + mDispatcher->registerInputChannel(mServerChannel, displayId); + } +}; + +// Test per-display input monitors for motion event. +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { + sp<FakeMonitorReceiver> monitorInPrimary = + new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + sp<FakeMonitorReceiver> monitorInSecondary = + new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); + + // Test touch down on primary display. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + monitorInPrimary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowInSecondary->assertNoEvents(); + monitorInSecondary->assertNoEvents(); + + // Test touch down on second display. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->assertNoEvents(); + monitorInPrimary->assertNoEvents(); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID); + monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, SECOND_DISPLAY_ID); + + // Test inject a non-pointer motion event. + // If specific a display, it will dispatch to the focused window of particular display, + // or it will dispatch to the focused window of focused display. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, + AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE)) + << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->assertNoEvents(); + monitorInPrimary->assertNoEvents(); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE); + monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_NONE); +} + +// Test per-display input monitors for key event. +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) { + //Input monitor per display. + sp<FakeMonitorReceiver> monitorInPrimary = + new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + sp<FakeMonitorReceiver> monitorInSecondary = + new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); + + // Test inject a key down. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + windowInPrimary->assertNoEvents(); + monitorInPrimary->assertNoEvents(); + windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); + monitorInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); +} + } // namespace android diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 22f15a05fe..b5d209092a 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -28,12 +28,14 @@ static const nsecs_t ARBITRARY_TIME = 1234; // Arbitrary display properties. static const int32_t DISPLAY_ID = 0; +static const int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1; static const int32_t DISPLAY_WIDTH = 480; static const int32_t DISPLAY_HEIGHT = 800; static const int32_t VIRTUAL_DISPLAY_ID = 1; static const int32_t VIRTUAL_DISPLAY_WIDTH = 400; static const int32_t VIRTUAL_DISPLAY_HEIGHT = 500; -static 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; @@ -132,6 +134,7 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface { InputReaderConfiguration mConfig; KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers; Vector<InputDeviceInfo> mInputDevices; + std::vector<DisplayViewport> mViewports; TouchAffineTransformation transform; protected: @@ -141,23 +144,37 @@ public: 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); + } + + 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 String8& deviceName) { - mConfig.excludedDeviceNames.push(deviceName); + 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) { @@ -188,7 +205,7 @@ public: return mInputDevices; } - TouchAffineTransformation getTouchAffineTransformation(const String8& inputDeviceDescriptor, + TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor, int32_t surfaceRotation) { return transform; } @@ -203,7 +220,8 @@ public: 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 +238,8 @@ private: v.deviceWidth = isRotated ? height : width; v.deviceHeight = isRotated ? width : height; v.uniqueId = uniqueId; + v.physicalPort = physicalPort; + v.type = type; return v; } @@ -236,11 +256,11 @@ private: } 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 ""; } }; @@ -263,7 +283,7 @@ public: } void assertNotifyConfigurationChangedWasCalled( - NotifyConfigurationChangedArgs* outEventArgs = NULL) { + NotifyConfigurationChangedArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty()) << "Expected notifyConfigurationChanged() to have been called."; if (outEventArgs) { @@ -278,7 +298,7 @@ public: } void assertNotifyDeviceResetWasCalled( - NotifyDeviceResetArgs* outEventArgs = NULL) { + NotifyDeviceResetArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty()) << "Expected notifyDeviceReset() to have been called."; if (outEventArgs) { @@ -292,7 +312,7 @@ public: << "Expected notifyDeviceReset() to not have been called."; } - void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = NULL) { + void assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called."; if (outEventArgs) { @@ -306,7 +326,7 @@ public: << "Expected notifyKey() to not have been called."; } - void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = NULL) { + void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called."; if (outEventArgs) { @@ -320,7 +340,7 @@ public: << "Expected notifyMotion() to not have been called."; } - void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = NULL) { + void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr) { ASSERT_FALSE(mNotifySwitchArgsQueue.empty()) << "Expected notifySwitch() to have been called."; if (outEventArgs) { @@ -392,7 +412,7 @@ class FakeEventHub : public EventHubInterface { }; KeyedVector<int32_t, Device*> mDevices; - Vector<String8> mExcludedDevices; + std::vector<std::string> mExcludedDevices; List<RawEvent> mEvents; protected: @@ -405,7 +425,7 @@ protected: 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 +442,7 @@ public: 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 +452,7 @@ public: 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 +466,7 @@ public: 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,7 +554,7 @@ public: return device->leds.valueFor(led); } - Vector<String8>& getExcludedDevices() { + std::vector<std::string>& getExcludedDevices() { return mExcludedDevices; } @@ -566,7 +586,7 @@ public: 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 +671,14 @@ private: 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; } @@ -781,7 +801,7 @@ private: } virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const { - return NULL; + return nullptr; } virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) { @@ -821,13 +841,14 @@ class FakeInputReaderContext : public InputReaderContext { 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() { } @@ -891,6 +912,10 @@ private: virtual void dispatchExternalStylusState(const StylusState&) { } + + virtual uint32_t getNextSequenceNum() { + return mNextSequenceNum++; + } }; @@ -940,7 +965,7 @@ public: mResetWasCalled = false; } - void assertProcessWasCalled(RawEvent* outLastEvent = NULL) { + void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) { ASSERT_TRUE(mProcessWasCalled) << "Expected process() to have been called."; if (outLastEvent) { @@ -1039,7 +1064,7 @@ public: const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) : InputReader(eventHub, policy, listener), - mNextDevice(NULL) { + mNextDevice(nullptr) { } virtual ~InstrumentedInputReader() { @@ -1052,7 +1077,7 @@ public: mNextDevice = device; } - InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const String8& name, + InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name, uint32_t classes) { InputDeviceIdentifier identifier; identifier.name = name; @@ -1066,7 +1091,7 @@ protected: 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,6 +1100,191 @@ protected: 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 --- @@ -1101,7 +1311,7 @@ protected: 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 +1339,7 @@ protected: } 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 +1351,18 @@ protected: }; 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; 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 +1371,7 @@ TEST_F(InputReaderTest, GetInputDevices) { 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 +1380,14 @@ TEST_F(InputReaderTest, GetInputDevices) { 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, "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 +1418,9 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { } 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 +1445,9 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { } 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 +1472,9 @@ TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { } 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 +1499,10 @@ TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { } 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 +1535,7 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { } 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 +1544,9 @@ TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChange } 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 +1561,39 @@ TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { 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, "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; +} + // --- InputDeviceTest --- @@ -1373,7 +1618,7 @@ protected: mFakeListener = new FakeInputListener(); 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 +1644,7 @@ const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD 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 +1672,7 @@ TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { 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 +1742,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe 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 +1794,7 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe 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; @@ -1567,10 +1813,11 @@ protected: 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 +1829,7 @@ protected: } 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 +1843,22 @@ protected: } 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 +1868,7 @@ protected: 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 +1902,7 @@ protected: }; 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 +1937,10 @@ TEST_F(SwitchInputMapperTest, Process) { 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 +1956,33 @@ TEST_F(SwitchInputMapperTest, Process) { 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 +2009,7 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { 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 +2024,7 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { 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 +2038,8 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { 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 +2053,8 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { 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 +2068,8 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { 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 +2083,8 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { 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 +2110,7 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { 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 +2118,19 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { 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 +2147,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateD 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 +2169,7 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { 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 +2179,8 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { 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 +2190,8 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { 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 +2201,8 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { 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 +2215,81 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { // 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 +2346,48 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) 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 +2407,18 @@ protected: 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 +2427,9 @@ void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper, 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 +2463,8 @@ TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeF 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 +2515,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat // 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 +2556,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat 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 +2605,16 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { 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 +2629,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { 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 +2642,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { 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 +2663,10 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { 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 +2680,9 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { 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 +2690,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { 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 +2708,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMot 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 +2725,7 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) 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 +2735,7 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) 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 +2745,7 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) 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 +2755,7 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) 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 +2779,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2795,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2819,9 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2848,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2864,16 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2890,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2910,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2931,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2951,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2972,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +2992,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +3013,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +3033,8 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleAllButtons) { 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 +3065,9 @@ TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerArou 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 +3094,9 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { 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 +3105,8 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { 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 +3119,8 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { 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 +3133,9 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { 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 +3154,9 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { 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); @@ -2917,6 +3200,9 @@ protected: 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 +3217,8 @@ protected: 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 +3271,20 @@ const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = { { 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 +3383,48 @@ void SingleTouchInputMapperTest::prepareAxes(int axes) { } 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 +4013,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) NotifyMotionArgs args; // Rotation 0. + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_0); processDown(mapper, toRawX(50), toRawY(75)); processSync(mapper); @@ -3732,6 +4027,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) 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 +4041,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) 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 +4055,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenOrientationAware_RotatesMotions) 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); @@ -4441,75 +4739,75 @@ void MultiTouchInputMapperTest::prepareAxes(int axes) { 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); } @@ -5944,5 +6242,76 @@ TEST_F(MultiTouchInputMapperTest, WhenMapperIsReset_TimestampIsCleared) { 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); +} } // namespace android diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index a7f3a5235e..33a2747312 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -41,18 +41,21 @@ cc_library_shared { "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 @@ cc_binary { shared_libs: [ "libsensorservice", + "libsensorprivacy", "libbinder", "libutils", ], diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp index d8e5b290d0..14f9a12553 100644 --- a/services/sensorservice/BatteryService.cpp +++ b/services/sensorservice/BatteryService.cpp @@ -94,7 +94,7 @@ void BatteryService::cleanupImpl(uid_t uid) { 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 1025a88eaf..207b09711a 100644 --- a/services/sensorservice/RecentEventLogger.cpp +++ b/services/sensorservice/RecentEventLogger.cpp @@ -26,24 +26,32 @@ namespace SensorServiceUtil { 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 @@ void RecentEventLogger::setFormat(std::string format) { } } -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 @@ bool RecentEventLogger::populateLastEvent(sensors_event_t *event) const { 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 bf1f655704..67378b7dd6 100644 --- a/services/sensorservice/RecentEventLogger.h +++ b/services/sensorservice/RecentEventLogger.h @@ -37,8 +37,13 @@ class RecentEventLogger : public Dumpable { 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 @@ protected: 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 7b00f4d98d..f2ea02eb00 100644 --- a/services/sensorservice/RotationVectorSensor.cpp +++ b/services/sensorservice/RotationVectorSensor.cpp @@ -94,7 +94,7 @@ const char* RotationVectorSensor::getSensorName() const { 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 115a983bc4..9b2cd50187 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,13 @@ #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::hidl_vec; +using android::hardware::Return; using android::SensorDeviceUtils::HidlServiceRegistrationWaiter; namespace android { @@ -51,12 +59,53 @@ static status_t StatusFromResult(Result result) { } } +template<typename EnumType> +constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) { + return static_cast<typename std::underlying_type<EnumType>::type>(value); +} + +// Used internally by the framework to wake the Event FMQ. These values must start after +// the last value of EventQueueFlagBits +enum EventQueueFlagBitsInternal : uint32_t { + INTERNAL_WAKE = 1 << 16, +}; + +void SensorsHalDeathReceivier::serviceDied( + uint64_t /* cookie */, + const wp<::android::hidl::base::V1_0::IBase>& /* service */) { + ALOGW("Sensors HAL died, attempting to reconnect."); + SensorDevice::getInstance().prepareForReconnect(); +} + +struct SensorsCallback : public ISensorsCallback { + using Result = ::android::hardware::sensors::V1_0::Result; + Return<void> onDynamicSensorsConnected( + const hidl_vec<SensorInfo> &dynamicSensorsAdded) override { + return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded); + } + + Return<void> onDynamicSensorsDisconnected( + const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override { + return SensorDevice::getInstance().onDynamicSensorsDisconnected( + dynamicSensorHandlesRemoved); + } +}; + SensorDevice::SensorDevice() - : mHidlTransportErrors(20), mRestartWaiter(new HidlServiceRegistrationWaiter()) { + : mHidlTransportErrors(20), + mRestartWaiter(new HidlServiceRegistrationWaiter()), + mReconnecting(false) { if (!connectHidlService()) { return; } + initializeSensorList(); + + mIsDirectReportSupported = + (checkReturn(mSensors->unregisterDirectChannel(-1)) != Result::INVALID_OPERATION); +} + +void SensorDevice::initializeSensorList() { float minPowerMa = 0.001; // 1 microAmp checkReturn(mSensors->getSensorsList( @@ -81,37 +130,197 @@ SensorDevice::SensorDevice() 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; + } } 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::createEventFlag(mEventQueue->getEventFlagWord(), &mEventQueueFlag); + + CHECK(mSensors != nullptr && mEventQueue != nullptr && + mWakeLockQueue != nullptr && mEventQueueFlag != nullptr); + + status_t status = StatusFromResult(checkReturn(mSensors->initialize( + *mEventQueue->getDesc(), + *mWakeLockQueue->getDesc(), + new SensorsCallback()))); + + if (status != NO_ERROR) { + connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT; + ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status)); + } else { + connectionStatus = HalConnectionStatus::CONNECTED; + mSensorsHalDeathReceiver = new SensorsHalDeathReceivier(); + sensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */); + } + } + + return connectionStatus; +} + +void SensorDevice::prepareForReconnect() { + mReconnecting = true; + + // Wake up the polling thread so it returns and allows the SensorService to initiate + // a reconnect. + mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE)); +} + +void SensorDevice::reconnect() { + Mutex::Autolock _l(mLock); + mSensors = nullptr; + + auto previousActivations = mActivationCount; + auto previousSensorList = mSensorList; + + mActivationCount.clear(); + mSensorList.clear(); + + if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) { + initializeSensorList(); + + if (sensorHandlesChanged(previousSensorList, mSensorList)) { + LOG_ALWAYS_FATAL("Sensor handles changed, cannot re-enable sensors."); + } else { + reactivateSensors(previousActivations); + } + } + mReconnecting = false; +} + +bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList, + const Vector<sensor_t>& newSensorList) { + bool didChange = false; + + if (oldSensorList.size() != newSensorList.size()) { + didChange = true; + } + + for (size_t i = 0; i < newSensorList.size() && !didChange; i++) { + bool found = false; + const sensor_t& newSensor = newSensorList[i]; + for (size_t j = 0; j < oldSensorList.size() && !found; j++) { + const sensor_t& prevSensor = oldSensorList[j]; + if (prevSensor.handle == newSensor.handle) { + found = true; + if (!sensorIsEquivalent(prevSensor, newSensor)) { + didChange = true; + } + } + } + + if (!found) { + // Could not find the new sensor in the old list of sensors, the lists must + // have changed. + didChange = true; + } + } + return didChange; +} + +bool SensorDevice::sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor) { + bool equivalent = true; + if (prevSensor.handle != newSensor.handle || + (strcmp(prevSensor.vendor, newSensor.vendor) != 0) || + (strcmp(prevSensor.stringType, newSensor.stringType) != 0) || + (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) || + (prevSensor.version != newSensor.version) || + (prevSensor.type != newSensor.type) || + (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) || + (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) || + (std::abs(prevSensor.power - newSensor.power) > 0.001f) || + (prevSensor.minDelay != newSensor.minDelay) || + (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) || + (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) || + (prevSensor.maxDelay != newSensor.maxDelay) || + (prevSensor.flags != newSensor.flags)) { + equivalent = false; + } + return equivalent; +} + +void SensorDevice::reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations) { + for (size_t i = 0; i < mSensorList.size(); i++) { + int handle = mSensorList[i].handle; + ssize_t activationIndex = previousActivations.indexOfKey(handle); + if (activationIndex < 0 || previousActivations[activationIndex].numActiveClients() <= 0) { + continue; + } + + const Info& info = previousActivations[activationIndex]; + for (size_t j = 0; j < info.batchParams.size(); j++) { + const BatchParams& batchParams = info.batchParams[j]; + status_t res = batchLocked(info.batchParams.keyAt(j), handle, 0 /* flags */, + batchParams.mTSample, batchParams.mTBatch); + + if (res == NO_ERROR) { + activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */); + } + } + } } void SensorDevice::handleDynamicSensorConnection(int handle, bool connected) { @@ -173,6 +382,19 @@ status_t SensorDevice::initCheck() const { ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { if (mSensors == nullptr) return NO_INIT; + ssize_t eventsRead = 0; + if (mSensors->supportsMessageQueues()) { + eventsRead = pollFmq(buffer, count); + } else if (mSensors->supportsPolling()) { + eventsRead = pollHal(buffer, count); + } else { + ALOGE("Must support polling or FMQ"); + eventsRead = -1; + } + return eventsRead; +} + +ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) { ssize_t err; int numHidlTransportErrors = 0; bool hidlTransportError = false; @@ -208,7 +430,7 @@ ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { 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 +438,86 @@ ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { return err; } +ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) { + ssize_t eventsRead = 0; + size_t availableEvents = mEventQueue->availableToRead(); + + if (availableEvents == 0) { + uint32_t eventFlagState = 0; + + // Wait for events to become available. This is necessary so that the Event FMQ's read() is + // able to be called with the correct number of events to read. If the specified number of + // events is not available, then read() would return no events, possibly introducing + // additional latency in delivering events to applications. + mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) | + asBaseType(INTERNAL_WAKE), &eventFlagState); + availableEvents = mEventQueue->availableToRead(); + + if ((eventFlagState & asBaseType(EventQueueFlagBits::READ_AND_PROCESS)) && + availableEvents == 0) { + ALOGW("Event FMQ wake without any events"); + } + + if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) { + ALOGD("Event FMQ internal wake, returning from poll with no events"); + return DEAD_OBJECT; + } + } + + size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()}); + if (eventsToRead > 0) { + if (mEventQueue->read(mEventBuffer.data(), eventsToRead)) { + // Notify the Sensors HAL that sensor events have been read. This is required to support + // the use of writeBlocking by the Sensors HAL. + mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ)); + + for (size_t i = 0; i < eventsToRead; i++) { + convertToSensorEvent(mEventBuffer[i], &buffer[i]); + } + eventsRead = eventsToRead; + } else { + ALOGW("Failed to read %zu events, currently %zu events available", + eventsToRead, availableEvents); + } + } + + return eventsRead; +} + +Return<void> SensorDevice::onDynamicSensorsConnected( + const hidl_vec<SensorInfo> &dynamicSensorsAdded) { + // Allocate a sensor_t structure for each dynamic sensor added and insert + // it into the dictionary of connected dynamic sensors keyed by handle. + for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) { + const SensorInfo &info = dynamicSensorsAdded[i]; + + auto it = mConnectedDynamicSensors.find(info.sensorHandle); + CHECK(it == mConnectedDynamicSensors.end()); + + sensor_t *sensor = new sensor_t(); + convertToSensor(info, sensor); + + mConnectedDynamicSensors.insert( + std::make_pair(sensor->handle, sensor)); + } + + return Return<void>(); +} + +Return<void> SensorDevice::onDynamicSensorsDisconnected( + const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) { + (void) dynamicSensorHandlesRemoved; + // TODO: Currently dynamic sensors do not seem to be removed + return Return<void>(); +} + +void SensorDevice::writeWakeLockHandled(uint32_t count) { + if (mSensors != nullptr && mSensors->supportsMessageQueues() && + !mWakeLockQueue->write(&count)) { + ALOGW("Failed to write wake lock handled"); + } +} + void SensorDevice::autoDisable(void *ident, int handle) { Mutex::Autolock _l(mLock); ssize_t activationIndex = mActivationCount.indexOfKey(handle); @@ -230,10 +532,15 @@ void SensorDevice::autoDisable(void *ident, int handle) { status_t SensorDevice::activate(void* ident, int handle, int enabled) { if (mSensors == nullptr) return NO_INIT; - status_t err(NO_ERROR); + Mutex::Autolock _l(mLock); + return activateLocked(ident, handle, enabled); +} + +status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { bool actuateHardware = false; - Mutex::Autolock _l(mLock); + status_t err(NO_ERROR); + ssize_t activationIndex = mActivationCount.indexOfKey(handle); if (activationIndex < 0) { ALOGW("Handle %d cannot be found in activation record", handle); @@ -333,6 +640,11 @@ status_t SensorDevice::batch( ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); Mutex::Autolock _l(mLock); + return batchLocked(ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); +} + +status_t SensorDevice::batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs) { ssize_t activationIndex = mActivationCount.indexOfKey(handle); if (activationIndex < 0) { ALOGW("Handle %d cannot be found in activation record", handle); @@ -563,7 +875,7 @@ int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, // --------------------------------------------------------------------------- -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 +963,9 @@ void SensorDevice::convertToSensorEvents( 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 +974,12 @@ void SensorDevice::convertToSensorEvents( } void SensorDevice::handleHidlDeath(const std::string & detail) { - // restart is the only option at present. - LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str()); + if (!SensorDevice::getInstance().mSensors->supportsMessageQueues()) { + // restart is the only option at present. + LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str()); + } else { + ALOGD("ISensors HAL died, death recipient will attempt reconnect"); + } } // --------------------------------------------------------------------------- diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index 6d7505158d..e1024ac1f7 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 @@ public: 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 @@ public: int getHalDeviceVersion() const; ssize_t poll(sensors_event_t* buffer, size_t count); + void writeWakeLockHandled(uint32_t count); status_t activate(void* ident, int handle, int enabled); status_t batch(void* ident, int handle, int flags, int64_t samplingPeriodNs, @@ -98,12 +110,22 @@ public: status_t injectSensorData(const sensors_event_t *event); void notifyConnectionDestroyed(void *ident); + using Result = ::android::hardware::sensors::V1_0::Result; + hardware::Return<void> onDynamicSensorsConnected( + const hardware::hidl_vec<hardware::sensors::V1_0::SensorInfo> &dynamicSensorsAdded); + hardware::Return<void> onDynamicSensorsDisconnected( + const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved); + + bool isReconnecting() const { + return mReconnecting; + } + // Dumpable virtual std::string dump() const; private: friend class Singleton<SensorDevice>; - sp<hardware::sensors::V1_0::ISensors> mSensors; + sp<SensorServiceUtil::ISensorsWrapper> mSensors; Vector<sensor_t> mSensorList; std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors; @@ -151,7 +173,7 @@ private: // the removed ident. If index >=0, ident is present and successfully removed. ssize_t removeBatchParamsForIdent(void* ident); - int numActiveClients(); + int numActiveClients() const; }; DefaultKeyedVector<int, Info> mActivationCount; @@ -163,6 +185,26 @@ private: SortedVector<void *> mDisabledClients; SensorDevice(); bool connectHidlService(); + void initializeSensorList(); + void reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations); + static bool sensorHandlesChanged(const Vector<sensor_t>& oldSensorList, + const Vector<sensor_t>& newSensorList); + static bool sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor); + + enum HalConnectionStatus { + CONNECTED, // Successfully connected to the HAL + DOES_NOT_EXIST, // Could not find the HAL + FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize + UNKNOWN, + }; + HalConnectionStatus connectHidlServiceV1_0(); + HalConnectionStatus connectHidlServiceV2_0(); + + ssize_t pollHal(sensors_event_t* buffer, size_t count); + ssize_t pollFmq(sensors_event_t* buffer, size_t count); + status_t activateLocked(void* ident, int handle, int enabled); + status_t batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs, + int64_t maxBatchReportLatencyNs); static void handleHidlDeath(const std::string &detail); template<typename T> @@ -189,6 +231,18 @@ private: 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; + + std::array<Event, SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer; + + sp<SensorsHalDeathReceivier> mSensorsHalDeathReceiver; + std::atomic_bool mReconnecting; }; // --------------------------------------------------------------------------- diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 956844f426..b66cbcfbe5 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -31,9 +31,10 @@ SensorService::SensorEventConnection::SensorEventConnection( 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 @@ void SensorService::SensorEventConnection::destroy() { } mService->cleanupConnection(this); - if (mEventCache != NULL) { - delete mEventCache; + if (mEventCache != nullptr) { + delete[] mEventCache; } mDestroyed = true; } @@ -200,7 +201,7 @@ void SensorService::SensorEventConnection::updateLooperRegistrationLocked( // Add the file descriptor to the Looper for receiving acknowledegments if the app has // registered for wake-up sensors OR for sending events in the cache. - int ret = looper->addFd(mChannel->getSendFd(), 0, looper_flags, this, NULL); + int ret = looper->addFd(mChannel->getSendFd(), 0, looper_flags, this, nullptr); if (ret == 1) { ALOGD_IF(DEBUG_CONNECTIONS, "%p addFd fd=%d", this, mChannel->getSendFd()); mHasLooperCallbacks = true; @@ -224,7 +225,7 @@ status_t SensorService::SensorEventConnection::sendEvents( wp<const SensorEventConnection> const * mapFlushEventsToConnections) { // filter out events not for this connection - sensors_event_t* sanitizedBuffer = nullptr; + std::unique_ptr<sensors_event_t[]> sanitizedBuffer; int count = 0; Mutex::Autolock _l(mConnectionLock); @@ -278,7 +279,7 @@ status_t SensorService::SensorEventConnection::sendEvents( } } else { // Regular sensor event, just copy it to the scratch buffer. - if (mHasSensorAccess) { + if (hasSensorAccess()) { scratch[count++] = buffer[i]; } } @@ -289,11 +290,12 @@ status_t SensorService::SensorEventConnection::sendEvents( buffer[i].meta_data.sensor == sensor_handle))); } } else { - if (mHasSensorAccess) { + if (hasSensorAccess()) { scratch = const_cast<sensors_event_t *>(buffer); count = numEvents; } else { - scratch = sanitizedBuffer = new sensors_event_t[numEvents]; + sanitizedBuffer.reset(new sensors_event_t[numEvents]); + scratch = sanitizedBuffer.get(); for (size_t i = 0; i < numEvents; i++) { if (buffer[i].type == SENSOR_TYPE_META_DATA) { scratch[count++] = buffer[i++]; @@ -305,7 +307,6 @@ status_t SensorService::SensorEventConnection::sendEvents( sendPendingFlushEventsLocked(); // Early return if there are no events for this connection. if (count == 0) { - delete sanitizedBuffer; return status_t(NO_ERROR); } @@ -315,39 +316,12 @@ status_t SensorService::SensorEventConnection::sendEvents( if (mCacheSize != 0) { // There are some events in the cache which need to be sent first. Copy this buffer to // the end of cache. - if (mCacheSize + count <= mMaxCacheSize) { - memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t)); - mCacheSize += count; - } else { - // Check if any new sensors have registered on this connection which may have increased - // the max cache size that is desired. - if (mCacheSize + count < computeMaxCacheSizeLocked()) { - reAllocateCacheLocked(scratch, count); - delete sanitizedBuffer; - return status_t(NO_ERROR); - } - // Some events need to be dropped. - int remaningCacheSize = mMaxCacheSize - mCacheSize; - if (remaningCacheSize != 0) { - memcpy(&mEventCache[mCacheSize], scratch, - remaningCacheSize * sizeof(sensors_event_t)); - } - int numEventsDropped = count - remaningCacheSize; - countFlushCompleteEventsLocked(mEventCache, numEventsDropped); - // Drop the first "numEventsDropped" in the cache. - memmove(mEventCache, &mEventCache[numEventsDropped], - (mCacheSize - numEventsDropped) * sizeof(sensors_event_t)); - - // Copy the remainingEvents in scratch buffer to the end of cache. - memcpy(&mEventCache[mCacheSize - numEventsDropped], scratch + remaningCacheSize, - numEventsDropped * sizeof(sensors_event_t)); - } - delete sanitizedBuffer; + appendEventsToCacheLocked(scratch, count); return status_t(NO_ERROR); } int index_wake_up_event = -1; - if (mHasSensorAccess) { + if (hasSensorAccess()) { index_wake_up_event = findWakeUpSensorEventLocked(scratch, count); if (index_wake_up_event >= 0) { scratch[index_wake_up_event].flags |= WAKE_UP_SENSOR_EVENT_NEEDS_ACK; @@ -373,18 +347,17 @@ status_t SensorService::SensorEventConnection::sendEvents( --mTotalAcksNeeded; #endif } - if (mEventCache == NULL) { + if (mEventCache == nullptr) { mMaxCacheSize = computeMaxCacheSizeLocked(); mEventCache = new sensors_event_t[mMaxCacheSize]; mCacheSize = 0; } - memcpy(&mEventCache[mCacheSize], scratch, count * sizeof(sensors_event_t)); - mCacheSize += count; + // Save the events so that they can be written later + appendEventsToCacheLocked(scratch, count); // Add this file descriptor to the looper to get a callback when this fd is available for // writing. updateLooperRegistrationLocked(mService->getLooper()); - delete sanitizedBuffer; return size; } @@ -394,7 +367,6 @@ status_t SensorService::SensorEventConnection::sendEvents( } #endif - delete sanitizedBuffer; return size < 0 ? status_t(size) : status_t(NO_ERROR); } @@ -403,6 +375,10 @@ void SensorService::SensorEventConnection::setSensorAccess(const bool hasAccess) mHasSensorAccess = hasAccess; } +bool SensorService::SensorEventConnection::hasSensorAccess() { + return mHasSensorAccess && !mService->mSensorPrivacyPolicy->isSensorPrivacyEnabled(); +} + void SensorService::SensorEventConnection::reAllocateCacheLocked(sensors_event_t const* scratch, int count) { sensors_event_t *eventCache_new; @@ -415,12 +391,66 @@ void SensorService::SensorEventConnection::reAllocateCacheLocked(sensors_event_t ALOGD_IF(DEBUG_CONNECTIONS, "reAllocateCacheLocked maxCacheSize=%d %d", mMaxCacheSize, new_cache_size); - delete mEventCache; + delete[] mEventCache; mEventCache = eventCache_new; mCacheSize += count; mMaxCacheSize = new_cache_size; } +void SensorService::SensorEventConnection::appendEventsToCacheLocked(sensors_event_t const* events, + int count) { + if (count <= 0) { + return; + } else if (mCacheSize + count <= mMaxCacheSize) { + // The events fit within the current cache: add them + memcpy(&mEventCache[mCacheSize], events, count * sizeof(sensors_event_t)); + mCacheSize += count; + } else if (mCacheSize + count <= computeMaxCacheSizeLocked()) { + // The events fit within a resized cache: resize the cache and add the events + reAllocateCacheLocked(events, count); + } else { + // The events do not fit within the cache: drop the oldest events. + int freeSpace = mMaxCacheSize - mCacheSize; + + // Drop up to the currently cached number of events to make room for new events + int cachedEventsToDrop = std::min(mCacheSize, count - freeSpace); + + // New events need to be dropped if there are more new events than the size of the cache + int newEventsToDrop = std::max(0, count - mMaxCacheSize); + + // Determine the number of new events to copy into the cache + int eventsToCopy = std::min(mMaxCacheSize, count); + + constexpr nsecs_t kMinimumTimeBetweenDropLogNs = 2 * 1000 * 1000 * 1000; // 2 sec + if (events[0].timestamp - mTimeOfLastEventDrop > kMinimumTimeBetweenDropLogNs) { + ALOGW("Dropping %d cached events (%d/%d) to save %d/%d new events. %d events previously" + " dropped", cachedEventsToDrop, mCacheSize, mMaxCacheSize, eventsToCopy, + count, mEventsDropped); + mEventsDropped = 0; + mTimeOfLastEventDrop = events[0].timestamp; + } else { + // Record the number dropped + mEventsDropped += cachedEventsToDrop + newEventsToDrop; + } + + // Check for any flush complete events in the events that will be dropped + countFlushCompleteEventsLocked(mEventCache, cachedEventsToDrop); + countFlushCompleteEventsLocked(events, newEventsToDrop); + + // Only shift the events if they will not all be overwritten + if (eventsToCopy != mMaxCacheSize) { + memmove(mEventCache, &mEventCache[cachedEventsToDrop], + (mCacheSize - cachedEventsToDrop) * sizeof(sensors_event_t)); + } + mCacheSize -= cachedEventsToDrop; + + // Copy the events into the cache + memcpy(&mEventCache[mCacheSize], &events[newEventsToDrop], + eventsToCopy * sizeof(sensors_event_t)); + mCacheSize += eventsToCopy; + } +} + void SensorService::SensorEventConnection::sendPendingFlushEventsLocked() { ASensorEvent flushCompleteEvent; memset(&flushCompleteEvent, 0, sizeof(flushCompleteEvent)); @@ -465,7 +495,7 @@ void SensorService::SensorEventConnection::writeToSocketFromCache() { 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 032721ea39..70778806ce 100644 --- a/services/sensorservice/SensorEventConnection.h +++ b/services/sensorservice/SensorEventConnection.h @@ -53,7 +53,7 @@ public: bool hasSensorAccess); status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch, - wp<const SensorEventConnection> const * mapFlushEventsToConnections = NULL); + wp<const SensorEventConnection> const * mapFlushEventsToConnections = nullptr); bool hasSensor(int32_t handle) const; bool hasAnySensor() const; bool hasOneShotSensors() const; @@ -108,6 +108,10 @@ private: // size, reallocate memory and copy over events from the older cache. void reAllocateCacheLocked(sensors_event_t const* scratch, int count); + // Add the events to the cache. If the cache would be exceeded, drop events at the beginning of + // the cache. + void appendEventsToCacheLocked(sensors_event_t const* events, int count); + // LooperCallback method. If there is data to read on this fd, it is an ack from the app that it // has read events from a wake up sensor, decrement mWakeLockRefCount. If this fd is available // for writing send the data from the cache. @@ -126,6 +130,10 @@ private: void updateLooperRegistration(const sp<Looper>& looper); void updateLooperRegistrationLocked(const sp<Looper>& looper); + // Returns whether sensor access is available based on both the uid being active and sensor + // privacy not being enabled. + bool hasSensorAccess(); + sp<SensorService> const mService; sp<BitTube> mChannel; uid_t mUid; @@ -161,6 +169,8 @@ private: sensors_event_t *mEventCache; int mCacheSize, mMaxCacheSize; + int64_t mTimeOfLastEventDrop; + int mEventsDropped; String8 mPackageName; const String16 mOpPackageName; #if DEBUG_CONNECTIONS diff --git a/services/sensorservice/SensorRecord.cpp b/services/sensorservice/SensorRecord.cpp index 53fb9de230..7c4c6a2046 100644 --- a/services/sensorservice/SensorRecord.cpp +++ b/services/sensorservice/SensorRecord.cpp @@ -71,7 +71,7 @@ wp<const SensorService::SensorEventConnection> 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 bba83727d9..5411515d49 100644 --- a/services/sensorservice/SensorRegistrationInfo.h +++ b/services/sensorservice/SensorRegistrationInfo.h @@ -47,7 +47,7 @@ public: 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 8e9e7fdf7c..9a37ff1902 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -29,6 +29,7 @@ #include <openssl/hmac.h> #include <openssl/rand.h> #include <sensor/SensorEventQueue.h> +#include <sensorprivacy/SensorPrivacyManager.h> #include <utils/SystemClock.h> #include "BatteryService.h" @@ -47,6 +48,7 @@ #include "SensorRecord.h" #include "SensorRegistrationInfo.h" +#include <ctime> #include <inttypes.h> #include <math.h> #include <sched.h> @@ -87,6 +89,7 @@ SensorService::SensorService() : mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED), mWakeLockAcquired(false) { mUidPolicy = new UidPolicy(this); + mSensorPrivacyPolicy = new SensorPrivacyPolicy(this); } bool SensorService::initializeHmacKey() { @@ -250,7 +253,7 @@ void SensorService::onFirstRef() { // it to maxSystemSocketBufferSize if necessary. FILE *fp = fopen("/proc/sys/net/core/wmem_max", "r"); char line[128]; - if (fp != NULL && fgets(line, sizeof(line), fp) != NULL) { + if (fp != nullptr && fgets(line, sizeof(line), fp) != nullptr) { line[sizeof(line) - 1] = '\0'; size_t maxSystemSocketBufferSize; sscanf(line, "%zu", &maxSystemSocketBufferSize); @@ -285,6 +288,9 @@ void SensorService::onFirstRef() { // Start watching UID changes to apply policy. mUidPolicy->registerSelf(); + + // Start watching sensor privacy changes + mSensorPrivacyPolicy->registerSelf(); } } } @@ -295,7 +301,7 @@ void SensorService::setSensorAccess(uid_t uid, bool hasAccess) { { Mutex::Autolock _l(mLock); for (size_t i = 0 ; i < activeConnections.size(); i++) { - if (activeConnections[i] != 0 && activeConnections[i]->getUid() == uid) { + if (activeConnections[i] != nullptr && activeConnections[i]->getUid() == uid) { activeConnections[i]->setSensorAccess(hasAccess); } } @@ -306,7 +312,7 @@ const Sensor& SensorService::registerSensor(SensorInterface* s, bool isDebug, bo int handle = s->getSensor().getHandle(); int type = s->getSensor().getType(); if (mSensors.add(handle, s, isDebug, isVirtual)){ - mRecentEvent.emplace(handle, new RecentEventLogger(type)); + mRecentEvent.emplace(handle, new SensorServiceUtil::RecentEventLogger(type)); return s->getSensor(); } else { return mSensors.getNonSensor(); @@ -337,6 +343,7 @@ SensorService::~SensorService() { delete entry.second; } mUidPolicy->unregisterSelf(); + mSensorPrivacyPolicy->unregisterSelf(); } status_t SensorService::dump(int fd, const Vector<String16>& args) { @@ -363,35 +370,16 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { } mCurrentOperatingMode = RESTRICTED; - // temporarily stop all sensor direct report - for (auto &i : mDirectConnections) { - sp<SensorDirectConnection> connection(i.promote()); - if (connection != nullptr) { - connection->stopAll(true /* backupRecord */); - } - } - - dev.disableAllSensors(); - // Clear all pending flush connections for all active sensors. If one of the active - // connections has called flush() and the underlying sensor has been disabled before a - // flush complete event is returned, we need to remove the connection from this queue. - for (size_t i=0 ; i< mActiveSensors.size(); ++i) { - mActiveSensors.valueAt(i)->clearAllPendingFlushConnections(); - } + // temporarily stop all sensor direct report and disable sensors + disableAllSensorsLocked(); mWhiteListedPackage.setTo(String8(args[1])); return status_t(NO_ERROR); } else if (args.size() == 1 && args[0] == String16("enable")) { // If currently in restricted mode, reset back to NORMAL mode else ignore. if (mCurrentOperatingMode == RESTRICTED) { mCurrentOperatingMode = NORMAL; - dev.enableAllSensors(); - // recover all sensor direct report - for (auto &i : mDirectConnections) { - sp<SensorDirectConnection> connection(i.promote()); - if (connection != nullptr) { - connection->recoverAll(); - } - } + // enable sensors and recover all sensor direct report + enableAllSensorsLocked(); } if (mCurrentOperatingMode == DATA_INJECTION) { resetToNormalModeLocked(); @@ -423,6 +411,11 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { } else { // Default dump the sensor list and debugging information. // + timespec curTime; + clock_gettime(CLOCK_REALTIME, &curTime); + struct tm* timeinfo = localtime(&(curTime.tv_sec)); + result.appendFormat("Captured at: %02d:%02d:%02d.%03d\n", timeinfo->tm_hour, + timeinfo->tm_min, timeinfo->tm_sec, (int)ns2ms(curTime.tv_nsec)); result.append("Sensor Device:\n"); result.append(SensorDevice::getInstance().dump().c_str()); @@ -471,11 +464,13 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { case DATA_INJECTION: result.appendFormat(" DATA_INJECTION : %s\n", mWhiteListedPackage.string()); } + result.appendFormat("Sensor Privacy: %s\n", + mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled"); result.appendFormat("%zd active connections\n", mActiveConnections.size()); for (size_t i=0 ; i < mActiveConnections.size() ; i++) { sp<SensorEventConnection> connection(mActiveConnections[i].promote()); - if (connection != 0) { + if (connection != nullptr) { result.appendFormat("Connection Number: %zu \n", i); connection->dump(result); } @@ -513,6 +508,52 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { return NO_ERROR; } +void SensorService::disableAllSensors() { + Mutex::Autolock _l(mLock); + disableAllSensorsLocked(); +} + +void SensorService::disableAllSensorsLocked() { + SensorDevice& dev(SensorDevice::getInstance()); + for (auto &i : mDirectConnections) { + sp<SensorDirectConnection> connection(i.promote()); + if (connection != nullptr) { + connection->stopAll(true /* backupRecord */); + } + } + dev.disableAllSensors(); + // Clear all pending flush connections for all active sensors. If one of the active + // connections has called flush() and the underlying sensor has been disabled before a + // flush complete event is returned, we need to remove the connection from this queue. + for (size_t i=0 ; i< mActiveSensors.size(); ++i) { + mActiveSensors.valueAt(i)->clearAllPendingFlushConnections(); + } +} + +void SensorService::enableAllSensors() { + Mutex::Autolock _l(mLock); + enableAllSensorsLocked(); +} + +void SensorService::enableAllSensorsLocked() { + // sensors should only be enabled if the operating state is not restricted and sensor + // privacy is not enabled. + if (mCurrentOperatingMode == RESTRICTED || mSensorPrivacyPolicy->isSensorPrivacyEnabled()) { + ALOGW("Sensors cannot be enabled: mCurrentOperatingMode = %d, sensor privacy = %s", + mCurrentOperatingMode, + mSensorPrivacyPolicy->isSensorPrivacyEnabled() ? "enabled" : "disabled"); + return; + } + SensorDevice& dev(SensorDevice::getInstance()); + dev.enableAllSensors(); + for (auto &i : mDirectConnections) { + sp<SensorDirectConnection> connection(i.promote()); + if (connection != nullptr) { + connection->recoverAll(); + } + } +} + // NOTE: This is a remote API - make sure all args are validated status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) { if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) { @@ -627,8 +668,13 @@ bool SensorService::threadLoop() { do { ssize_t count = device.poll(mSensorEventBuffer, numEventMax); if (count < 0) { - ALOGE("sensor poll failed (%s)", strerror(-count)); - break; + if(count == DEAD_OBJECT && device.isReconnecting()) { + device.reconnect(); + continue; + } else { + ALOGE("sensor poll failed (%s)", strerror(-count)); + break; + } } // Reset sensors_event_t.flags to zero for all events in the buffer. @@ -651,16 +697,18 @@ bool SensorService::threadLoop() { // sending events to clients (incrementing SensorEventConnection::mWakeLockRefCount) should // not be interleaved with decrementing SensorEventConnection::mWakeLockRefCount and // releasing the wakelock. - bool bufferHasWakeUpEvent = false; + uint32_t wakeEvents = 0; for (int i = 0; i < count; i++) { if (isWakeUpSensorEvent(mSensorEventBuffer[i])) { - bufferHasWakeUpEvent = true; - break; + wakeEvents++; } } - if (bufferHasWakeUpEvent && !mWakeLockAcquired) { - setWakeLockAcquiredLocked(true); + if (wakeEvents > 0) { + if (!mWakeLockAcquired) { + setWakeLockAcquiredLocked(true); + } + device.writeWakeLockHandled(wakeEvents); } recordLastValueLocked(mSensorEventBuffer, count); @@ -722,11 +770,11 @@ bool SensorService::threadLoop() { // on the hardware sensor. mapFlushEventsToConnections[i] will be the // SensorEventConnection mapped to the corresponding flush_complete_event in // mSensorEventBuffer[i] if such a mapping exists (NULL otherwise). - mMapFlushEventsToConnections[i] = NULL; + mMapFlushEventsToConnections[i] = nullptr; if (mSensorEventBuffer[i].type == SENSOR_TYPE_META_DATA) { const int sensor_handle = mSensorEventBuffer[i].meta_data.sensor; SensorRecord* rec = mActiveSensors.valueFor(sensor_handle); - if (rec != NULL) { + if (rec != nullptr) { mMapFlushEventsToConnections[i] = rec->getFirstPendingFlushConnection(); rec->removeFirstPendingFlushConnection(); } @@ -770,7 +818,7 @@ bool SensorService::threadLoop() { size_t numConnections = activeConnections.size(); for (size_t i=0 ; i < numConnections; ++i) { - if (activeConnections[i] != NULL) { + if (activeConnections[i] != nullptr) { activeConnections[i]->removeSensor(handle); } } @@ -783,7 +831,7 @@ bool SensorService::threadLoop() { bool needsWakeLock = false; size_t numConnections = activeConnections.size(); for (size_t i=0 ; i < numConnections; ++i) { - if (activeConnections[i] != 0) { + if (activeConnections[i] != nullptr) { activeConnections[i]->sendEvents(mSensorEventBuffer, count, mSensorEventScratch, mMapFlushEventsToConnections); needsWakeLock |= activeConnections[i]->needsWakeLock(); @@ -816,7 +864,7 @@ void SensorService::resetAllWakeLockRefCounts() { { Mutex::Autolock _l(mLock); for (size_t i=0 ; i < activeConnections.size(); ++i) { - if (activeConnections[i] != 0) { + if (activeConnections[i] != nullptr) { activeConnections[i]->resetWakeLockRefCount(); } } @@ -1021,15 +1069,15 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri int requestedMode, const String16& opPackageName) { // Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION. if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) { - return NULL; + return nullptr; } Mutex::Autolock _l(mLock); // To create a client in DATA_INJECTION mode to inject data, SensorService should already be // operating in DI mode. if (requestedMode == DATA_INJECTION) { - if (mCurrentOperatingMode != DATA_INJECTION) return NULL; - if (!isWhiteListedPackage(packageName)) return NULL; + if (mCurrentOperatingMode != DATA_INJECTION) return nullptr; + if (!isWhiteListedPackage(packageName)) return nullptr; } uid_t uid = IPCThreadState::self()->getCallingUid(); @@ -1063,6 +1111,12 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( const native_handle *resource) { Mutex::Autolock _l(mLock); + // No new direct connections are allowed when sensor privacy is enabled + if (mSensorPrivacyPolicy->isSensorPrivacyEnabled()) { + ALOGE("Cannot create new direct connections when sensor privacy is enabled"); + return nullptr; + } + struct sensors_direct_mem_t mem = { .type = type, .format = format, @@ -1277,6 +1331,15 @@ void SensorService::cleanupConnection(SensorEventConnection* c) { ALOGD_IF(DEBUG_CONNECTIONS, "... and it was the last connection"); mActiveSensors.removeItemsAt(i, 1); mActiveVirtualSensors.erase(handle); + + // If this is the last connection, then mark the RecentEventLogger as stale. This is + // critical for on-change events since the previous event is sent to a client if the + // sensor is already active. If two clients request the sensor at the same time, one + // of the clients would receive a stale event. + auto logger = mRecentEvent.find(handle); + if (logger != mRecentEvent.end()) { + logger->second->setLastEventStale(); + } delete rec; size--; } else { @@ -1325,7 +1388,7 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, } SensorRecord* rec = mActiveSensors.valueFor(handle); - if (rec == 0) { + if (rec == nullptr) { rec = new SensorRecord(connection); mActiveSensors.add(handle, rec); if (sensor->isVirtual()) { @@ -1343,16 +1406,17 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, auto logger = mRecentEvent.find(handle); if (logger != mRecentEvent.end()) { sensors_event_t event; - // It is unlikely that this buffer is empty as the sensor is already active. - // One possible corner case may be two applications activating an on-change - // sensor at the same time. - if(logger->second->populateLastEvent(&event)) { + // Verify that the last sensor event was generated from the current activation + // of the sensor. If not, it is possible for an on-change sensor to receive a + // sensor event that is stale if two clients re-activate the sensor + // simultaneously. + if(logger->second->populateLastEventIfCurrent(&event)) { event.sensor = handle; if (event.version == sizeof(sensors_event_t)) { if (isWakeUpSensorEvent(event) && !mWakeLockAcquired) { setWakeLockAcquiredLocked(true); } - connection->sendEvents(&event, 1, NULL); + connection->sendEvents(&event, 1, nullptr); if (!connection->needsWakeLock() && mWakeLockAcquired) { checkWakeLockStateLocked(); } @@ -1534,7 +1598,7 @@ status_t SensorService::flushSensor(const sp<SensorEventConnection>& connection, status_t err_flush = sensor->flush(connection.get(), handle); if (err_flush == NO_ERROR) { SensorRecord* rec = mActiveSensors.valueFor(handle); - if (rec != NULL) rec->addPendingFlushConnection(connection); + if (rec != nullptr) rec->addPendingFlushConnection(connection); } err = (err_flush != NO_ERROR) ? err_flush : err; } @@ -1592,7 +1656,7 @@ void SensorService::checkWakeLockStateLocked() { bool releaseLock = true; for (size_t i=0 ; i<mActiveConnections.size() ; i++) { sp<SensorEventConnection> connection(mActiveConnections[i].promote()); - if (connection != 0) { + if (connection != nullptr) { if (connection->needsWakeLock()) { releaseLock = false; break; @@ -1617,7 +1681,7 @@ void SensorService::populateActiveConnections( 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); } } @@ -1730,4 +1794,31 @@ bool SensorService::UidPolicy::isUidActiveLocked(uid_t uid) { 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 f71723d8c9..136ee27131 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -26,6 +26,7 @@ #include <sensor/ISensorServer.h> #include <sensor/ISensorEventConnection.h> #include <sensor/Sensor.h> +#include "android/hardware/BnSensorPrivacyListener.h" #include <utils/AndroidThreads.h> #include <utils/KeyedVector.h> @@ -59,7 +60,6 @@ namespace android { // --------------------------------------------------------------------------- class SensorInterface; -using namespace SensorServiceUtil; class SensorService : public BinderService<SensorService>, @@ -118,6 +118,8 @@ private: void onUidGone(uid_t uid, bool disabled); void onUidActive(uid_t uid); void onUidIdle(uid_t uid, bool disabled); + void onUidStateChanged(uid_t uid __unused, int32_t procState __unused, + int64_t procStateSeq __unused) {} void addOverrideUid(uid_t uid, bool active); void removeOverrideUid(uid_t uid); @@ -131,6 +133,30 @@ private: 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. @@ -274,10 +300,17 @@ private: // Prints the shell command help status_t printHelp(int out); + // temporarily stops all active direct connections and disables all sensors + void disableAllSensors(); + void disableAllSensorsLocked(); + // restarts the previously stopped direct connections and enables all sensors + void enableAllSensors(); + void enableAllSensorsLocked(); + static uint8_t sHmacGlobalKey[128]; static bool sHmacGlobalKeyIsValid; - SensorList mSensors; + SensorServiceUtil::SensorList mSensors; status_t mInitCheck; // Socket buffersize used to initialize BitTube. This size depends on whether batching is @@ -294,7 +327,7 @@ private: bool mWakeLockAcquired; sensors_event_t *mSensorEventBuffer, *mSensorEventScratch; wp<const SensorEventConnection> * mMapFlushEventsToConnections; - std::unordered_map<int, RecentEventLogger*> mRecentEvent; + std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent; SortedVector< wp<SensorDirectConnection> > mDirectConnections; Mode mCurrentOperatingMode; @@ -308,6 +341,7 @@ private: Vector<SensorRegistrationInfo> mLastNSensorRegistrations; sp<UidPolicy> mUidPolicy; + sp<SensorPrivacyPolicy> mSensorPrivacyPolicy; }; } // namespace android diff --git a/services/sensorservice/SensorsWrapper.h b/services/sensorservice/SensorsWrapper.h new file mode 100644 index 0000000000..d1a72345f7 --- /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 ff200660c1..b781744553 100644 --- a/services/sensorservice/hidl/EventQueue.cpp +++ b/services/sensorservice/hidl/EventQueue.cpp @@ -64,7 +64,7 @@ EventQueue::EventQueue( 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 fee6da1e60..938060063f 100644 --- a/services/sensorservice/hidl/SensorManager.cpp +++ b/services/sensorservice/hidl/SensorManager.cpp @@ -157,7 +157,7 @@ sp<Looper> SensorManager::getLooper() { 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 ce4daa5f35..0c1ea44eab 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -4,6 +4,7 @@ cc_defaults { "-DLOG_TAG=\"SurfaceFlinger\"", "-Wall", "-Werror", + "-Wformat", "-Wthread-safety", "-Wunused", "-Wunreachable-code", @@ -22,9 +23,12 @@ cc_defaults { "android.hardware.configstore-utils", "android.hardware.configstore@1.0", "android.hardware.configstore@1.1", + "android.hardware.configstore@1.2", "android.hardware.graphics.allocator@2.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,14 +47,17 @@ cc_defaults { "libhwbinder", "liblayers_proto", "liblog", + "libnativewindow", "libpdx_default_transport", "libprotobuf-cpp-lite", "libsync", "libtimestats_proto", "libui", + "libinput", "libutils", ], static_libs: [ + "librenderengine", "libserviceutils", "libtrace_proto", "libvr_manager", @@ -59,14 +66,18 @@ cc_defaults { 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: [ + "librenderengine", "libserviceutils", ], export_shared_lib_headers: [ "android.hardware.graphics.allocator@2.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", @@ -74,6 +85,18 @@ cc_defaults { ], } +cc_defaults { + name: "libsurfaceflinger_production_defaults", + defaults: ["libsurfaceflinger_defaults"], + cflags: [ + "-fvisibility=hidden", + "-fwhole-program-vtables", // requires ThinLTO + ], + lto: { + thin: true, + }, +} + cc_library_headers { name: "libsurfaceflinger_headers", export_include_dirs: ["."], @@ -86,78 +109,68 @@ filegroup { srcs: [ "BufferLayer.cpp", "BufferLayerConsumer.cpp", + "BufferQueueLayer.cpp", + "BufferStateLayer.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", + "LayerBE.cpp", "LayerProtoHelper.cpp", "LayerRejecter.cpp", "LayerStats.cpp", "LayerVector.cpp", - "MessageQueue.cpp", "MonitoredProducer.cpp", + "NativeWindowSurface.cpp", "RenderArea.cpp", - "RenderEngine/Description.cpp", - "RenderEngine/GLES20RenderEngine.cpp", - "RenderEngine/GLExtensions.cpp", - "RenderEngine/Image.cpp", - "RenderEngine/Mesh.cpp", - "RenderEngine/Program.cpp", - "RenderEngine/ProgramCache.cpp", - "RenderEngine/RenderEngine.cpp", - "RenderEngine/Surface.cpp", - "RenderEngine/Texture.cpp", + "Scheduler/DispSync.cpp", + "Scheduler/DispSyncSource.cpp", + "Scheduler/EventControlThread.cpp", + "Scheduler/EventThread.cpp", + "Scheduler/IdleTimer.cpp", + "Scheduler/LayerHistory.cpp", + "Scheduler/MessageQueue.cpp", + "Scheduler/Scheduler.cpp", + "Scheduler/SchedulerUtils.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceInterceptor.cpp", "SurfaceTracing.cpp", "TimeStats/TimeStats.cpp", - "Transform.cpp", + "TransactionCompletedThread.cpp", ], } cc_library_shared { + // Please use libsurfaceflinger_defaults to configure how the sources are + // built, so the same settings can be used elsewhere. name: "libsurfaceflinger", - defaults: ["libsurfaceflinger_defaults"], - cflags: [ - "-fvisibility=hidden", - "-Werror=format", - ], + defaults: ["libsurfaceflinger_production_defaults"], srcs: [ ":libsurfaceflinger_sources", + + // Note: SurfaceFlingerFactory is not in the default sources so that it + // can be easily replaced. + "SurfaceFlingerFactory.cpp", ], logtags: ["EventLog/EventLogTags.logtags"], - include_dirs: [ - "frameworks/native/vulkan/vkjson", - "frameworks/native/vulkan/include", - ], - cppflags: [ - "-fwhole-program-vtables", // requires ThinLTO - ], - lto: { - thin: true, - }, } -cc_binary { - name: "surfaceflinger", +cc_defaults { + name: "libsurfaceflinger_binary", defaults: ["surfaceflinger_defaults"], - init_rc: ["surfaceflinger.rc"], - srcs: ["main_surfaceflinger.cpp"], whole_static_libs: [ "libsigchain", ], @@ -165,15 +178,17 @@ cc_binary { "android.frameworks.displayservice@1.0", "android.hardware.configstore-utils", "android.hardware.configstore@1.0", + "android.hardware.configstore@1.2", "android.hardware.graphics.allocator@2.0", "libbinder", "libcutils", "libdisplayservicehidl", "libhidlbase", "libhidltransport", + "libinput", "liblayers_proto", "liblog", - "libsurfaceflinger", + "libsync", "libtimestats_proto", "libutils", ], @@ -182,19 +197,19 @@ cc_binary { "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"], } cc_library_shared { diff --git a/services/surfaceflinger/Barrier.h b/services/surfaceflinger/Barrier.h index 3e9d4433ad..97028a89fa 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 f5b5eda9ec..0a3be71b79 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -23,9 +23,10 @@ #include "Colorizer.h" #include "DisplayDevice.h" #include "LayerRejecter.h" -#include "clz.h" -#include "RenderEngine/RenderEngine.h" +#include "TimeStats/TimeStats.h" + +#include <renderengine/RenderEngine.h> #include <gui/BufferItem.h> #include <gui/BufferQueue.h> @@ -50,28 +51,16 @@ 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()); - - mTextureName = mFlinger->getNewTexture(); - mTexture.init(Texture::TEXTURE_EXTERNAL, mTextureName); +BufferLayer::BufferLayer(const LayerCreationArgs& args) + : Layer(args), mTextureName(args.flinger->getNewTexture()) { + ALOGV("Creating Layer %s", args.name.string()); - if (flags & ISurfaceComposerClient::eNonPremultiplied) mPremultipliedAlpha = false; + mTexture.init(renderengine::Texture::TEXTURE_EXTERNAL, mTextureName); - mCurrentState.requested = mCurrentState.active; + mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied); - // drawing state & current state are identical - mDrawingState = mCurrentState; + mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; + mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp; } BufferLayer::~BufferLayer() { @@ -81,15 +70,17 @@ BufferLayer::~BufferLayer() { ALOGE("Found stale hardware composer layers when destroying " "surface flinger layer %s", mName.string()); - destroyAllHwcLayers(); + destroyAllHwcLayersPlusChildren(); } + + mFlinger->mTimeStats->onDestroy(getSequence()); } void BufferLayer::useSurfaceDamage() { if (mFlinger->mForceFullDamage) { surfaceDamageRegion = Region::INVALID_REGION; } else { - surfaceDamageRegion = mConsumer->getSurfaceDamage(); + surfaceDamageRegion = getDrawingSurfaceDamage(); } } @@ -97,46 +88,27 @@ void BufferLayer::useEmptyDamage() { 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 ((getBE().compositionInfo.hwc.sidebandStream == nullptr) && (mActiveBuffer == nullptr)) { + return false; + } + + // if the layer has the opaque flag, then we're always opaque, + // otherwise we use the current buffer's format. + return ((s.flags & layer_state_t::eLayerOpaque) != 0) || getOpacityForFormat(getPixelFormat()); } bool BufferLayer::isVisible() const { return !(isHiddenByPolicy()) && getAlpha() > 0.0f && - (getBE().compositionInfo.mBuffer != nullptr || - getBE().compositionInfo.hwc.sidebandStream != nullptr); + (mActiveBuffer != nullptr || getBE().compositionInfo.hwc.sidebandStream != nullptr); } bool BufferLayer::isFixedSize() const { return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE; } -status_t BufferLayer::setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { - uint32_t const maxSurfaceDims = - min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims()); - - // never allow a surface larger than what our underlying GL implementation - // can handle. - if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) { - ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h)); - return BAD_VALUE; - } - - mFormat = format; - - mPotentialCursor = (flags & ISurfaceComposerClient::eCursorWindow) ? true : false; - mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false; - mCurrentOpacity = getOpacityForFormat(format); - - mConsumer->setDefaultBufferSize(w, h); - mConsumer->setDefaultBufferFormat(format); - mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); - - return NO_ERROR; -} - static constexpr mat4 inverseOrientation(uint32_t transform) { const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); @@ -159,10 +131,10 @@ static constexpr mat4 inverseOrientation(uint32_t transform) { * onDraw will draw the current layer onto the presentable buffer */ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const { + bool useIdentityTransform) { ATRACE_CALL(); - if (CC_UNLIKELY(getBE().compositionInfo.mBuffer == 0)) { + 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 @@ -191,7 +163,7 @@ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, // 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(); + status_t err = 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 @@ -204,12 +176,12 @@ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, if (!blackOutLayer) { // TODO: we could be more subtle with isFixedSize() - const bool useFiltering = getFiltering() || needsFiltering(renderArea) || isFixedSize(); + const bool useFiltering = needsFiltering(renderArea) || 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()) { /* @@ -240,8 +212,7 @@ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, } // Set things up for texturing. - mTexture.setDimensions(getBE().compositionInfo.mBuffer->getWidth(), - getBE().compositionInfo.mBuffer->getHeight()); + mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight()); mTexture.setFiltering(useFiltering); mTexture.setMatrix(textureMatrix); @@ -253,52 +224,100 @@ void BufferLayer::onDraw(const RenderArea& renderArea, const Region& clip, engine.disableTexturing(); } -void BufferLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { - mConsumer->setReleaseFence(releaseFence); +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 && + getBE().compositionInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102); } -void BufferLayer::abandon() { - mConsumer->abandon(); -} +void BufferLayer::setPerFrameData(DisplayId displayId, const ui::Transform& transform, + const Rect& viewport, int32_t supportedPerFrameMetadata) { + RETURN_IF_NO_HWC_LAYER(displayId); -bool BufferLayer::shouldPresentNow(const DispSync& dispSync) const { - if (mSidebandStreamChanged || mAutoRefresh) { - return true; + // 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)); + + auto& hwcInfo = getBE().mHwcLayers[displayId]; + 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); } + getBE().compositionInfo.hwc.visibleRegion = visible; - Mutex::Autolock lock(mQueueItemLock); - if (mQueueItems.empty()) { - return false; + 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); } - auto timestamp = mQueueItems[0].mTimestamp; - nsecs_t expectedPresent = mConsumer->computeExpectedPresent(dispSync); + getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion; - // 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); + // Sideband layers + if (getBE().compositionInfo.hwc.sidebandStream.get()) { + setCompositionType(displayId, 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)); + } + getBE().compositionInfo.compositionType = HWC2::Composition::Sideband; + return; + } - bool isDue = timestamp < expectedPresent; - return isDue || !isPlausible; -} + // Device or Cursor layers + if (mPotentialCursor) { + ALOGV("[%s] Requesting Cursor composition", mName.string()); + setCompositionType(displayId, HWC2::Composition::Cursor); + } else { + ALOGV("[%s] Requesting Device composition", mName.string()); + setCompositionType(displayId, HWC2::Composition::Device); + } -void BufferLayer::setTransformHint(uint32_t orientation) const { - mConsumer->setTransformHint(orientation); + 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 = getDrawingHdrMetadata(); + error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, metadata); + if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) { + ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + + error = hwcLayer->setColorTransform(getColorTransform()); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace; + getBE().compositionInfo.hwc.hdrMetadata = getDrawingHdrMetadata(); + getBE().compositionInfo.hwc.supportedPerFrameMetadata = supportedPerFrameMetadata; + getBE().compositionInfo.hwc.colorTransform = getColorTransform(); + + setHwcLayerBuffer(displayId); } 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 +327,18 @@ bool BufferLayer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFenc // 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 +348,13 @@ bool BufferLayer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFenc } 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 +363,20 @@ bool BufferLayer::onPostComposition(const std::shared_ptr<FenceTime>& glDoneFenc 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) { +Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) { 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; + std::optional<Region> sidebandStreamDirtyRegion = latchSidebandStream(recomputeVisibleRegions); - const State& s(getDrawingState()); - return getTransform().transform(Region(Rect(s.active.w, s.active.h))); + if (sidebandStreamDirtyRegion) { + return *sidebandStreamDirtyRegion; } - Region outDirtyRegion; - if (mQueuedFrames <= 0 && !mAutoRefresh) { - return outDirtyRegion; + Region dirtyRegion; + + if (!hasReadyFrame()) { + return dirtyRegion; } // if we've already called updateTexImage() without going through @@ -405,119 +385,42 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime // compositionComplete() call. // we'll trigger an update in onPreComposition(). if (mRefreshPending) { - return outDirtyRegion; + return dirtyRegion; } // If the head buffer's acquire fence hasn't signaled yet, return and // try again later - if (!headFenceHasSignaled()) { + if (!fenceHasSignaled()) { mFlinger->signalLayerUpdate(); - return outDirtyRegion; + return dirtyRegion; } // Capture the old state of the layer for comparisons later + Mutex::Autolock lock(mStateMutex); 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; - } - - // 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; + return dirtyRegion; } - 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(); + status_t err = updateTexImage(recomputeVisibleRegions, latchTime, releaseFence); + if (err != NO_ERROR) { + return dirtyRegion; } - // 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 dirtyRegion; } mBufferLatched = true; - mPreviousFrameNumber = mCurrentFrameNumber; - mCurrentFrameNumber = mConsumer->getFrameNumber(); - { - Mutex::Autolock lock(mFrameEventHistoryMutex); - mFrameEventHistory.addLatch(mCurrentFrameNumber, latchTime); + err = updateFrameNumber(latchTime); + if (err != NO_ERROR) { + return dirtyRegion; } mRefreshPending = true; @@ -528,38 +431,36 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime 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) || + Rect crop(getDrawingCrop()); + const uint32_t transform(getDrawingTransform()); + const uint32_t scalingMode(getDrawingScalingMode()); + if ((crop != mCurrentCrop) || (transform != mCurrentTransform) || (scalingMode != mCurrentScalingMode)) { mCurrentCrop = crop; mCurrentTransform = transform; @@ -568,15 +469,13 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime } 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; } @@ -603,203 +502,78 @@ Region BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime } // 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); + return getTransformLocked().transform(Region(getBufferSize(s))); } -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)); +// transaction +void BufferLayer::notifyAvailableFrames() { + auto headFrameNumber = getHeadFrameNumber(); + bool headFenceSignaled = fenceHasSignaled(); + Mutex::Autolock lock(mLocalSyncPointMutex); + for (auto& point : mLocalSyncPoints) { + if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) { + point->setFrameAvailable(); } - 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; +bool BufferLayer::hasReadyFrame() const { + return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh(); } -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); +uint32_t BufferLayer::getEffectiveScalingMode() const { + if (mOverrideScalingMode >= 0) { + return mOverrideScalingMode; } - const sp<const DisplayDevice> hw(mFlinger->getDefaultDisplayDevice()); - updateTransformHint(hw); + return mCurrentScalingMode; } -// --------------------------------------------------------------------------- -// 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); +bool BufferLayer::isProtected() const { + const sp<GraphicBuffer>& buffer(mActiveBuffer); + return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); +} - // Wake up any pending callbacks - mLastFrameNumberReceived = item.mFrameNumber; - mQueueItemCondition.broadcast(); +bool BufferLayer::latchUnsignaledBuffers() { + static bool propertyLoaded = false; + static bool latch = false; + static std::mutex mutex; + std::lock_guard<std::mutex> lock(mutex); + if (!propertyLoaded) { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.latch_unsignaled", value, "0"); + latch = atoi(value); + propertyLoaded = true; } - - mFlinger->signalLayerUpdate(); + return latch; } -void BufferLayer::onFrameReplaced(const BufferItem& item) { - { // Autolock scope - Mutex::Autolock lock(mQueueItemLock); +// h/w composer set-up +bool BufferLayer::allTransactionsSignaled() { + auto headFrameNumber = getHeadFrameNumberLocked(); + bool matchingFramesFound = false; + bool allTransactionsApplied = true; + Mutex::Autolock lock(mLocalSyncPointMutex); - // 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()); - } + for (auto& point : mLocalSyncPoints) { + if (point->getFrameNumber() > headFrameNumber) { + break; } + matchingFramesFound = true; - if (mQueueItems.empty()) { - ALOGE("Can't replace a frame on an empty queue"); - return; + if (!point->frameIsAvailable()) { + // We haven't notified the remote layer that the frame for + // this point is available yet. Notify it now, and then + // abort this attempt to latch. + point->setFrameAvailable(); + allTransactionsApplied = false; + break; } - 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(); + allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied(); } -} - -bool BufferLayer::needsFiltering(const RenderArea& renderArea) const { - return mNeedsFiltering || renderArea.needsFiltering(); + return !matchingFramesFound || allTransactionsApplied; } // As documented in libhardware header, formats in the range @@ -824,15 +598,13 @@ bool BufferLayer::getOpacityForFormat(uint32_t format) { 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); +bool BufferLayer::needsFiltering(const RenderArea& renderArea) const { + return mNeedsFiltering || renderArea.needsFiltering(); } void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const { ATRACE_CALL(); + Mutex::Autolock lock(mStateMutex); const State& s(getDrawingState()); computeGeometry(renderArea, getBE().mMesh, useIdentityTransform); @@ -851,157 +623,89 @@ void BufferLayer::drawWithOpenGL(const RenderArea& renderArea, bool useIdentityT * 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 + const Rect bounds{computeBoundsLocked()}; // Rounds from FloatRect - Transform t = getTransform(); + ui::Transform t = getTransformLocked(); 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(); - } - } + const int bufferWidth = getBufferSize(s).getWidth(); + const int bufferHeight = getBufferSize(s).getHeight(); - 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); + const float left = float(win.left) / float(bufferWidth); + const float top = float(win.top) / float(bufferHeight); + const float right = float(win.right) / float(bufferWidth); + const float bottom = float(win.bottom) / float(bufferHeight); // 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>()); + renderengine::Mesh::VertexArray<vec2> texCoords(getBE().mMesh.getTexCoordArray<vec2>()); + // flip texcoords vertically because BufferLayerConsumer expects them to be in GL convention 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); + const auto roundedCornerState = getRoundedCornerStateLocked(); + const auto cropRect = roundedCornerState.cropRect; + setupRoundedCornersCropCoordinates(win, cropRect); + auto& engine(mFlinger->getRenderEngine()); engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), false /* disableTexture */, - getColor()); + getColor(), roundedCornerState.radius); engine.setSourceDataSpace(mCurrentDataSpace); if (isHdrY410()) { engine.setSourceY410BT2020(true); } + engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight()); + 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; - } - return static_cast<uint32_t>(producerStickyTransform); -} - -bool BufferLayer::latchUnsignaledBuffers() { - static bool propertyLoaded = false; - static bool latch = false; - static std::mutex mutex; - std::lock_guard<std::mutex> lock(mutex); - if (!propertyLoaded) { - char value[PROPERTY_VALUE_MAX] = {}; - property_get("debug.sf.latch_unsignaled", value, "0"); - latch = atoi(value); - propertyLoaded = true; - } - return latch; +uint64_t BufferLayer::getHeadFrameNumber() const { + Mutex::Autolock lock(mStateMutex); + return getHeadFrameNumberLocked(); } -uint64_t BufferLayer::getHeadFrameNumber() const { - Mutex::Autolock lock(mQueueItemLock); - if (!mQueueItems.empty()) { - return mQueueItems[0].mFrameNumber; +uint64_t BufferLayer::getHeadFrameNumberLocked() const { + if (hasFrameUpdateLocked()) { + return getFrameNumber(); } else { return mCurrentFrameNumber; } } -bool BufferLayer::headFenceHasSignaled() const { - if (latchUnsignaledBuffers()) { - return true; +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)); } - 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; + if (mActiveBuffer == nullptr) { + return Rect::INVALID_RECT; } - return mQueueItems[0].mFenceTime->getSignalTime() != - Fence::SIGNAL_TIME_PENDING; -} -uint32_t BufferLayer::getEffectiveScalingMode() const { - if (mOverrideScalingMode >= 0) { - return mOverrideScalingMode; - } - return mCurrentScalingMode; -} - -// ---------------------------------------------------------------------------- -// transaction -// ---------------------------------------------------------------------------- + uint32_t bufWidth = mActiveBuffer->getWidth(); + uint32_t bufHeight = mActiveBuffer->getHeight(); -void BufferLayer::notifyAvailableFrames() { - auto headFrameNumber = getHeadFrameNumber(); - bool headFenceSignaled = headFenceHasSignaled(); - Mutex::Autolock lock(mLocalSyncPointMutex); - for (auto& point : mLocalSyncPoints) { - if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled) { - point->setFrameAvailable(); - } + // Undo any transformations on the buffer and return the result. + if (mCurrentTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); } -} - -sp<IGraphicBufferProducer> BufferLayer::getProducer() const { - return mProducer; -} - -// --------------------------------------------------------------------------- -// h/w composer set-up -// --------------------------------------------------------------------------- - -bool BufferLayer::allTransactionsSignaled() { - auto headFrameNumber = getHeadFrameNumber(); - bool matchingFramesFound = false; - bool allTransactionsApplied = true; - Mutex::Autolock lock(mLocalSyncPointMutex); - for (auto& point : mLocalSyncPoints) { - if (point->getFrameNumber() > headFrameNumber) { - break; + if (getTransformToDisplayInverseLocked()) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); } - matchingFramesFound = true; - - if (!point->frameIsAvailable()) { - // We haven't notified the remote layer that the frame for - // this point is available yet. Notify it now, and then - // abort this attempt to latch. - point->setFrameAvailable(); - allTransactionsApplied = false; - break; - } - - allTransactionsApplied = allTransactionsApplied && point->transactionIsApplied(); } - return !matchingFramesFound || allTransactionsApplied; + + return Rect(bufWidth, bufHeight); } } // namespace android diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index bf0ca69253..98ae2861f2 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -24,14 +24,12 @@ #include "FrameTracker.h" #include "LayerVector.h" #include "MonitoredProducer.h" -#include "RenderEngine/Mesh.h" -#include "RenderEngine/Texture.h" #include "SurfaceFlinger.h" -#include "Transform.h" #include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> - +#include <renderengine/Mesh.h> +#include <renderengine/Texture.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> @@ -41,158 +39,168 @@ #include <utils/String8.h> #include <utils/Timers.h> +#include <system/window.h> // For NATIVE_WINDOW_SCALING_MODE_FREEZE + #include <stdint.h> #include <sys/types.h> #include <list> 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 // ----------------------------------------------------------------------- +public: + // 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; - /* - * getTypeId - Provide unique string for each class type in the Layer - * hierarchy - */ + // 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; + bool isOpaque(const Layer::State& s) const override; + + // isVisible - true if this layer is visible, false otherwise + bool isVisible() const override EXCLUDES(mStateMutex); - /* - * isVisible - true if this layer is visible, false otherwise - */ - bool isVisible() const override; + // isProtected - true if the layer may contain protected content in the + // GRALLOC_USAGE_PROTECTED sense. + bool isProtected() const override; - /* - * isFixedSize - true if content has a fixed size - */ + // 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. - */ + // onDraw - draws the surface. void onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const override; + bool useIdentityTransform) override; - void onLayerDisplayed(const sp<Fence>& releaseFence) override; + bool isHdrY410() const 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; + void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport, + int32_t supportedPerFrameMetadata) override; -public: 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 EXCLUDES(mStateMutex); + + // 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. + // If there was a GL composition step rendering the previous frame, then + // releaseFence will be populated with a native fence that fires when + // composition has completed. + Region latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) override EXCLUDES(mStateMutex); - // If a buffer was replaced this frame, release the former buffer - void releasePendingBuffer(nsecs_t dequeueReadyTime); - - /* - * 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 notifyAvailableFrames() override; - void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override; + bool hasReadyFrame() const override EXCLUDES(mStateMutex); - bool isOpaque(const Layer::State& s) const override; + // Returns the current scaling mode, unless mOverrideScalingMode + // is set, in which case, it returns mOverrideScalingMode + uint32_t getEffectiveScalingMode() const override; + // ----------------------------------------------------------------------- + // ----------------------------------------------------------------------- + // Functions that must be implemented by derived classes + // ----------------------------------------------------------------------- private: - void onFirstRef() override; + virtual bool fenceHasSignaled() const EXCLUDES(mStateMutex) = 0; - // Interface implementation for - // BufferLayerConsumer::ContentsChangedListener - void onFrameAvailable(const BufferItem& item) override; - void onFrameReplaced(const BufferItem& item) override; - void onSidebandStreamChanged() override; + virtual nsecs_t getDesiredPresentTime() = 0; + std::shared_ptr<FenceTime> getCurrentFenceTime() const EXCLUDES(mStateMutex) { + Mutex::Autolock lock(mStateMutex); + return getCurrentFenceTimeLocked(); + } - // needsLinearFiltering - true if this surface's state requires filtering - bool needsFiltering(const RenderArea& renderArea) const; + virtual std::shared_ptr<FenceTime> getCurrentFenceTimeLocked() const REQUIRES(mStateMutex) = 0; - static bool getOpacityForFormat(uint32_t format); + virtual void getDrawingTransformMatrix(float *matrix) = 0; + virtual uint32_t getDrawingTransform() const REQUIRES(mStateMutex) = 0; + virtual ui::Dataspace getDrawingDataSpace() const REQUIRES(mStateMutex) = 0; + virtual Rect getDrawingCrop() const REQUIRES(mStateMutex) = 0; + virtual uint32_t getDrawingScalingMode() const = 0; + virtual Region getDrawingSurfaceDamage() const EXCLUDES(mStateMutex) = 0; + virtual const HdrMetadata& getDrawingHdrMetadata() const EXCLUDES(mStateMutex) = 0; + virtual int getDrawingApi() const EXCLUDES(mStateMutex) = 0; + virtual PixelFormat getPixelFormat() const = 0; - // drawing - void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const; + virtual uint64_t getFrameNumber() const = 0; - // Temporary - Used only for LEGACY camera mode. - uint32_t getProducerStickyTransform() const; + virtual bool getAutoRefresh() const = 0; + virtual bool getSidebandStreamChanged() const = 0; - // Loads the corresponding system property once per process - static bool latchUnsignaledBuffers(); + virtual std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) + EXCLUDES(mStateMutex) = 0; - uint64_t getHeadFrameNumber() const; - bool headFenceHasSignaled() const; + virtual bool hasFrameUpdateLocked() const REQUIRES(mStateMutex) = 0; - // Returns the current scaling mode, unless mOverrideScalingMode - // is set, in which case, it returns mOverrideScalingMode - uint32_t getEffectiveScalingMode() const override; + virtual void setFilteringEnabled(bool enabled) = 0; -public: - void notifyAvailableFrames() override; + virtual status_t bindTextureImage() EXCLUDES(mStateMutex) = 0; + virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& flushFence) REQUIRES(mStateMutex) = 0; - PixelFormat getPixelFormat() const override { return mFormat; } - sp<IGraphicBufferProducer> getProducer() const; + virtual status_t updateActiveBuffer() REQUIRES(mStateMutex) = 0; + virtual status_t updateFrameNumber(nsecs_t latchTime) = 0; -private: - sp<BufferLayerConsumer> mConsumer; + virtual void setHwcLayerBuffer(DisplayId displayId) EXCLUDES(mStateMutex) = 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; + bool allTransactionsSignaled() REQUIRES(mStateMutex); + + static bool getOpacityForFormat(uint32_t format); + + bool hasFrameUpdate() const EXCLUDES(mStateMutex) { + Mutex::Autolock lock(mStateMutex); + return hasFrameUpdateLocked(); + } - // constants - uint32_t mTextureName; // from GLES - PixelFormat mFormat; + // from GLES + const uint32_t mTextureName; + +private: + // needsLinearFiltering - true if this surface's state requires filtering + bool needsFiltering(const RenderArea& renderArea) const; + + // drawing + void drawWithOpenGL(const RenderArea& renderArea, bool useIdentityTransform) const + EXCLUDES(mStateMutex); + + uint64_t getHeadFrameNumber() const EXCLUDES(mStateMutex); + + uint64_t getHeadFrameNumberLocked() const REQUIRES(mStateMutex); + + uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE}; + + // main thread. + bool mBufferLatched{false}; // TODO: Use mActiveBuffer? - // 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; + mutable renderengine::Texture mTexture; + + bool mRefreshPending{false}; - bool mUpdateTexImageFailed; // This is only accessed on the main thread. - bool mRefreshPending; + Rect getBufferSize(const State& s) const override REQUIRES(mStateMutex); }; } // namespace android diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp index 87333d0ffd..6826050d86 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 @@ namespace android { 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,56 +98,10 @@ void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedLis 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) { + uint64_t maxFrameNumber, + const sp<Fence>& releaseFence) { ATRACE_CALL(); BLC_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); @@ -160,18 +111,12 @@ status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const Dis 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; @@ -201,12 +146,12 @@ status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, const Dis } // Release the previous buffer. - err = updateAndReleaseLocked(item, &mPendingRelease); + err = updateAndReleaseLocked(item, &mPendingRelease, releaseFence); if (err != NO_ERROR) { return err; } - if (!SyncFeatures::getInstance().useNativeFenceSync()) { + if (!mRE.useNativeFenceSync()) { // Bind the new buffer to the GL texture. // // Older devices require the "implicit" synchronization provided @@ -234,8 +179,7 @@ void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) { return; } - auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer - : mCurrentTextureImage->graphicBuffer(); + auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer : mCurrentTextureBuffer; auto err = addReleaseFence(slot, buffer, fence); if (err != OK) { BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); @@ -271,32 +215,24 @@ status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t pres } // 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. if (item->mGraphicBuffer != nullptr) { - mImages[item->mSlot] = new Image(item->mGraphicBuffer, mRE); + mImages[item->mSlot] = nullptr; } 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) { + PendingRelease* pendingRelease, + const sp<Fence>& releaseFence) { 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(); + err = syncForReleaseLocked(releaseFence); 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. @@ -308,19 +244,18 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, } BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, - mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : 0, - slot, mSlots[slot].mGraphicBuffer->handle); + mCurrentTextureBuffer != nullptr ? mCurrentTextureBuffer->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]; + sp<GraphicBuffer> nextTextureBuffer = mSlots[slot].mGraphicBuffer; // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (pendingRelease == nullptr) { - status_t status = - releaseBufferLocked(mCurrentTexture, mCurrentTextureImage->graphicBuffer()); + status_t status = releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer); if (status < NO_ERROR) { BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); @@ -329,14 +264,15 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, } } else { pendingRelease->currentTexture = mCurrentTexture; - pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer(); + pendingRelease->graphicBuffer = mCurrentTextureBuffer; pendingRelease->isPending = true; } } // Update the BufferLayerConsumer state. mCurrentTexture = slot; - mCurrentTextureImage = nextTextureImage; + mCurrentTextureBuffer = nextTextureBuffer; + mCurrentTextureImageFreed = nullptr; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; @@ -357,41 +293,63 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, status_t BufferLayerConsumer::bindTextureImageLocked() { ATRACE_CALL(); + mRE.checkErrors(); - if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) { + // It is possible for the current slot's buffer to be freed before a new one + // is bound. In that scenario we still want to bind the image. + if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureBuffer == nullptr) { BLC_LOGE("bindTextureImage: no currently-bound texture"); mRE.bindExternalTextureImage(mTexName, *mRE.createImage()); return NO_INIT; } - 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; + renderengine::Image* imageToRender; + + // mCurrentTextureImageFreed is non-null iff mCurrentTexture == + // BufferQueue::INVALID_BUFFER_SLOT, so we can omit that check. + if (mCurrentTextureImageFreed) { + imageToRender = mCurrentTextureImageFreed.get(); + } else if (mImages[mCurrentTexture]) { + imageToRender = mImages[mCurrentTexture].get(); + } else { + std::unique_ptr<renderengine::Image> image = mRE.createImage(); + bool success = image->setNativeWindowBuffer(mCurrentTextureBuffer->getNativeBuffer(), + mCurrentTextureBuffer->getUsage() & + GRALLOC_USAGE_PROTECTED); + if (!success) { + BLC_LOGE("bindTextureImage: Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 + " fmt=%d", + mCurrentTextureBuffer->getWidth(), mCurrentTextureBuffer->getHeight(), + mCurrentTextureBuffer->getStride(), mCurrentTextureBuffer->getUsage(), + mCurrentTextureBuffer->getPixelFormat()); + BLC_LOGW("bindTextureImage: can't create image on slot=%d", mCurrentTexture); + mRE.bindExternalTextureImage(mTexName, *image); + return UNKNOWN_ERROR; + } + imageToRender = image.get(); + // Cache the image here so that we can reuse it. + mImages[mCurrentTexture] = std::move(image); } - mRE.bindExternalTextureImage(mTexName, mCurrentTextureImage->image()); + mRE.bindExternalTextureImage(mTexName, *imageToRender); // Wait for the new buffer to be ready. return doFenceWaitLocked(); } -status_t BufferLayerConsumer::syncForReleaseLocked() { +status_t BufferLayerConsumer::syncForReleaseLocked(const sp<Fence>& releaseFence) { BLC_LOGV("syncForReleaseLocked"); if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (SyncFeatures::getInstance().useNativeFenceSync()) { - base::unique_fd fenceFd = mRE.flush(); - if (fenceFd == -1) { + if (mRE.useNativeFenceSync() && releaseFence != Fence::NO_FENCE) { + // TODO(alecmouri): fail further upstream if the fence is invalid + if (!releaseFence->isValid()) { 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); + status_t err = + addReleaseFenceLocked(mCurrentTexture, mCurrentTextureBuffer, releaseFence); if (err != OK) { BLC_LOGE("syncForReleaseLocked: error adding release fence: " "%s (%d)", @@ -418,26 +376,23 @@ void BufferLayerConsumer::setFilteringEnabled(bool enabled) { 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) { BLC_LOGD("computeCurrentTransformMatrixLocked: " - "mCurrentTextureImage is nullptr"); + "mCurrentTextureBuffer is nullptr"); } - const Rect& cropRect = canUseImageCrop(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop; - GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, cropRect, mCurrentTransform, - mFilteringEnabled); + GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, mCurrentTextureBuffer, mCurrentCrop, + mCurrentTransform, mFilteringEnabled); } nsecs_t BufferLayerConsumer::getTimestamp() { @@ -485,7 +440,7 @@ sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot) const { *outSlot = mCurrentTexture; } - return (mCurrentTextureImage == nullptr) ? nullptr : mCurrentTextureImage->graphicBuffer(); + return mCurrentTextureBuffer; } Rect BufferLayerConsumer::getCurrentCrop() const { @@ -516,13 +471,8 @@ std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const { } 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); @@ -548,8 +498,10 @@ void BufferLayerConsumer::freeBufferLocked(int slotIndex) { BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); if (slotIndex == mCurrentTexture) { mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; + mCurrentTextureImageFreed = std::move(mImages[slotIndex]); + } else { + mImages[slotIndex] = nullptr; } - mImages[slotIndex].clear(); ConsumerBase::freeBufferLocked(slotIndex); } @@ -588,7 +540,7 @@ void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* ne void BufferLayerConsumer::abandonLocked() { BLC_LOGV("abandonLocked"); - mCurrentTextureImage.clear(); + mCurrentTextureBuffer.clear(); ConsumerBase::abandonLocked(); } @@ -606,39 +558,4 @@ void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const 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; - } - - 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 f81cdb1d91..ea46245ea4 100644 --- a/services/surfaceflinger/BufferLayerConsumer.h +++ b/services/surfaceflinger/BufferLayerConsumer.h @@ -37,10 +37,10 @@ class DispSync; class Layer; class String8; -namespace RE { +namespace renderengine { class RenderEngine; class Image; -} // namespace RE +} // namespace renderengine /* * BufferLayerConsumer consumes buffers of graphics data from a BufferQueue, @@ -73,15 +73,13 @@ public: // BufferLayerConsumer constructs a new BufferLayerConsumer object. The // tex parameter indicates the name of the RenderEngine texture to which // images are to be streamed. - BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, RE::RenderEngine& engine, + BufferLayerConsumer(const sp<IGraphicBufferConsumer>& bq, renderengine::RenderEngine& engine, uint32_t tex, Layer* layer); // Sets the contents changed listener. This should be used instead of // ConsumerBase::setFrameAvailableListener(). void setContentsChangedListener(const wp<ContentsChangedListener>& listener); - nsecs_t computeExpectedPresent(const DispSync& dispSync); - // updateTexImage acquires the most recently queued buffer, and sets the // image contents of the target texture to it. // @@ -93,8 +91,9 @@ public: // 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, + const sp<Fence>& releaseFence); // See BufferLayerConsumer::bindTextureImageLocked(). status_t bindTextureImage(); @@ -186,8 +185,7 @@ protected: // 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; @@ -208,56 +206,17 @@ protected: // 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, + const sp<Fence>& releaseFence = Fence::NO_FENCE); - // 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> { - 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); - - 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; - }; - // 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); @@ -283,7 +242,7 @@ private: // 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(); + status_t syncForReleaseLocked(const sp<Fence>& releaseFence); // The default consumer usage flags that BufferLayerConsumer always sets on its // BufferQueue instance; these will be OR:d with any additional flags passed @@ -291,10 +250,10 @@ private: // 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 + // mCurrentTextureImage 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; + sp<GraphicBuffer> mCurrentTextureBuffer; // mCurrentCrop is the crop rectangle that applies to the current texture. // It gets set each time updateTexImage is called. @@ -352,7 +311,7 @@ private: // setFilteringEnabled(). bool mFilteringEnabled; - RE::RenderEngine& mRE; + renderengine::RenderEngine& mRE; // mTexName is the name of the RenderEngine texture to which streamed // images will be bound when bindTexImage is called. It is set at @@ -364,15 +323,6 @@ private: wp<ContentsChangedListener> mContentsChangedListener; - // mImages stores the buffers that have been allocated by the BufferQueue - // for each buffer slot. It is initialized to null pointers, and gets - // filled in with the result of BufferQueue::acquire when the - // client dequeues a buffer from a - // slot that has not yet been used. The buffer allocated to a slot will also - // be replaced if the requested buffer usage or geometry differs from that - // of the buffer allocated to a slot. - sp<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS]; - // mCurrentTexture is the buffer slot index of the buffer that is currently // bound to the RenderEngine texture. It is initialized to INVALID_BUFFER_SLOT, // indicating that no buffer slot is currently bound to the texture. Note, @@ -381,6 +331,17 @@ private: // reset mCurrentTexture to INVALID_BUFFER_SLOT. int mCurrentTexture; + // Cached image used for rendering the current texture through GPU + // composition, which contains the cached image after freeBufferLocked is + // called on the current buffer. Whenever latchBuffer is called, this is + // expected to be cleared. Then, if bindTexImage is called before the next + // buffer is acquired, then this image is bound. + std::unique_ptr<renderengine::Image> mCurrentTextureImageFreed; + + // Cached images used for rendering the current texture through GPU + // composition. + std::unique_ptr<renderengine::Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS]; + // A release that is pending on the receipt of a new release fence from // presentDisplay PendingRelease mPendingRelease; diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp new file mode 100644 index 0000000000..3341b98b67 --- /dev/null +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -0,0 +1,502 @@ +/* + * 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 "BufferQueueLayer.h" +#include "LayerRejecter.h" + +#include "TimeStats/TimeStats.h" + +#include <system/window.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::getTransformToDisplayInverseLocked() const { + return mConsumer->getTransformToDisplayInverse(); +} + +void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { + if (!mConsumer->releasePendingBuffer()) { + return; + } + + auto releaseFenceTime = std::make_shared<FenceTime>(mConsumer->getPrevFinalReleaseFence()); + mReleaseTimeline.updateSignalTimes(); + mReleaseTimeline.push(releaseFenceTime); + + Mutex::Autolock lock(mFrameEventHistoryMutex); + if (mPreviousFrameNumber != 0) { + mFrameEventHistory.addRelease(mPreviousFrameNumber, dequeueReadyTime, + std::move(releaseFenceTime)); + } +} + +void BufferQueueLayer::setDefaultBufferSize(uint32_t w, uint32_t h) { + mConsumer->setDefaultBufferSize(w, h); +} + +int32_t BufferQueueLayer::getQueuedFrameCount() const { + return mQueuedFrames; +} + +bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const { + if (getSidebandStreamChanged() || getAutoRefresh()) { + return true; + } + + if (!hasFrameUpdate()) { + return false; + } + + Mutex::Autolock lock(mQueueItemLock); + + const int64_t addedTime = mQueueItems[0].mTimestamp; + + // Ignore timestamps more than a second in the future + const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1)); + ALOGW_IF(!isPlausible, + "[%s] Timestamp %" PRId64 " seems implausible " + "relative to expectedPresent %" PRId64, + mName.string(), addedTime, expectedPresentTime); + + const bool isDue = addedTime < expectedPresentTime; + return isDue || !isPlausible; +} + +// ----------------------------------------------------------------------- +// Interface implementation for BufferLayer +// ----------------------------------------------------------------------- + +bool BufferQueueLayer::fenceHasSignaled() const { + if (latchUnsignaledBuffers()) { + return true; + } + + if (!hasFrameUpdate()) { + return true; + } + + Mutex::Autolock lock(mQueueItemLock); + if (mQueueItems[0].mIsDroppable) { + // Even though this buffer's fence may not have signaled yet, it could + // be replaced by another buffer before it has a chance to, which means + // that it's possible to get into a situation where a buffer is never + // able to be latched. To avoid this, grab this buffer anyway. + return true; + } + return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; +} + +nsecs_t BufferQueueLayer::getDesiredPresentTime() { + return mConsumer->getTimestamp(); +} + +std::shared_ptr<FenceTime> BufferQueueLayer::getCurrentFenceTimeLocked() const { + return mConsumer->getCurrentFenceTime(); +} + +void BufferQueueLayer::getDrawingTransformMatrix(float *matrix) { + return mConsumer->getTransformMatrix(matrix); +} + +// NOTE: SurfaceFlinger's definitions of "Current" and "Drawing" do not neatly map to BufferQueue's +// These functions get the fields for the frame that is currently in SurfaceFlinger's Drawing state +// so the functions start with "getDrawing". The data is retrieved from the BufferQueueConsumer's +// current buffer so the consumer functions start with "getCurrent". +// +// This results in the rather confusing functions below. +uint32_t BufferQueueLayer::getDrawingTransform() const { + return mConsumer->getCurrentTransform(); +} + +ui::Dataspace BufferQueueLayer::getDrawingDataSpace() const { + return mConsumer->getCurrentDataSpace(); +} + +Rect BufferQueueLayer::getDrawingCrop() const { + return mConsumer->getCurrentCrop(); +} + +uint32_t BufferQueueLayer::getDrawingScalingMode() const { + return mConsumer->getCurrentScalingMode(); +} + +Region BufferQueueLayer::getDrawingSurfaceDamage() const { + return mConsumer->getSurfaceDamage(); +} + +const HdrMetadata& BufferQueueLayer::getDrawingHdrMetadata() const { + return mConsumer->getCurrentHdrMetadata(); +} + +int BufferQueueLayer::getDrawingApi() const { + return mConsumer->getCurrentApi(); +} + +PixelFormat BufferQueueLayer::getPixelFormat() const { + return mFormat; +} + +uint64_t BufferQueueLayer::getFrameNumber() const { + Mutex::Autolock lock(mQueueItemLock); + return mQueueItems[0].mFrameNumber; +} + +bool BufferQueueLayer::getAutoRefresh() const { + return mAutoRefresh; +} + +bool BufferQueueLayer::getSidebandStreamChanged() const { + return mSidebandStreamChanged; +} + +std::optional<Region> BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { + bool sidebandStreamChanged = true; + Mutex::Autolock lock(mStateMutex); + if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) { + // mSidebandStreamChanged was changed to false + // replicated in LayerBE until FE/BE is ready to be synchronized + getBE().compositionInfo.hwc.sidebandStream = mConsumer->getSidebandStream(); + if (getBE().compositionInfo.hwc.sidebandStream != nullptr) { + setTransactionFlags(eTransactionNeeded); + mFlinger->setTransactionFlags(eTraversalNeeded); + } + + recomputeVisibleRegions = true; + const State& s(getDrawingState()); + return getTransformLocked().transform(Region(Rect(s.active_legacy.w, s.active_legacy.h))); + } + return {}; +} + +bool BufferQueueLayer::hasFrameUpdateLocked() 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, + const sp<Fence>& releaseFence) { + // 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(); + status_t updateResult; + LayerRejecter r(mState.drawing, getCurrentState(), recomputeVisibleRegions, + getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode, + getTransformToDisplayInverseLocked(), mFreezeGeometryUpdates); + + const nsecs_t expectedPresentTime = mFlinger->mUseScheduler + ? mFlinger->mScheduler->mPrimaryDispSync->expectedPresentTime() + : mFlinger->mPrimaryDispSync->expectedPresentTime(); + + updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh, &queuedBuffer, + mLastFrameNumberReceived, releaseFence); + + 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); + getBE().compositionInfo.mBuffer = mActiveBuffer; + getBE().compositionInfo.mBufferSlot = 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(DisplayId displayId) { + auto& hwcInfo = getBE().mHwcLayers[displayId]; + auto& hwcLayer = hwcInfo.layer; + + uint32_t hwcSlot = 0; + sp<GraphicBuffer> hwcBuffer; + hwcInfo.bufferCache.getHwcBuffer(mActiveBufferSlot, 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(), + getBE().compositionInfo.mBuffer->handle, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + getBE().compositionInfo.mBufferSlot = mActiveBufferSlot; + getBE().compositionInfo.mBuffer = mActiveBuffer; + getBE().compositionInfo.hwc.fence = acquireFence; +} + +// ----------------------------------------------------------------------- +// Interface implementation for BufferLayerConsumer::ContentsChangedListener +// ----------------------------------------------------------------------- + +void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { + // Add this buffer from our internal queue tracker + { // Autolock scope + // Report the requested present time to the Scheduler. + if (mFlinger->mUseScheduler) { + mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp, + item.mIsAutoTimestamp, mName.c_str()); + } + + 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()) { + bool ignored = false; + latchBuffer(ignored, systemTime(), Fence::NO_FENCE); + usleep(16000); + releasePendingBuffer(systemTime()); + } else { + mFlinger->signalLayerUpdate(); + } +} + +void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { + { // Autolock scope + Mutex::Autolock lock(mQueueItemLock); + + // Ensure that callbacks are handled in order + while (item.mFrameNumber != mLastFrameNumberReceived + 1) { + status_t result = mQueueItemCondition.waitRelative(mQueueItemLock, ms2ns(500)); + if (result != NO_ERROR) { + ALOGE("[%s] Timed out waiting on callback", mName.string()); + } + } + + if (!hasFrameUpdate()) { + ALOGE("Can't replace a frame on an empty queue"); + return; + } + mQueueItems.editItemAt(mQueueItems.size() - 1) = item; + + // Wake up any pending callbacks + mLastFrameNumberReceived = item.mFrameNumber; + mQueueItemCondition.broadcast(); + } +} + +void BufferQueueLayer::onSidebandStreamChanged() { + bool sidebandStreamChanged = false; + if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, true)) { + // mSidebandStreamChanged was changed to true + mFlinger->signalLayerUpdate(); + } +} + +// ----------------------------------------------------------------------- + +void BufferQueueLayer::onFirstRef() { + BufferLayer::onFirstRef(); + + // Creates a custom BufferQueue for SurfaceFlingerConsumer to use + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer, true); + mProducer = new MonitoredProducer(producer, mFlinger, this); + { + // Grab the SF state lock during this since it's the only safe way to access RenderEngine + Mutex::Autolock lock(mFlinger->mStateLock); + mConsumer = + new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this); + } + mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); + mConsumer->setContentsChangedListener(this); + mConsumer->setName(mName); + + // BufferQueueCore::mMaxDequeuedBufferCount is default to 1 + if (!mFlinger->isLayerTripleBufferingDisabled()) { + mProducer->setMaxDequeuedBufferCount(2); + } + + if (const auto display = mFlinger->getDefaultDisplayDevice()) { + updateTransformHint(display); + } +} + +status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format) { + uint32_t const maxSurfaceDims = + std::min(mFlinger->getMaxTextureSize(), mFlinger->getMaxViewportDims()); + + // never allow a surface larger than what our underlying GL implementation + // can handle. + if ((uint32_t(w) > maxSurfaceDims) || (uint32_t(h) > maxSurfaceDims)) { + ALOGE("dimensions too large %u x %u", uint32_t(w), uint32_t(h)); + return BAD_VALUE; + } + + mFormat = format; + + setDefaultBufferSize(w, h); + mConsumer->setDefaultBufferFormat(format); + mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); + + return NO_ERROR; +} + +sp<IGraphicBufferProducer> BufferQueueLayer::getProducer() const { + return mProducer; +} + +uint32_t BufferQueueLayer::getProducerStickyTransform() const { + int producerStickyTransform = 0; + int ret = mProducer->query(NATIVE_WINDOW_STICKY_TRANSFORM, &producerStickyTransform); + if (ret != OK) { + ALOGW("%s: Error %s (%d) while querying window sticky transform.", __FUNCTION__, + strerror(-ret), ret); + return 0; + } + return static_cast<uint32_t>(producerStickyTransform); +} + +} // namespace android diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h new file mode 100644 index 0000000000..f9da044b83 --- /dev/null +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "BufferLayer.h" + +#include <utils/String8.h> + +namespace android { + +/* + * A new BufferQueue and a new BufferLayerConsumer are created when the + * BufferLayer is first referenced. + * + * This also implements onFrameAvailable(), which notifies SurfaceFlinger + * that new data has arrived. + */ +class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener { +public: + explicit BufferQueueLayer(const LayerCreationArgs&); + ~BufferQueueLayer() override; + + // ----------------------------------------------------------------------- + // Interface implementation for Layer + // ----------------------------------------------------------------------- +public: + void onLayerDisplayed(const sp<Fence>& releaseFence) override; + + void setTransformHint(uint32_t orientation) const override; + + std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override; + + bool getTransformToDisplayInverseLocked() const override REQUIRES(mStateMutex); + + // If a buffer was replaced this frame, release the former buffer + void releasePendingBuffer(nsecs_t dequeueReadyTime) override; + + void setDefaultBufferSize(uint32_t w, uint32_t h) override; + + int32_t getQueuedFrameCount() const override; + + bool shouldPresentNow(nsecs_t expectedPresentTime) const override; + // ----------------------------------------------------------------------- + + // ----------------------------------------------------------------------- + // Interface implementation for BufferLayer + // ----------------------------------------------------------------------- +public: + bool fenceHasSignaled() const override; + +private: + nsecs_t getDesiredPresentTime() override; + std::shared_ptr<FenceTime> getCurrentFenceTimeLocked() const override REQUIRES(mStateMutex); + + void getDrawingTransformMatrix(float *matrix) override; + uint32_t getDrawingTransform() const override REQUIRES(mStateMutex); + ui::Dataspace getDrawingDataSpace() const override REQUIRES(mStateMutex); + Rect getDrawingCrop() const override REQUIRES(mStateMutex); + 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; + + std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override + EXCLUDES(mStateMutex); + + bool hasFrameUpdateLocked() const override REQUIRES(mStateMutex); + + void setFilteringEnabled(bool enabled) override; + + status_t bindTextureImage() override; + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) override REQUIRES(mStateMutex); + + status_t updateActiveBuffer() override REQUIRES(mStateMutex); + status_t updateFrameNumber(nsecs_t latchTime) override; + + void setHwcLayerBuffer(DisplayId displayId) 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}; +}; + +} // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp new file mode 100644 index 0000000000..0e46928335 --- /dev/null +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -0,0 +1,648 @@ +/* + * 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 "BufferStateLayer.h" + +#include "TimeStats/TimeStats.h" + +#include <private/gui/SyncFeatures.h> +#include <renderengine/Image.h> + +#include <limits> + +namespace android { + +// clang-format off +const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +}; +// clang-format on + +BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args) { + mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; +} +BufferStateLayer::~BufferStateLayer() = default; + +// ----------------------------------------------------------------------- +// Interface implementation for Layer +// ----------------------------------------------------------------------- +void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { + // The transaction completed callback can only be sent if the release fence from the PREVIOUS + // frame has fired. In practice, we should never actually wait on the previous release fence + // but we should store it just in case. + mPreviousReleaseFence = releaseFence; +} + +void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const { + // TODO(marissaw): send the transform hint to buffer owner + return; +} + +void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { + return; +} + +bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { + if (getSidebandStreamChanged() || getAutoRefresh()) { + return true; + } + + return hasFrameUpdate(); +} + +bool BufferStateLayer::willPresentCurrentTransaction() const { + Mutex::Autolock lock(mStateMutex); + // Returns true if the most recent Transaction applied to CurrentState will be presented. + return getSidebandStreamChanged() || getAutoRefresh() || + (mState.current.modified && mState.current.buffer != nullptr); +} + +bool BufferStateLayer::getTransformToDisplayInverseLocked() const { + return mState.current.transformToDisplayInverse; +} + +void BufferStateLayer::pushPendingStateLocked() { + if (!mState.current.modified) { + return; + } + mState.pending.push_back(mState.current); + ATRACE_INT(mTransactionName.string(), mState.pending.size()); +} + +bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) { + const bool stateUpdateAvailable = !mState.pending.empty(); + while (!mState.pending.empty()) { + popPendingState(stateToCommit); + } + mCurrentStateModified = stateUpdateAvailable && mState.current.modified; + mState.current.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) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.transform == transform) return false; + mState.current.sequence++; + mState.current.transform = transform; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInverse) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.transformToDisplayInverse == transformToDisplayInverse) return false; + mState.current.sequence++; + mState.current.transformToDisplayInverse = transformToDisplayInverse; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setCrop(const Rect& crop) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.crop == crop) return false; + mState.current.sequence++; + mState.current.crop = crop; + mState.current.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(); + + Mutex::Autolock lock(mStateMutex); + if (mState.current.active.transform.tx() == x && mState.current.active.transform.ty() == y && + mState.current.active.w == w && mState.current.active.h == h) { + return false; + } + + if (!frame.isValid()) { + x = y = w = h = 0; + } + mState.current.active.transform.set(x, y); + mState.current.active.w = w; + mState.current.active.h = h; + + mState.current.sequence++; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.buffer) { + mReleasePreviousBuffer = true; + } + + mState.current.sequence++; + mState.current.buffer = buffer; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) { + Mutex::Autolock lock(mStateMutex); + // The acquire fences of BufferStateLayers have already signaled before they are set + mCallbackHandleAcquireTime = fence->getSignalTime(); + + mState.current.acquireFence = fence; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.dataspace == dataspace) return false; + mState.current.sequence++; + mState.current.dataspace = dataspace; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setHdrMetadata(const HdrMetadata& hdrMetadata) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.hdrMetadata == hdrMetadata) return false; + mState.current.sequence++; + mState.current.hdrMetadata = hdrMetadata; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setSurfaceDamageRegion(const Region& surfaceDamage) { + Mutex::Autolock lock(mStateMutex); + mState.current.sequence++; + mState.current.surfaceDamageRegion = surfaceDamage; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setApi(int32_t api) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.api == api) return false; + mState.current.sequence++; + mState.current.api = api; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool BufferStateLayer::setSidebandStream(const sp<NativeHandle>& sidebandStream) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.sidebandStream == sidebandStream) return false; + mState.current.sequence++; + mState.current.sidebandStream = sidebandStream; + mState.current.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().registerPendingLatchedCallbackHandle(handle); + + // Store so latched time and release fence can be set + { + Mutex::Autolock lock(mStateMutex); + mState.current.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().addUnlatchedCallbackHandle(handle); + } + } + + mReleasePreviousBuffer = false; + mCallbackHandleAcquireTime = -1; + + return willPresent; +} + +bool BufferStateLayer::setTransparentRegionHint(const Region& transparent) { + Mutex::Autolock lock(mStateMutex); + mState.current.transparentRegionHint = transparent; + mState.current.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->computeBounds(Region())); + if (!parentBounds.isEmpty()) { + return parentBounds; + } + } + + // if there is no parent layer, use the buffer's bounds as the buffer size + if (s.buffer) { + return s.buffer->getBounds(); + } + return Rect::INVALID_RECT; +} +// ----------------------------------------------------------------------- + +// ----------------------------------------------------------------------- +// Interface implementation for BufferLayer +// ----------------------------------------------------------------------- +bool BufferStateLayer::fenceHasSignaled() const { + if (latchUnsignaledBuffers()) { + return true; + } + + Mutex::Autolock lock(mStateMutex); + return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled; +} + +nsecs_t BufferStateLayer::getDesiredPresentTime() { + // TODO(marissaw): support an equivalent to desiredPresentTime for timestats metrics + return 0; +} + +std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTimeLocked() 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 { + Mutex::Autolock lock(mStateMutex); + return getDrawingState().surfaceDamageRegion; +} + +const HdrMetadata& BufferStateLayer::getDrawingHdrMetadata() const { + Mutex::Autolock lock(mStateMutex); + return getDrawingState().hdrMetadata; +} + +int BufferStateLayer::getDrawingApi() const { + Mutex::Autolock lock(mStateMutex); + 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(); +} + +std::optional<Region> BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) { + Mutex::Autolock lock(mStateMutex); + if (mSidebandStreamChanged.exchange(false)) { + const State& s(getDrawingState()); + // mSidebandStreamChanged was true + // replicated in LayerBE until FE/BE is ready to be synchronized + getBE().compositionInfo.hwc.sidebandStream = s.sidebandStream; + if (getBE().compositionInfo.hwc.sidebandStream != nullptr) { + setTransactionFlags(eTransactionNeeded); + mFlinger->setTransactionFlags(eTraversalNeeded); + } + recomputeVisibleRegions = true; + + return getTransformLocked().transform(Region(Rect(s.active.w, s.active.h))); + } + return {}; +} + +bool BufferStateLayer::hasFrameUpdateLocked() const { + return mCurrentStateModified && getCurrentState().buffer != nullptr; +} + +void BufferStateLayer::setFilteringEnabled(bool enabled) { + GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mActiveBuffer, mCurrentCrop, + mCurrentTransform, enabled); +} + +status_t BufferStateLayer::bindTextureImage() { + Mutex::Autolock lock(mStateMutex); + return bindTextureImageLocked(); +} +status_t BufferStateLayer::bindTextureImageLocked() { + const State& s(getDrawingState()); + auto& engine(mFlinger->getRenderEngine()); + + engine.checkErrors(); + + // TODO(marissaw): once buffers are cached, don't create a new image everytime + mTextureImage = engine.createImage(); + + bool created = + mTextureImage->setNativeWindowBuffer(s.buffer->getNativeBuffer(), + s.buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + if (!created) { + ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + s.buffer->getWidth(), s.buffer->getHeight(), s.buffer->getStride(), + s.buffer->getUsage(), s.buffer->getPixelFormat()); + engine.bindExternalTextureImage(mTextureName, *engine.createImage()); + return NO_INIT; + } + + engine.bindExternalTextureImage(mTextureName, *mTextureImage); + + // Wait for the new buffer to be ready. + if (s.acquireFence->isValid()) { + if (SyncFeatures::getInstance().useWaitSync()) { + base::unique_fd fenceFd(s.acquireFence->dup()); + if (fenceFd == -1) { + ALOGE("error dup'ing fence fd: %d", errno); + return -errno; + } + if (!engine.waitFence(std::move(fenceFd))) { + ALOGE("failed to wait on fence fd"); + return UNKNOWN_ERROR; + } + } else { + status_t err = s.acquireFence->waitForever("BufferStateLayer::bindTextureImage"); + if (err != NO_ERROR) { + ALOGE("error waiting for fence: %d", err); + return err; + } + } + } + + return NO_ERROR; +} + +status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime, + const sp<Fence>& releaseFence) { + const State& s(getDrawingState()); + + if (!s.buffer) { + 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; + } + + mFlinger->getTransactionCompletedThread() + .addLatchedCallbackHandles(getDrawingState().callbackHandles, latchTime, + mPreviousReleaseFence); + + // Handle sync fences + if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) { + // TODO(alecmouri): Fail somewhere upstream if the fence is invalid. + if (!releaseFence->isValid()) { + mFlinger->mTimeStats->onDestroy(layerID); + return UNKNOWN_ERROR; + } + + // Check status of fences first because merging is expensive. + // Merging an invalid fence with any other fence results in an + // invalid fence. + auto currentStatus = s.acquireFence->getStatus(); + if (currentStatus == Fence::Status::Invalid) { + ALOGE("Existing fence has invalid state"); + mFlinger->mTimeStats->onDestroy(layerID); + return BAD_VALUE; + } + + auto incomingStatus = releaseFence->getStatus(); + if (incomingStatus == Fence::Status::Invalid) { + ALOGE("New fence has invalid state"); + mState.drawing.acquireFence = releaseFence; + mFlinger->mTimeStats->onDestroy(layerID); + return BAD_VALUE; + } + + // If both fences are signaled or both are unsignaled, we need to merge + // them to get an accurate timestamp. + if (currentStatus == incomingStatus) { + char fenceName[32] = {}; + snprintf(fenceName, 32, "%.28s:%d", mName.string(), mFrameNumber); + sp<Fence> mergedFence = + Fence::merge(fenceName, mState.drawing.acquireFence, releaseFence); + if (!mergedFence.get()) { + ALOGE("failed to merge release fences"); + // synchronization is broken, the best we can do is hope fences + // signal in order so the new fence will act like a union + mState.drawing.acquireFence = releaseFence; + mFlinger->mTimeStats->onDestroy(layerID); + return BAD_VALUE; + } + mState.drawing.acquireFence = mergedFence; + } else if (incomingStatus == Fence::Status::Unsignaled) { + // If one fence has signaled and the other hasn't, the unsignaled + // fence will approximately correspond with the correct timestamp. + // There's a small race if both fences signal at about the same time + // and their statuses are retrieved with unfortunate timing. However, + // by this point, they will have both signaled and only the timestamp + // will be slightly off; any dependencies after this point will + // already have been met. + mState.drawing.acquireFence = releaseFence; + } + } else { + // 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 = bindTextureImageLocked(); + if (err != NO_ERROR) { + mFlinger->mTimeStats->onDestroy(layerID); + return BAD_VALUE; + } + } + + // TODO(marissaw): properly support mTimeStats + mFlinger->mTimeStats->setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime); + mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTimeLocked()); + mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime); + + return NO_ERROR; +} + +status_t BufferStateLayer::updateActiveBuffer() { + const State& s(getDrawingState()); + + if (s.buffer == nullptr) { + return BAD_VALUE; + } + + mActiveBuffer = s.buffer; + getBE().compositionInfo.mBuffer = mActiveBuffer; + getBE().compositionInfo.mBufferSlot = 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(DisplayId displayId) { + Mutex::Autolock lock(mStateMutex); + auto& hwcInfo = getBE().mHwcLayers[displayId]; + auto& hwcLayer = hwcInfo.layer; + + const State& s(getDrawingState()); + + // TODO(marissaw): support more than one slot + uint32_t hwcSlot = 0; + + auto error = hwcLayer->setBuffer(hwcSlot, s.buffer, s.acquireFence); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), + s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error)); + } + + mCurrentStateModified = false; + mFrameNumber++; +} + +void BufferStateLayer::onFirstRef() { + BufferLayer::onFirstRef(); + + if (const auto display = mFlinger->getDefaultDisplayDevice()) { + updateTransformHint(display); + } +} + +} // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h new file mode 100644 index 0000000000..655353cf3d --- /dev/null +++ b/services/surfaceflinger/BufferStateLayer.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "BufferLayer.h" +#include "Layer.h" + +#include <gui/GLConsumer.h> +#include <renderengine/Image.h> +#include <renderengine/RenderEngine.h> +#include <system/window.h> +#include <utils/String8.h> + +namespace android { + +class BufferStateLayer : public BufferLayer { +public: + explicit BufferStateLayer(const LayerCreationArgs&); + ~BufferStateLayer() override; + + // ----------------------------------------------------------------------- + // Interface implementation for Layer + // ----------------------------------------------------------------------- + void onLayerDisplayed(const sp<Fence>& releaseFence) override; + void setTransformHint(uint32_t orientation) const override; + void releasePendingBuffer(nsecs_t dequeueReadyTime) override; + + bool shouldPresentNow(nsecs_t expectedPresentTime) const override; + + bool getTransformToDisplayInverseLocked() const override REQUIRES(mStateMutex); + + uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override { + return flags; + } + + void pushPendingStateLocked() override REQUIRES(mStateMutex); + bool applyPendingStates(Layer::State* stateToCommit) override REQUIRES(mStateMutex); + + 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 EXCLUDES(mStateMutex); + bool setTransformToDisplayInverse(bool transformToDisplayInverse) override + EXCLUDES(mStateMutex); + bool setCrop(const Rect& crop) override EXCLUDES(mStateMutex); + bool setFrame(const Rect& frame) override EXCLUDES(mStateMutex); + bool setBuffer(const sp<GraphicBuffer>& buffer) override EXCLUDES(mStateMutex); + bool setAcquireFence(const sp<Fence>& fence) override EXCLUDES(mStateMutex); + bool setDataspace(ui::Dataspace dataspace) override EXCLUDES(mStateMutex); + bool setHdrMetadata(const HdrMetadata& hdrMetadata) override EXCLUDES(mStateMutex); + bool setSurfaceDamageRegion(const Region& surfaceDamage) override EXCLUDES(mStateMutex); + bool setApi(int32_t api) override EXCLUDES(mStateMutex); + bool setSidebandStream(const sp<NativeHandle>& sidebandStream) override EXCLUDES(mStateMutex); + bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& handles) override + EXCLUDES(mStateMutex); + + // 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 REQUIRES(mStateMutex); + // ----------------------------------------------------------------------- + + // ----------------------------------------------------------------------- + // Interface implementation for BufferLayer + // ----------------------------------------------------------------------- + bool fenceHasSignaled() const override EXCLUDES(mStateMutex); + +private: + nsecs_t getDesiredPresentTime() override; + std::shared_ptr<FenceTime> getCurrentFenceTimeLocked() const override REQUIRES(mStateMutex); + + void getDrawingTransformMatrix(float *matrix) override; + uint32_t getDrawingTransform() const override REQUIRES(mStateMutex); + ui::Dataspace getDrawingDataSpace() const override REQUIRES(mStateMutex); + Rect getDrawingCrop() const override REQUIRES(mStateMutex); + uint32_t getDrawingScalingMode() const override; + Region getDrawingSurfaceDamage() const override EXCLUDES(mStateMutex); + const HdrMetadata& getDrawingHdrMetadata() const override EXCLUDES(mStateMutex); + int getDrawingApi() const override EXCLUDES(mStateMutex); + PixelFormat getPixelFormat() const override; + + uint64_t getFrameNumber() const override; + + bool getAutoRefresh() const override; + bool getSidebandStreamChanged() const override; + + std::optional<Region> latchSidebandStream(bool& recomputeVisibleRegions) override + EXCLUDES(mStateMutex); + + bool hasFrameUpdateLocked() const override REQUIRES(mStateMutex); + + void setFilteringEnabled(bool enabled) override; + + status_t bindTextureImage() override EXCLUDES(mStateMutex); + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + const sp<Fence>& releaseFence) override REQUIRES(mStateMutex); + + status_t updateActiveBuffer() override REQUIRES(mStateMutex); + status_t updateFrameNumber(nsecs_t latchTime) override; + + void setHwcLayerBuffer(DisplayId displayId) override EXCLUDES(mStateMutex); + +private: + void onFirstRef() override; + bool willPresentCurrentTransaction() const; + status_t bindTextureImageLocked() REQUIRES(mStateMutex); + + 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; + + // TODO(marissaw): support sticky transform for LEGACY camera mode +}; + +} // namespace android diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp index ff957c0ee7..cb7642e60f 100644 --- a/services/surfaceflinger/ColorLayer.cpp +++ b/services/surfaceflinger/ColorLayer.cpp @@ -22,54 +22,58 @@ #include <stdlib.h> #include <sys/types.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) {} + +ColorLayer::~ColorLayer() = default; void ColorLayer::onDraw(const RenderArea& renderArea, const Region& /* clip */, - bool useIdentityTransform) const { + bool useIdentityTransform) { + Mutex::Autolock lock(mStateMutex); half4 color = getColor(); if (color.a > 0) { - Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2); + renderengine::Mesh mesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2); computeGeometry(renderArea, mesh, useIdentityTransform); auto& engine(mFlinger->getRenderEngine()); + + Rect win{computeBoundsLocked()}; + + const auto roundedCornerState = getRoundedCornerStateLocked(); + const auto cropRect = roundedCornerState.cropRect; + setupRoundedCornersCropCoordinates(win, cropRect); + engine.setupLayerBlending(getPremultipledAlpha(), false /* opaque */, - true /* disableTexture */, color); + true /* disableTexture */, color, roundedCornerState.radius); + + engine.setSourceDataSpace(mCurrentDataSpace); + engine.setupCornerRadiusCropSize(cropRect.getWidth(), cropRect.getHeight()); engine.drawMesh(mesh); engine.disableBlending(); } } 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; - } - auto& hwcInfo = getBE().mHwcLayers[hwcId]; +void ColorLayer::setPerFrameData(DisplayId displayId, const ui::Transform& transform, + const Rect& viewport, int32_t /* supportedPerFrameMetadata */) { + RETURN_IF_NO_HWC_LAYER(displayId); + + Region visible = transform.transform(visibleRegion.intersect(viewport)); + + auto& hwcInfo = getBE().mHwcLayers[displayId]; auto& hwcLayer = hwcInfo.layer; auto error = hwcLayer->setVisibleRegion(visible); if (error != HWC2::Error::None) { @@ -77,15 +81,18 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { to_string(error).c_str(), static_cast<int32_t>(error)); visible.dump(LOG_TAG); } + getBE().compositionInfo.hwc.visibleRegion = visible; - setCompositionType(hwcId, HWC2::Composition::SolidColor); + setCompositionType(displayId, HWC2::Composition::SolidColor); 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)); } + getBE().compositionInfo.hwc.dataspace = mCurrentDataSpace; + Mutex::Autolock lock(mStateMutex); 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 +101,9 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(), static_cast<int32_t>(error)); } + getBE().compositionInfo.hwc.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 +111,22 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(), static_cast<int32_t>(error)); } + getBE().compositionInfo.hwc.transform = HWC2::Transform::None; + + error = hwcLayer->setColorTransform(getColorTransformLocked()); + if (error != HWC2::Error::None) { + ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + getBE().compositionInfo.hwc.colorTransform = getColorTransformLocked(); + + 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); + } + getBE().compositionInfo.hwc.surfaceDamage = surfaceDamageRegion; } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h index 0cde398ce9..5850a2e699 100644 --- a/services/surfaceflinger/ColorLayer.h +++ b/services/surfaceflinger/ColorLayer.h @@ -25,16 +25,21 @@ namespace android { 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; virtual const char* getTypeId() const { return "ColorLayer"; } - virtual void onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const; - bool isVisible() const override; + virtual void onDraw(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform) + EXCLUDES(mStateMutex); + bool isVisible() const override EXCLUDES(mStateMutex); - void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override; + void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport, + int32_t supportedPerFrameMetadata) override EXCLUDES(mStateMutex); + + bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; } + +protected: + FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; } }; } // namespace android diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h index d56b1c8d9b..b7d61ce08e 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 @@ public: : 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/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp index f259d93c3f..ca49f6c7af 100644 --- a/services/surfaceflinger/ContainerLayer.cpp +++ b/services/surfaceflinger/ContainerLayer.cpp @@ -22,18 +22,16 @@ 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; -void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) const {} +void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) {} bool ContainerLayer::isVisible() const { return !isHiddenByPolicy(); } -void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&) {} +void ContainerLayer::setPerFrameData(DisplayId, const ui::Transform&, const Rect&, int32_t) {} } // namespace android diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h index b352b96762..6c4f7c848d 100644 --- a/services/surfaceflinger/ContainerLayer.h +++ b/services/surfaceflinger/ContainerLayer.h @@ -25,18 +25,20 @@ namespace android { 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; + bool useIdentityTransform) override; + bool isVisible() const override EXCLUDES(mStateMutex); - void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override; + void setPerFrameData(DisplayId displayId, const ui::Transform& transform, const Rect& viewport, + int32_t supportedPerFrameMetadata) override; bool isCreatedFromMainThread() const override { return true; } + + bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; } }; } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 309fd0a791..e7b7fbe191 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -18,6 +18,8 @@ #undef LOG_TAG #define LOG_TAG "DisplayDevice" +#include "DisplayDevice.h" + #include <array> #include <unordered_set> @@ -26,37 +28,33 @@ #include <string.h> #include <math.h> +#include <android-base/stringprintf.h> +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <configstore/Utils.h> #include <cutils/properties.h> - -#include <utils/RefBase.h> -#include <utils/Log.h> - +#include <gui/Surface.h> +#include <hardware/gralloc.h> +#include <renderengine/RenderEngine.h> +#include <sync/sync.h> +#include <system/window.h> #include <ui/DebugUtils.h> #include <ui/DisplayInfo.h> #include <ui/PixelFormat.h> - -#include <gui/Surface.h> - -#include <hardware/gralloc.h> +#include <utils/Log.h> +#include <utils/RefBase.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 <configstore/Utils.h> - namespace android { // retrieve triple buffer setting from configstore using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; +using android::base::StringAppendF; using android::ui::ColorMode; using android::ui::Dataspace; using android::ui::Hdr; @@ -72,7 +70,8 @@ uint32_t DisplayDevice::sPrimaryDisplayOrientation = 0; namespace { // ordered list of known SDR color modes -const std::array<ColorMode, 2> sSdrColorModes = { +const std::array<ColorMode, 3> sSdrColorModes = { + ColorMode::DISPLAY_BT2020, ColorMode::DISPLAY_P3, ColorMode::SRGB, }; @@ -99,9 +98,11 @@ const std::array<RenderIntent, 2> sHdrRenderIntents = { Dataspace colorModeToDataspace(ColorMode mode) { switch (mode) { case ColorMode::SRGB: - return Dataspace::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: @@ -212,54 +213,49 @@ RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, Ren } // 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, - 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), - 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(); +DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger, + const wp<IBinder>& displayToken, + const std::optional<DisplayId>& displayId) + : flinger(flinger), displayToken(displayToken), displayId(displayId) {} + +DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args) + : lastCompositionHadVisibleLayers(false), + mFlinger(args.flinger), + mDisplayToken(args.displayToken), + mSequenceId(args.sequenceId), + mId(args.displayId), + mNativeWindow(args.nativeWindow), + mGraphicBuffer(nullptr), + mDisplaySurface(args.displaySurface), + mDisplayInstallOrientation(args.displayInstallOrientation), + mPageFlipCount(0), + mIsVirtual(args.isVirtual), + mIsSecure(args.isSecure), + mLayerStack(NO_LAYER_STACK), + mOrientation(), + mViewport(Rect::INVALID_RECT), + mFrame(Rect::INVALID_RECT), + mPowerMode(args.initialPowerMode), + mActiveConfig(0), + mColorTransform(HAL_COLOR_TRANSFORM_IDENTITY), + mHasWideColorGamut(args.hasWideColorGamut), + mHasHdr10Plus(false), + mHasHdr10(false), + mHasHLG(false), + mHasDolbyVision(false), + mSupportedPerFrameMetadata(args.supportedPerFrameMetadata), + mIsPrimary(args.isPrimary) { + populateColorModes(args.hwcColorModes); + + ALOGE_IF(!mNativeWindow, "No native window was set for display"); + ALOGE_IF(!mDisplaySurface, "No display surface was set for display"); + + std::vector<Hdr> types = args.hdrCapabilities.getSupportedHdrTypes(); for (Hdr hdrType : types) { switch (hdrType) { + case Hdr::HDR10_PLUS: + mHasHdr10Plus = true; + break; case Hdr::HDR10: mHasHdr10 = true; break; @@ -274,9 +270,9 @@ DisplayDevice::DisplayDevice( } } - float minLuminance = hdrCapabilities.getDesiredMinLuminance(); - float maxLuminance = hdrCapabilities.getDesiredMaxLuminance(); - float maxAverageLuminance = hdrCapabilities.getDesiredMaxAverageLuminance(); + 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; @@ -285,15 +281,27 @@ DisplayDevice::DisplayDevice( // insert HDR10/HLG as we will force client composition for HDR10/HLG // layers if (!hasHDR10Support()) { - types.push_back(Hdr::HDR10); + types.push_back(Hdr::HDR10); } if (!hasHLGSupport()) { - types.push_back(Hdr::HLG); + types.push_back(Hdr::HLG); } } mHdrCapabilities = HdrCapabilities(types, maxLuminance, maxAverageLuminance, minLuminance); + 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); + + mDisplayWidth = ANativeWindow_getWidth(window); + mDisplayHeight = ANativeWindow_getHeight(window); + // initialize the display orientation transform. setProjection(DisplayState::eOrientationDefault, mViewport, mFrame); } @@ -301,16 +309,12 @@ DisplayDevice::DisplayDevice( DisplayDevice::~DisplayDevice() = default; void DisplayDevice::disconnect(HWComposer& hwc) { - if (mHwcDisplayId >= 0) { - hwc.disconnectDisplay(mHwcDisplayId); - mHwcDisplayId = -1; + if (mId) { + hwc.disconnectDisplay(*mId); + mId.reset(); } } -bool DisplayDevice::isValid() const { - return mFlinger != nullptr; -} - int DisplayDevice::getWidth() const { return mDisplayWidth; } @@ -319,8 +323,8 @@ int DisplayDevice::getHeight() const { return mDisplayHeight; } -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; } @@ -332,7 +336,6 @@ uint32_t DisplayDevice::getPageFlipCount() const { void DisplayDevice::flip() const { - mFlinger->getRenderEngine().checkErrors(); mPageFlipCount++; } @@ -340,15 +343,18 @@ 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; +status_t DisplayDevice::prepareFrame(HWComposer& hwc, + std::vector<CompositionInfo>& compositionData) { + if (mId) { + status_t error = hwc.prepare(*mId, compositionData); + if (error != NO_ERROR) { + return error; + } } DisplaySurface::CompositionType compositionType; - bool hasClient = hwc.hasClientComposition(mHwcDisplayId); - bool hasDevice = hwc.hasDeviceComposition(mHwcDisplayId); + bool hasClient = hwc.hasClientComposition(mId); + bool hasDevice = hwc.hasDeviceComposition(mId); if (hasClient && hasDevice) { compositionType = DisplaySurface::COMPOSITION_MIXED; } else if (hasClient) { @@ -364,34 +370,106 @@ status_t DisplayDevice::prepareFrame(HWComposer& hwc) { return mDisplaySurface->prepareFrame(compositionType); } -void DisplayDevice::swapBuffers(HWComposer& hwc) const { - if (hwc.hasClientComposition(mHwcDisplayId) || hwc.hasFlipClientTargetRequest(mHwcDisplayId)) { - mSurface->swapBuffers(); +void DisplayDevice::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); +} + +sp<GraphicBuffer> DisplayDevice::dequeueBuffer() { + int fd; + ANativeWindowBuffer* buffer; + + status_t res = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fd); + + if (res != NO_ERROR) { + ALOGE("ANativeWindow::dequeueBuffer failed for display [%s] with error: %d", + getDisplayName().c_str(), res); + // 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); + + // Block until the buffer is ready + // TODO(alecmouri): it's perhaps more appropriate to block renderengine so + // that the gl driver can block instead. + if (fd >= 0) { + sync_wait(fd, -1); + close(fd); + } + + return mGraphicBuffer; +} + +void DisplayDevice::queueBuffer(HWComposer& hwc) { + if (hwc.hasClientComposition(mId) || hwc.hasFlipClientTargetRequest(mId)) { + // 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", + mDisplayName.c_str()); + // We shouldn't deadlock here, since mGraphicBuffer == nullptr only + // after a successful call to queueBuffer, or if dequeueBuffer has + // never been called. + dequeueBuffer(); + } + + if (mGraphicBuffer == nullptr) { + ALOGE("No buffer is ready for display [%s]", mDisplayName.c_str()); + } else { + status_t res = mNativeWindow->queueBuffer(mNativeWindow.get(), + mGraphicBuffer->getNativeBuffer(), + dup(mBufferReady)); + if (res != NO_ERROR) { + ALOGE("Error when queueing buffer for display [%s]: %d", mDisplayName.c_str(), res); + // We risk blocking on dequeueBuffer if the primary display failed + // to queue up its buffer, so crash here. + if (isPrimary()) { + LOG_ALWAYS_FATAL("ANativeWindow::queueBuffer failed with error: %d", res); + } else { + mNativeWindow->cancelBuffer(mNativeWindow.get(), + mGraphicBuffer->getNativeBuffer(), + dup(mBufferReady)); + } + } + + mBufferReady.reset(); + mGraphicBuffer = nullptr; + } } status_t result = mDisplaySurface->advanceFrame(); if (result != NO_ERROR) { - ALOGE("[%s] failed pushing new frame to HWC: %d", - mDisplayName.string(), result); + ALOGE("[%s] failed pushing new frame to HWC: %d", mDisplayName.c_str(), result); } } -void DisplayDevice::onSwapBuffersCompleted() const { +void DisplayDevice::onPresentDisplayCompleted() { 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); + mFlinger->getRenderEngine().setViewportAndProjection(w, h, sourceCrop, ui::Transform::ROT_0); +} + +void DisplayDevice::finishBuffer() { + mBufferReady = mFlinger->getRenderEngine().flush(); + if (mBufferReady.get() < 0) { + mFlinger->getRenderEngine().finish(); + } } const sp<Fence>& DisplayDevice::getClientTargetAcquireFence() const { @@ -421,7 +499,7 @@ Region DisplayDevice::getDirtyRegion(bool repaintEverything) const { if (repaintEverything) { dirty.set(getBounds()); } else { - const Transform& planeTransform(mGlobalTransform); + const ui::Transform& planeTransform(mGlobalTransform); dirty = planeTransform.transform(this->dirtyRegion); dirty.andSelf(getBounds()); } @@ -437,8 +515,8 @@ 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; } // ---------------------------------------------------------------------------- @@ -500,37 +578,37 @@ uint32_t DisplayDevice::getOrientationTransform() const { uint32_t transform = 0; switch (mOrientation) { case DisplayState::eOrientationDefault: - transform = Transform::ROT_0; + transform = ui::Transform::ROT_0; break; case DisplayState::eOrientation90: - transform = Transform::ROT_90; + transform = ui::Transform::ROT_90; break; case DisplayState::eOrientation180: - transform = Transform::ROT_180; + transform = ui::Transform::ROT_180; break; case DisplayState::eOrientation270: - transform = Transform::ROT_270; + transform = ui::Transform::ROT_270; break; } return transform; } status_t DisplayDevice::orientationToTransfrom( - int orientation, int w, int h, Transform* tr) + int orientation, int w, int h, ui::Transform* tr) { uint32_t flags = 0; switch (orientation) { case DisplayState::eOrientationDefault: - flags = Transform::ROT_0; + flags = ui::Transform::ROT_0; break; 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: return BAD_VALUE; @@ -542,19 +620,10 @@ status_t DisplayDevice::orientationToTransfrom( 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); + mDisplayWidth = newWidth; + mDisplayHeight = newHeight; } void DisplayDevice::setProjection(int orientation, @@ -565,7 +634,7 @@ void DisplayDevice::setProjection(int orientation, const int w = mDisplayWidth; const int h = mDisplayHeight; - Transform R; + ui::Transform R; DisplayDevice::orientationToTransfrom(orientation, w, h, &R); if (!frame.isValid()) { @@ -580,16 +649,16 @@ void DisplayDevice::setProjection(int orientation, // 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,10 +678,9 @@ void DisplayDevice::setProjection(int orientation, // need to take care of primary display rotation for mGlobalTransform // for case if the panel is not installed aligned with device orientation - if (mType == DisplayType::DISPLAY_PRIMARY) { - int primaryDisplayOrientation = mFlinger->getPrimaryDisplayOrientation(); + if (isPrimary()) { DisplayDevice::orientationToTransfrom( - (orientation + primaryDisplayOrientation) % (DisplayState::eOrientation270 + 1), + (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1), w, h, &R); } @@ -623,7 +691,7 @@ void DisplayDevice::setProjection(int orientation, const uint8_t type = mGlobalTransform.getType(); mNeedsFiltering = (!mGlobalTransform.preserveRects() || - (type >= Transform::SCALE)); + (type >= ui::Transform::SCALE)); mScissor = mGlobalTransform.transform(viewport); if (mScissor.isEmpty()) { @@ -631,20 +699,20 @@ void DisplayDevice::setProjection(int orientation, } mOrientation = orientation; - if (mType == DisplayType::DISPLAY_PRIMARY) { + if (isPrimary()) { uint32_t transform = 0; switch (mOrientation) { case DisplayState::eOrientationDefault: - transform = Transform::ROT_0; + transform = ui::Transform::ROT_0; break; case DisplayState::eOrientation90: - transform = Transform::ROT_90; + transform = ui::Transform::ROT_90; break; case DisplayState::eOrientation180: - transform = Transform::ROT_180; + transform = ui::Transform::ROT_180; break; case DisplayState::eOrientation270: - transform = Transform::ROT_270; + transform = ui::Transform::ROT_270; break; } sPrimaryDisplayOrientation = transform; @@ -657,34 +725,43 @@ uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() { return sPrimaryDisplayOrientation; } -void DisplayDevice::dump(String8& result) const { - const Transform& tr(mGlobalTransform); +std::string DisplayDevice::getDebugName() const { + const auto id = mId ? to_string(*mId) + ", " : std::string(); + return base::StringPrintf("DisplayDevice{%s%s%s\"%s\"}", id.c_str(), + isPrimary() ? "primary, " : "", isVirtual() ? "virtual, " : "", + mDisplayName.c_str()); +} + +void DisplayDevice::dump(std::string& result) const { + const ui::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]); + StringAppendF(&result, "+ %s\n", getDebugName().c_str()); + StringAppendF(&result, + " layerStack=%u, (%4dx%4d), ANativeWindow=%p " + "format=%d, orient=%2d (type=%08x), flips=%u, isSecure=%d, " + "powerMode=%d, activeConfig=%d, numLayers=%zu\n", + mLayerStack, mDisplayWidth, mDisplayHeight, window, + ANativeWindow_getFormat(window), mOrientation, tr.getType(), getPageFlipCount(), + mIsSecure, mPowerMode, mActiveConfig, mVisibleLayersSortedByZ.size()); + StringAppendF(&result, + " 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); + StringAppendF(&result, + " wideColorGamut=%d, hdr10plus =%d, hdr10=%d, colorMode=%s, dataspace: %s " + "(%d)\n", + mHasWideColorGamut, mHasHdr10Plus, mHasHdr10, + decodeColorMode(mActiveColorMode).c_str(), + dataspaceDetails(static_cast<android_dataspace>(dataspace)).c_str(), dataspace); String8 surfaceDump; mDisplaySurface->dumpAsString(surfaceDump); - result.append(surfaceDump); + result.append(surfaceDump.string(), surfaceDump.size()); } // Map dataspace/intent to the best matched dataspace/colorMode/renderIntent @@ -704,7 +781,7 @@ void DisplayDevice::addColorMode( const Dataspace dataspace = colorModeToDataspace(mode); const Dataspace hwcDataspace = colorModeToDataspace(hwcColorMode); - ALOGV("DisplayDevice %d/%d: map (%s, %s) to (%s, %s, %s)", mType, mHwcDisplayId, + ALOGV("%s: map (%s, %s) to (%s, %s, %s)", getDebugName().c_str(), dataspaceDetails(static_cast<android_dataspace_t>(dataspace)).c_str(), decodeRenderIntent(intent).c_str(), dataspaceDetails(static_cast<android_dataspace_t>(hwcDataspace)).c_str(), @@ -757,7 +834,7 @@ void DisplayDevice::populateColorModes( 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)); + auto iter = mColorModes.find(getColorModeKey(Dataspace::V0_SRGB, intent)); return iter != mColorModes.end() && iter->second.renderIntent == intent; } @@ -794,18 +871,6 @@ void DisplayDevice::getBestColorMode(Dataspace dataspace, RenderIntent intent, } } -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(); -} +std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1); } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 6c3bd91793..250a65026f 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -17,40 +17,43 @@ #ifndef ANDROID_DISPLAY_DEVICE_H #define ANDROID_DISPLAY_DEVICE_H -#include "Transform.h" - #include <stdlib.h> -#include <unordered_map> -#include <math/mat4.h> +#include <memory> +#include <optional> +#include <string> +#include <unordered_map> +#include <android/native_window.h> #include <binder/IBinder.h> -#include <gui/ISurfaceComposer.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; + +struct CompositionInfo; +struct DisplayDeviceCreationArgs; +struct DisplayInfo; class DisplayDevice : public LightRefBase<DisplayDevice> { @@ -64,42 +67,15 @@ public: 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, - 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); ~DisplayDevice(); - // 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. @@ -111,6 +87,7 @@ public: int getWidth() const; int getHeight() const; + int getInstallOrientation() const { return mDisplayInstallOrientation; } void setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers); const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const; @@ -125,27 +102,28 @@ public: int getOrientation() const { return mOrientation; } uint32_t getOrientationTransform() const; static uint32_t getPrimaryDisplayOrientationTransform(); - const Transform& getTransform() const { return mGlobalTransform; } + const ui::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; } 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 { return mId; } + const wp<IBinder>& getDisplayToken() const { return mDisplayToken; } + int32_t getSequenceId() const { return mSequenceId; } int32_t getSupportedPerFrameMetadata() const { return mSupportedPerFrameMetadata; } // 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); + status_t prepareFrame(HWComposer& hwc, std::vector<CompositionInfo>& compositionInfo); bool hasWideColorGamut() const { return mHasWideColorGamut; } // Whether h/w composer has native support for specific HDR type. + bool hasHDR10PlusSupport() const { return mHasHdr10Plus; } bool hasHDR10Support() const { return mHasHdr10; } bool hasHLGSupport() const { return mHasHLG; } bool hasDolbyVisionSupport() const { return mHasDolbyVision; } @@ -169,20 +147,28 @@ public: ui::Dataspace* outDataspace, ui::ColorMode* outMode, ui::RenderIntent* outIntent) const; - void swapBuffers(HWComposer& hwc) const; + void setProtected(bool useProtected); + // Queues the drawn buffer for consumption by HWC. + void queueBuffer(HWComposer& hwc); + // Allocates a buffer as scratch space for GPU composition + sp<GraphicBuffer> dequeueBuffer(); // called after h/w composer has completed its set() call - void onSwapBuffersCompleted() const; + void onPresentDisplayCompleted(); Rect getBounds() const { return Rect(mDisplayWidth, mDisplayHeight); } inline Rect bounds() const { return getBounds(); } - void setDisplayName(const String8& displayName); - const String8& getDisplayName() const { return mDisplayName; } + void setDisplayName(const std::string& displayName); + const std::string& getDisplayName() const { return mDisplayName; } - bool makeCurrent() const; + // Acquires a new buffer for GPU composition. + void readyNewBuffer(); + // Marks the current buffer has finished, so that it can be presented and + // swapped out. + void finishBuffer(); void setViewportAndProjection() const; const sp<Fence>& getClientTargetAcquireFence() const; @@ -192,7 +178,7 @@ public: */ int getPowerMode() const; void setPowerMode(int mode); - bool isDisplayOn() const; + bool isPoweredOn() const; ui::ColorMode getActiveColorMode() const; void setActiveColorMode(ui::ColorMode mode); @@ -216,27 +202,33 @@ public: * 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; + + std::optional<DisplayId> mId; // ANativeWindow this display is rendering into sp<ANativeWindow> mNativeWindow; + // Current buffer that this display can render to. + sp<GraphicBuffer> mGraphicBuffer; sp<DisplaySurface> mDisplaySurface; + // File descriptor indicating that mGraphicBuffer is ready for display, i.e. + // that drawing to the buffer is now complete. + base::unique_fd mBufferReady; - 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; + const bool mIsSecure; /* * Can only accessed from the main thread, these members @@ -252,7 +244,7 @@ private: * Transaction state */ static status_t orientationToTransfrom(int orientation, - int w, int h, Transform* tr); + int w, int h, ui::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 @@ -267,7 +259,7 @@ private: Rect mFrame; // pre-computed scissor to apply to the display Rect mScissor; - Transform mGlobalTransform; + ui::Transform mGlobalTransform; bool mNeedsFiltering; // Current power mode int mPowerMode; @@ -285,6 +277,7 @@ private: // Initialized by SurfaceFlinger when the DisplayDevice is created. // Fed to RenderEngine during composition. bool mHasWideColorGamut; + bool mHasHdr10Plus; bool mHasHdr10; bool mHasHLG; bool mHasDolbyVision; @@ -310,19 +303,16 @@ private: 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; @@ -330,30 +320,143 @@ struct DisplayDeviceState { 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<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, - ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone) - : DisplayRenderArea(device, device->getBounds(), device->getHeight(), device->getWidth(), - rotation) {} - DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqHeight, - uint32_t reqWidth, ISurfaceComposer::Rotation rotation) - : RenderArea(reqHeight, reqWidth, CaptureFill::OPAQUE, rotation), mDevice(device), - mSourceCrop(sourceCrop) {} - - const Transform& getTransform() const override { return mDevice->getTransform(); } + ui::Transform::orientation_flags rotation = ui::Transform::ROT_0) + : DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(), + device->getCompositionDataSpace(), rotation) {} + DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth, + uint32_t reqHeight, ui::Dataspace reqDataSpace, + ui::Transform::orientation_flags rotation) + : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace, + getDisplayRotation(rotation, device->getInstallOrientation())), + mDevice(device), + mSourceCrop(sourceCrop) {} + + const ui::Transform& getTransform() const override { return mDevice->getTransform(); } Rect getBounds() const override { return mDevice->getBounds(); } int getHeight() const override { return mDevice->getHeight(); } int getWidth() const override { return mDevice->getWidth(); } bool isSecure() const override { return mDevice->isSecure(); } - bool needsFiltering() const override { return mDevice->needsFiltering(); } - Rect getSourceCrop() const override { return mSourceCrop; } + + bool needsFiltering() const override { + // check if the projection from the logical display to the physical + // display needs filtering + if (mDevice->needsFiltering()) { + return true; + } + + // check if the projection from the logical render area (i.e., the + // physical display) to the physical render area requires filtering + const Rect sourceCrop = getSourceCrop(); + int width = sourceCrop.width(); + int height = sourceCrop.height(); + if (getRotationFlags() & ui::Transform::ROT_90) { + std::swap(width, height); + } + return width != getReqWidth() || height != getReqHeight(); + } + + Rect getSourceCrop() const override { + // use the (projected) logical display viewport by default + if (mSourceCrop.isEmpty()) { + return mDevice->getScissor(); + } + + const int orientation = mDevice->getInstallOrientation(); + if (orientation == DisplayState::eOrientationDefault) { + return mSourceCrop; + } + + // Install orientation is transparent to the callers. Apply it now. + uint32_t flags = 0x00; + switch (orientation) { + case DisplayState::eOrientation90: + flags = ui::Transform::ROT_90; + break; + case DisplayState::eOrientation180: + flags = ui::Transform::ROT_180; + break; + case DisplayState::eOrientation270: + flags = ui::Transform::ROT_270; + break; + } + ui::Transform tr; + tr.set(flags, getWidth(), getHeight()); + return tr.transform(mSourceCrop); + } private: + // Install orientation is transparent to the callers. We need to cancel + // it out by modifying rotation flags. + static ui::Transform::orientation_flags getDisplayRotation( + ui::Transform::orientation_flags rotation, int orientation) { + if (orientation == DisplayState::eOrientationDefault) { + return rotation; + } + + // convert hw orientation into flag presentation + // here inverse transform needed + uint8_t hw_rot_90 = 0x00; + uint8_t hw_flip_hv = 0x00; + switch (orientation) { + case DisplayState::eOrientation90: + hw_rot_90 = ui::Transform::ROT_90; + hw_flip_hv = ui::Transform::ROT_180; + break; + case DisplayState::eOrientation180: + hw_flip_hv = ui::Transform::ROT_180; + break; + case DisplayState::eOrientation270: + 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 & 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) & ui::Transform::ROT_180; + } + + return static_cast<ui::Transform::orientation_flags>( + (rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv)); + } + const sp<const DisplayDevice> mDevice; const Rect mSourceCrop; }; diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index 37ba4339c3..2d91c68f02 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> @@ -172,22 +171,31 @@ Composer::Composer(const std::string& serviceName) 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) { @@ -346,16 +354,26 @@ Error Composer::getColorModes(Display display, { 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) { @@ -469,21 +487,43 @@ Error Composer::getHdrCapabilities(Display display, 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; - } - - *outTypes = tmpTypes; - *outMaxLuminance = tmpMaxLuminance; - *outMaxAverageLuminance = tmpMaxAverageLuminance; - *outMinLuminance = tmpMinLuminance; - }); + 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; + }); + } 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; } @@ -547,8 +587,11 @@ Error Composer::setColorMode(Display display, ColorMode mode, 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)); @@ -897,23 +940,43 @@ Error Composer::setLayerPerFrameMetadata(Display display, Layer layer, 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, @@ -924,15 +987,22 @@ Error Composer::getRenderIntents(Display display, ColorMode colorMode, } 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; } @@ -945,18 +1015,148 @@ Error Composer::getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatri } Error error = kDefaultError; - mClient_2_2->getDataspaceSaturationMatrix(dataspace, [&](const auto& tmpError, const auto& tmpMatrix) { - error = tmpError; - if (error != Error::NONE) { - return; - } + 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()); + }); - *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; +} + CommandReader::~CommandReader() { resetData(); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index beee53966f..9d0d8d92f2 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 types = hardware::graphics::common; 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 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,28 @@ public: 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; }; namespace impl { @@ -374,12 +392,29 @@ public: 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; + private: class CommandWriter : public CommandWriterBase { public: @@ -405,7 +440,8 @@ private: 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 0000000000..ba7818dd02 --- /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 0000000000..1599995297 --- /dev/null +++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h @@ -0,0 +1,87 @@ +/* + * 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> + +namespace android { + +struct DisplayId { + using Type = uint64_t; + 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 e6d783434a..27812f7492 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -52,26 +52,25 @@ using ui::Dataspace; * */ -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( @@ -142,8 +141,7 @@ status_t FramebufferSurface::nextBuffer(uint32_t& outSlot, 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 +159,7 @@ void FramebufferSurface::freeBufferLocked(int slotIndex) { 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 0fd8e9eaa4..2431dfd7c7 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -17,6 +17,7 @@ #ifndef ANDROID_SF_FRAMEBUFFER_SURFACE_H #define ANDROID_SF_FRAMEBUFFER_SURFACE_H +#include "DisplayIdentification.h" #include "DisplaySurface.h" #include "HWComposerBufferCache.h" @@ -38,7 +39,8 @@ class HWComposer; class FramebufferSurface : public ConsumerBase, public 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 +65,7 @@ private: 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 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 1a60c83b01..392b608290 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -124,6 +124,12 @@ uint32_t Device::getMaxVirtualDisplayCount() const 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) { @@ -228,6 +234,22 @@ Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& mId(id), mIsConnected(false), mType(type) { + std::vector<Hwc2::DisplayCapability> tmpCapabilities; + auto error = static_cast<Error>(mComposer.getDisplayCapabilities(mId, &tmpCapabilities)); + if (error == Error::None) { + for (auto capability : tmpCapabilities) { + mDisplayCapabilities.emplace(static_cast<DisplayCapability>(capability)); + } + } else if (error == Error::Unsupported) { + if (capabilities.count(Capability::SkipClientColorTransform)) { + mDisplayCapabilities.emplace(DisplayCapability::SkipClientColorTransform); + } + bool dozeSupport = false; + error = static_cast<Error>(mComposer.getDozeSupport(mId, &dozeSupport)); + if (error == Error::None && dozeSupport) { + mDisplayCapabilities.emplace(DisplayCapability::Doze); + } + } ALOGV("Created display %" PRIu64, id); } @@ -403,21 +425,17 @@ Error Display::getColorModes(std::vector<ColorMode>* outModes) const return static_cast<Error>(intError); } -Error Display::getSupportedPerFrameMetadata(int32_t* outSupportedPerFrameMetadata) const +int32_t Display::getSupportedPerFrameMetadata() const { - *outSupportedPerFrameMetadata = 0; - std::vector<Hwc2::PerFrameMetadataKey> tmpKeys; - auto intError = mComposer.getPerFrameMetadataKeys(mId, &tmpKeys); - auto error = static_cast<Error>(intError); - if (error != Error::None) { - return error; - } + int32_t supportedPerFrameMetadata = 0; + + std::vector<Hwc2::PerFrameMetadataKey> tmpKeys = mComposer.getPerFrameMetadataKeys(mId); + std::set<Hwc2::PerFrameMetadataKey> keys(tmpKeys.begin(), tmpKeys.end()); // Check whether a specific metadata type is supported. A metadata type is considered // supported if and only if all required fields are supported. // SMPTE2086 - std::set<Hwc2::PerFrameMetadataKey> keys(tmpKeys.begin(), tmpKeys.end()); if (hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X) && hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y) && hasMetadataKey(keys, Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X) && @@ -428,15 +446,20 @@ Error Display::getSupportedPerFrameMetadata(int32_t* outSupportedPerFrameMetadat 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, @@ -505,15 +528,8 @@ Error Display::getType(DisplayType* outType) const 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,6 +552,27 @@ Error Display::getHdrCapabilities(HdrCapabilities* outCapabilities) const return Error::None; } +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<Layer*, sp<Fence>>* outFences) const { @@ -765,7 +802,8 @@ Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capabil : mComposer(composer), mCapabilities(capabilities), mDisplayId(displayId), - mId(layerId) + mId(layerId), + mColorMatrix(android::mat4()) { ALOGV("Created layer %" PRIu64 " on display %" PRIu64, layerId, displayId); } @@ -872,37 +910,49 @@ Error Layer::setPerFrameMetadata(const int32_t supportedPerFrameMetadata, if (validTypes & HdrMetadata::SMPTE2086) { perFrameMetadatas.insert(perFrameMetadatas.end(), {{Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X, - mHdrMetadata.smpte2086.displayPrimaryRed.x}, - {Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, - mHdrMetadata.smpte2086.displayPrimaryRed.y}, - {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X, - mHdrMetadata.smpte2086.displayPrimaryGreen.x}, - {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y, - mHdrMetadata.smpte2086.displayPrimaryGreen.y}, - {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X, - mHdrMetadata.smpte2086.displayPrimaryBlue.x}, - {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y, - mHdrMetadata.smpte2086.displayPrimaryBlue.y}, - {Hwc2::PerFrameMetadataKey::WHITE_POINT_X, - mHdrMetadata.smpte2086.whitePoint.x}, - {Hwc2::PerFrameMetadataKey::WHITE_POINT_Y, - mHdrMetadata.smpte2086.whitePoint.y}, - {Hwc2::PerFrameMetadataKey::MAX_LUMINANCE, - mHdrMetadata.smpte2086.maxLuminance}, - {Hwc2::PerFrameMetadataKey::MIN_LUMINANCE, - mHdrMetadata.smpte2086.minLuminance}}); + mHdrMetadata.smpte2086.displayPrimaryRed.x}, + {Hwc2::PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, + mHdrMetadata.smpte2086.displayPrimaryRed.y}, + {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X, + mHdrMetadata.smpte2086.displayPrimaryGreen.x}, + {Hwc2::PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y, + mHdrMetadata.smpte2086.displayPrimaryGreen.y}, + {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X, + mHdrMetadata.smpte2086.displayPrimaryBlue.x}, + {Hwc2::PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y, + mHdrMetadata.smpte2086.displayPrimaryBlue.y}, + {Hwc2::PerFrameMetadataKey::WHITE_POINT_X, + mHdrMetadata.smpte2086.whitePoint.x}, + {Hwc2::PerFrameMetadataKey::WHITE_POINT_Y, + mHdrMetadata.smpte2086.whitePoint.y}, + {Hwc2::PerFrameMetadataKey::MAX_LUMINANCE, + mHdrMetadata.smpte2086.maxLuminance}, + {Hwc2::PerFrameMetadataKey::MIN_LUMINANCE, + mHdrMetadata.smpte2086.minLuminance}}); } if (validTypes & HdrMetadata::CTA861_3) { perFrameMetadatas.insert(perFrameMetadatas.end(), {{Hwc2::PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL, - mHdrMetadata.cta8613.maxContentLightLevel}, - {Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, - mHdrMetadata.cta8613.maxFrameAverageLightLevel}}); + mHdrMetadata.cta8613.maxContentLightLevel}, + {Hwc2::PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, + mHdrMetadata.cta8613.maxFrameAverageLightLevel}}); } - auto intError = mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas); - return static_cast<Error>(intError); + Error error = static_cast<Error>( + mComposer.setLayerPerFrameMetadata(mDisplayId, mId, perFrameMetadatas)); + + if (validTypes & HdrMetadata::HDR10PLUS) { + std::vector<Hwc2::PerFrameMetadataBlob> perFrameMetadataBlobs; + perFrameMetadataBlobs.push_back( + {Hwc2::PerFrameMetadataKey::HDR10_PLUS_SEI, mHdrMetadata.hdr10plus}); + Error setMetadataBlobsError = static_cast<Error>( + mComposer.setLayerPerFrameMetadataBlobs(mDisplayId, mId, perFrameMetadataBlobs)); + if (error == Error::None) { + return setMetadataBlobsError; + } + } + return error; } Error Layer::setDisplayFrame(const Rect& frame) @@ -972,4 +1022,13 @@ Error Layer::setInfo(uint32_t type, uint32_t appId) return static_cast<Error>(intError); } +// Composer HAL 2.3 +Error Layer::setColorTransform(const android::mat4& matrix) { + if (matrix == mColorMatrix) { + return Error::None; + } + mColorMatrix = matrix; + auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray()); + return static_cast<Error>(intError); +} } // namespace HWC2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index e423167a28..f5cb97e061 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -40,6 +40,7 @@ #include "PowerAdvisor.h" namespace android { + struct DisplayedFrameStats; class Fence; class FloatRect; class GraphicBuffer; @@ -95,6 +96,9 @@ public: }; 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); @@ -215,10 +219,8 @@ public: 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; + // Returns a bitmask which contains HdrMetadata::Type::*. + [[clang::warn_unused_result]] int32_t getSupportedPerFrameMetadata() const; [[clang::warn_unused_result]] Error getRenderIntents( android::ui::ColorMode colorMode, std::vector<android::ui::RenderIntent>* outRenderIntents) const; @@ -236,6 +238,14 @@ public: [[clang::warn_unused_result]] Error supportsDoze(bool* outSupport) const; [[clang::warn_unused_result]] Error getHdrCapabilities( android::HdrCapabilities* outCapabilities) const; + [[clang::warn_unused_result]] Error getDisplayedContentSamplingAttributes( + android::ui::PixelFormat* outFormat, android::ui::Dataspace* outDataspace, + uint8_t* outComponentMask) const; + [[clang::warn_unused_result]] Error setDisplayContentSamplingEnabled(bool enabled, + uint8_t componentMask, + uint64_t maxFrames) const; + [[clang::warn_unused_result]] Error getDisplayedContentSample( + uint64_t maxFrames, uint64_t timestamp, android::DisplayedFrameStats* outStats) const; [[clang::warn_unused_result]] Error getReleaseFences( std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const; @@ -268,6 +278,9 @@ public: hwc2_display_t getId() const { return mId; } bool isConnected() const { return mIsConnected; } void setConnected(bool connected); // For use by Device only + const std::unordered_set<DisplayCapability>& getCapabilities() const { + return mDisplayCapabilities; + }; private: int32_t getAttribute(hwc2_config_t configId, Attribute attribute); @@ -294,6 +307,7 @@ private: DisplayType mType; std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers; std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs; + std::unordered_set<DisplayCapability> mDisplayCapabilities; }; // Convenience C++ class to access hwc2_device_t Layer functions directly. @@ -340,6 +354,9 @@ public: [[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 + [[clang::warn_unused_result]] Error setColorTransform(const android::mat4& matrix); + private: // These are references to data owned by HWC2::Device, which will outlive // this HWC2::Layer, so these references are guaranteed to be valid for @@ -352,6 +369,7 @@ private: android::ui::Dataspace mDataSpace = android::ui::Dataspace::UNKNOWN; android::HdrMetadata mHdrMetadata; std::function<void(Layer*)> mLayerDestroyedListener; + android::mat4 mColorMatrix; }; } // namespace HWC2 diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index f5f7a821f0..0497571871 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -20,31 +20,12 @@ #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 <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 "HWComposer.h" @@ -54,16 +35,19 @@ #include "../Layer.h" // needed only for debugging #include "../SurfaceFlinger.h" +#define LOG_HWC_DISPLAY_ERROR(hwcDisplayId, msg) \ + ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg) + #define LOG_DISPLAY_ERROR(displayId, msg) \ - ALOGE("%s failed for display %d: %s", __FUNCTION__, 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 %d: %s (%d)", __FUNCTION__, what, displayId, \ - to_string(error).c_str(), static_cast<int32_t>(error)) +#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 +66,42 @@ namespace android { -#define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION - -// --------------------------------------------------------------------------- - -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) { + 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 +129,115 @@ void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) { } } -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 false; - } + RETURN_IF_INVALID_DISPLAY(*displayId, false); - if (mHwcDisplaySlots.count(display->getId()) == 0) { - ALOGE("Unknown physical display %" PRIu64 " passed to vsync callback", - display->getId()); + auto& displayData = mDisplayData[*displayId]; + if (displayData.isVirtual) { + LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display"); 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; - } - - if (outDisplay) { - *outDisplay = disp; + displayData.lastHwVsync = timestamp; } - 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; - } - - 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); + ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__); + return {}; + } + + 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 +247,7 @@ HWC2::Layer* HWComposer::createLayer(int32_t displayId) { 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 +255,29 @@ void HWComposer::destroyLayer(int32_t displayId, HWC2::Layer* layer) { 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 +286,12 @@ std::vector<std::shared_ptr<const HWC2::Display::Config>> 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 +307,37 @@ std::shared_ptr<const HWC2::Display::Config> return config; } -int HWComposer::getActiveConfigIndex(int32_t displayId) const { - if (!isValidDisplay(displayId)) { - ALOGV("getActiveConfigIndex: Attempted to access invalid display %d", displayId); - return -1; - } +int HWComposer::getActiveConfigIndex(DisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, -1); + int index; - auto error = mDisplayData[displayId].hwcDisplay->getActiveConfigIndex(&index); + auto error = mDisplayData.at(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)); + LOG_DISPLAY_ERROR(displayId, "No active config"); 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 +350,49 @@ status_t HWComposer::setActiveColorMode(int32_t displayId, ui::ColorMode mode, 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); + std::lock_guard lock(displayData.vsyncEnabledLock); + if (enabled == displayData.vsyncEnabled) { + return; + } - displayData.vsyncEnabled = enabled; + ATRACE_CALL(); + auto error = displayData.hwcDisplay->setVsyncEnabled(enabled); + RETURN_IF_HWC_ERROR(error, displayId); - char tag[16]; - snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId); - ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0); - } + 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, std::vector<CompositionInfo>& compositionData) { 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 +416,7 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { // 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,18 +454,21 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { displayData.hasClientComposition = false; displayData.hasDeviceComposition = false; - for (auto& layer : displayDevice.getVisibleLayersSortedByZ()) { - auto hwcLayer = layer->getHwcLayer(displayId); + for (auto& compositionInfo : compositionData) { + auto hwcLayer = compositionInfo.hwc.hwcLayer; - if (changedTypes.count(hwcLayer) != 0) { + 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); + validateChange(compositionInfo.compositionType, + changedTypes[&*hwcLayer]); + compositionInfo.compositionType = changedTypes[&*hwcLayer]; + compositionInfo.layer->mLayer->setCompositionType(displayId, + compositionInfo.compositionType, + false); } - switch (layer->getCompositionType(displayId)) { + switch (compositionInfo.compositionType) { case HWC2::Composition::Client: displayData.hasClientComposition = true; break; @@ -499,17 +482,19 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { break; } - if (layerRequests.count(hwcLayer) != 0 && - layerRequests[hwcLayer] == + if (layerRequests.count(&*hwcLayer) != 0 && + layerRequests[&*hwcLayer] == HWC2::LayerRequest::ClearClientTarget) { - layer->setClearClientTarget(displayId, true); + compositionInfo.hwc.clearClientTarget = true; + compositionInfo.layer->mLayer->setClearClientTarget(displayId, true); } else { - if (layerRequests.count(hwcLayer) != 0) { + if (layerRequests.count(&*hwcLayer) != 0) { LOG_DISPLAY_ERROR(displayId, - ("Unknown layer request " + to_string(layerRequests[hwcLayer])) + ("Unknown layer request " + to_string(layerRequests[&*hwcLayer])) .c_str()); } - layer->setClearClientTarget(displayId, false); + compositionInfo.hwc.clearClientTarget = false; + compositionInfo.layer->mLayer->setClearClientTarget(displayId, false); } } @@ -519,49 +504,48 @@ status_t HWComposer::prepare(DisplayDevice& displayDevice) { 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 +553,7 @@ sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId, 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 +581,11 @@ status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) { 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 +595,7 @@ status_t HWComposer::setPowerMode(int32_t displayId, int32_t intMode) { 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 +637,7 @@ status_t HWComposer::setPowerMode(int32_t displayId, int32_t intMode) { 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 +651,7 @@ status_t HWComposer::setActiveConfig(int32_t displayId, size_t configId) { 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 +663,52 @@ status_t HWComposer::setColorTransform(int32_t displayId, 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 +717,22 @@ status_t HWComposer::getHdrCapabilities( 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 +742,116 @@ mat4 HWComposer::getDataspaceSaturationMatrix(int32_t displayId, ui::Dataspace d 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; +} 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)) { - return {}; +std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const { + if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId); + it != mPhysicalDisplayIdMap.end()) { + return it->second; } - return mDisplayData[displayId].hwcDisplay->getId(); + return {}; } -// --------------------------------------------------------------------------- - -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"); +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 {}; } -HWComposer::DisplayData::~DisplayData() { -} +std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect(hwc2_display_t hwcDisplayId) { + if (isUsingVrComposer() && mInternalHwcDisplayId) { + ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId); + return {}; + } + + 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; -void HWComposer::DisplayData::reset() { - ALOGV("DisplayData reset"); - *this = DisplayData(); + return DisplayIdentificationInfo{getFallbackDisplayId(port), + hwcDisplayId == mInternalHwcDisplayId ? "Internal display" + : "External display"}; } -// --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index f9689482cc..d9a0916f85 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -17,48 +17,29 @@ #ifndef ANDROID_SF_HWCOMPOSER_H #define ANDROID_SF_HWCOMPOSER_H -#include "HWC2.h" - -#include <stdint.h> -#include <sys/types.h> +#include <cstdint> +#include <memory> +#include <mutex> +#include <optional> +#include <unordered_map> +#include <unordered_set> +#include <vector> +#include <android-base/thread_annotations.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 <memory> -#include <optional> -#include <set> -#include <vector> -extern "C" int clock_nanosleep(clockid_t clock_id, int flags, - const struct timespec *request, - struct timespec *remain); - -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; @@ -67,173 +48,181 @@ class Composer; class HWComposer { public: - explicit HWComposer(std::unique_ptr<android::Hwc2::Composer> composer); + explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer); ~HWComposer(); void registerCallback(HWC2::ComposerCallback* callback, int32_t sequenceId); + bool getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort, + DisplayIdentificationData* outData) const; + bool hasCapability(HWC2::Capability capability) const; + bool hasDisplayCapability(const std::optional<DisplayId>& displayId, + HWC2::DisplayCapability capability) const; - // 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. + std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height, + ui::PixelFormat* format); // Attempts to create a new layer on this display - HWC2::Layer* createLayer(int32_t displayId); + HWC2::Layer* createLayer(DisplayId displayId); // Destroy a previously created layer - void destroyLayer(int32_t displayId, HWC2::Layer* layer); + void destroyLayer(DisplayId displayId, HWC2::Layer* layer); // Asks the HAL what it can do - status_t prepare(DisplayDevice& displayDevice); + status_t prepare(DisplayId displayId, std::vector<CompositionInfo>& compositionData); - status_t setClientTarget(int32_t displayId, uint32_t slot, - const sp<Fence>& acquireFence, - const sp<GraphicBuffer>& target, ui::Dataspace dataspace); + status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence, + const sp<GraphicBuffer>& target, ui::Dataspace dataspace); // Present layers to the display and read releaseFences. - status_t presentAndGetReleaseFences(int32_t displayId); + status_t presentAndGetReleaseFences(DisplayId displayId); // set power mode - status_t setPowerMode(int32_t displayId, int mode); + status_t setPowerMode(DisplayId displayId, int mode); // set active config - status_t setActiveConfig(int32_t displayId, size_t configId); + status_t setActiveConfig(DisplayId displayId, size_t configId); // Sets a color transform to be applied to the result of composition - status_t setColorTransform(int32_t displayId, const mat4& transform); + status_t setColorTransform(DisplayId displayId, const mat4& transform); // reset state when an external, non-virtual display is disconnected - void disconnectDisplay(int32_t displayId); + void disconnectDisplay(DisplayId displayId); // does this display have layers handled by HWC - bool hasDeviceComposition(int32_t displayId) const; + bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const; // does this display have pending request to flip client target - bool hasFlipClientTargetRequest(int32_t displayId) const; + bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const; // does this display have layers handled by GLES - bool hasClientComposition(int32_t displayId) const; + bool hasClientComposition(const std::optional<DisplayId>& displayId) const; // get the present fence received from the last call to present. - sp<Fence> getPresentFence(int32_t displayId) const; + sp<Fence> getPresentFence(DisplayId displayId) const; // Get last release fence for the given layer - sp<Fence> getLayerReleaseFence(int32_t displayId, - HWC2::Layer* layer) const; + sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const; // 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); + status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence, + const sp<GraphicBuffer>& buffer); // 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); + void clearReleaseFences(DisplayId displayId); // Fetches the HDR capabilities of the given display - status_t getHdrCapabilities(int32_t displayId, HdrCapabilities* outCapabilities); + status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities); - int32_t getSupportedPerFrameMetadata(int32_t displayId) const; + int32_t getSupportedPerFrameMetadata(DisplayId displayId) const; // Returns the available RenderIntent of the given display. - std::vector<ui::RenderIntent> getRenderIntents(int32_t displayId, ui::ColorMode colorMode) const; + std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId, + ui::ColorMode colorMode) const; + + mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace); - mat4 getDataspaceSaturationMatrix(int32_t displayId, ui::Dataspace dataspace); + // Returns the attributes of the color sampling engine. + status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat, + ui::Dataspace* outDataspace, + uint8_t* outComponentMask); + status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled, + uint8_t componentMask, uint64_t maxFrames); + status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp, + DisplayedFrameStats* outStats); // 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. + std::optional<DisplayIdentificationInfo> onHotplug(hwc2_display_t hwcDisplayId, + HWC2::Connection connection); - void setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled); + bool onVsync(hwc2_display_t hwcDisplayId, int64_t timestamp); + void setVsyncEnabled(DisplayId displayId, HWC2::Vsync enabled); - // 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; + nsecs_t getRefreshTimestamp(DisplayId displayId) const; + bool isConnected(DisplayId displayId) const; // Non-const because it can update configMap inside of mDisplayData - std::vector<std::shared_ptr<const HWC2::Display::Config>> - getConfigs(int32_t displayId) const; + std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs(DisplayId displayId) const; - std::shared_ptr<const HWC2::Display::Config> - getActiveConfig(int32_t displayId) const; - int getActiveConfigIndex(int32_t displayId) const; + std::shared_ptr<const HWC2::Display::Config> getActiveConfig(DisplayId displayId) const; + int getActiveConfigIndex(DisplayId displayId) const; - std::vector<ui::ColorMode> getColorModes(int32_t displayId) const; + std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const; - status_t setActiveColorMode(int32_t displayId, ui::ColorMode mode, - ui::RenderIntent renderIntent); + status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode, + ui::RenderIntent renderIntent); bool isUsingVrComposer() const; // for debugging ---------------------------------------------------------- - void dump(String8& out) const; + void dump(std::string& out) const; + + Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); } - android::Hwc2::Composer* getComposer() const { return mHwcDevice->getComposer(); } + // TODO(b/74619554): Remove special cases for internal/external display. + std::optional<hwc2_display_t> getInternalHwcDisplayId() const { return mInternalHwcDisplayId; } + std::optional<hwc2_display_t> getExternalHwcDisplayId() const { return mExternalHwcDisplayId; } + + std::optional<DisplayId> toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const; + std::optional<hwc2_display_t> fromPhysicalDisplayId(DisplayId displayId) const; - std::optional<hwc2_display_t> getHwcDisplayId(int32_t displayId) const; 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}; - uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()}; + // This must be destroyed before mDisplayData, because destructor may call back into HWComposer + // and look up DisplayData. + std::unique_ptr<HWC2::Device> mHwcDevice; - // protected by mLock - mutable Mutex mLock; - mutable std::unordered_map<int32_t, nsecs_t> mLastHwVSync{ - {{HWC_DISPLAY_PRIMARY, 0}, {HWC_DISPLAY_EXTERNAL, 0}}}; + std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap; + std::optional<hwc2_display_t> mInternalHwcDisplayId; + std::optional<hwc2_display_t> mExternalHwcDisplayId; + bool mHasMultiDisplaySupport = false; - // thread-safe - mutable Mutex mVsyncLock; + std::unordered_set<DisplayId> mFreeVirtualDisplayIds; + uint32_t mNextVirtualDisplayId = 0; + uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()}; }; -// --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_SF_HWCOMPOSER_H diff --git a/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h b/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.h deleted file mode 100644 index fe7944f635..0000000000 --- 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/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 9a2817dba1..27d3dc5609 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -32,11 +32,11 @@ namespace android { // --------------------------------------------------------------------------- #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) { switch (type) { @@ -48,34 +48,34 @@ static const char* dbgCompositionTypeStr(DisplaySurface::CompositionType type) { } } -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 +102,7 @@ VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, int32_t dispId, } 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 +116,9 @@ VirtualDisplaySurface::~VirtualDisplaySurface() { } status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) { - if (mDisplayId < 0) + if (!mDisplayId) { return NO_ERROR; + } mMustRecompose = mustRecompose; @@ -129,8 +130,9 @@ status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) { } 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 +179,9 @@ status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { } status_t VirtualDisplaySurface::advanceFrame() { - if (mDisplayId < 0) + if (!mDisplayId) { return NO_ERROR; + } if (mCompositionType == COMPOSITION_HWC) { VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED, @@ -211,7 +214,7 @@ status_t VirtualDisplaySurface::advanceFrame() { // 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) { @@ -221,22 +224,23 @@ status_t VirtualDisplaySurface::advanceFrame() { &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 +295,9 @@ const sp<Fence>& VirtualDisplaySurface::getClientTargetAcquireFence() const { 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 +318,7 @@ status_t VirtualDisplaySurface::setAsyncMode(bool async) { 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 +364,7 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, uint 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 +451,9 @@ status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */, 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 +510,9 @@ status_t VirtualDisplaySurface::queueBuffer(int pslot, 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 +623,8 @@ void VirtualDisplaySurface::resetPerFrameState() { } status_t VirtualDisplaySurface::refreshOutputBuffer() { + LOG_FATAL_IF(!mDisplayId); + if (mOutputProducerSlot >= 0) { mSource[SOURCE_SINK]->cancelBuffer( mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot), @@ -633,8 +642,8 @@ status_t VirtualDisplaySurface::refreshOutputBuffer() { // 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 5c8aceae92..33678df963 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -17,6 +17,10 @@ #ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H #define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H +#include <optional> +#include <string> + +#include "DisplayIdentification.h" #include "DisplaySurface.h" #include "HWComposerBufferCache.h" @@ -73,11 +77,10 @@ class VirtualDisplaySurface : public 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 @@ private: // 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; diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h deleted file mode 100644 index 9c13ed2755..0000000000 --- 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 1539873aaa..f4cc49b851 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 @@ bool FrameTracker::isFrameValidLocked(size_t idx) const { 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 b4a9fd68fb..555dcc1d1f 100644 --- a/services/surfaceflinger/FrameTracker.h +++ b/services/surfaceflinger/FrameTracker.h @@ -90,7 +90,7 @@ public: 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 0caac9b16d..f4b3cddc63 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -25,6 +25,8 @@ #include <sys/types.h> #include <algorithm> +#include <android-base/stringprintf.h> + #include <cutils/compiler.h> #include <cutils/native_handle.h> #include <cutils/properties.h> @@ -50,11 +52,11 @@ #include "LayerRejecter.h" #include "MonitoredProducer.h" #include "SurfaceFlinger.h" -#include "clz.h" #include "DisplayHardware/HWComposer.h" +#include "TimeStats/TimeStats.h" -#include "RenderEngine/RenderEngine.h" +#include <renderengine/RenderEngine.h> #include <mutex> #include "LayerProtoHelper.h" @@ -63,77 +65,60 @@ namespace android { -LayerBE::LayerBE() - : mMesh(Mesh::TRIANGLE_FAN, 4, 2, 2) { -} - - -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), - mFiltering(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) { +using base::StringAppendF; + +std::atomic<int32_t> Layer::sSequence{1}; + +Layer::Layer(const LayerCreationArgs& args) + : mFlinger(args.flinger), + mName(args.name), + mClientRef(args.client), + mBE{this, args.name.string()} { 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.flags = layerFlags; - mCurrentState.active.transform.set(0, 0); - mCurrentState.crop.makeInvalid(); - mCurrentState.finalCrop.makeInvalid(); - mCurrentState.requestedFinalCrop = mCurrentState.finalCrop; - mCurrentState.requestedCrop = mCurrentState.crop; - mCurrentState.z = 0; - mCurrentState.color.a = 1.0f; - mCurrentState.layerStack = 0; - mCurrentState.sequence = 0; - mCurrentState.requested = mCurrentState.active; - mCurrentState.appId = 0; - mCurrentState.type = 0; + mState.current.active_legacy.w = args.w; + mState.current.active_legacy.h = args.h; + mState.current.flags = layerFlags; + mState.current.active_legacy.transform.set(0, 0); + mState.current.crop_legacy.makeInvalid(); + mState.current.requestedCrop_legacy = mState.current.crop_legacy; + mState.current.z = 0; + mState.current.color.a = 1.0f; + mState.current.layerStack = 0; + mState.current.sequence = 0; + mState.current.requested_legacy = mState.current.active_legacy; + mState.current.appId = 0; + mState.current.type = 0; + mState.current.active.w = UINT32_MAX; + mState.current.active.h = UINT32_MAX; + mState.current.active.transform.set(0, 0); + mState.current.transform = 0; + mState.current.transformToDisplayInverse = false; + mState.current.crop.makeInvalid(); + mState.current.acquireFence = new Fence(-1); + mState.current.dataspace = ui::Dataspace::UNKNOWN; + mState.current.hdrMetadata.validTypes = 0; + mState.current.surfaceDamageRegion.clear(); + mState.current.cornerRadius = 0.0f; + mState.current.api = -1; + mState.current.hasColorTransform = false; // drawing state & current state are identical - mDrawingState = mCurrentState; + mState.drawing = mState.current; CompositorTiming compositorTiming; - flinger->getCompositorTiming(&compositorTiming); + args.flinger->getCompositorTiming(&compositorTiming); mFrameEventHistory.initializeCompositorTiming(compositorTiming); mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval); + + mFlinger->onLayerCreated(); } Layer::~Layer() { @@ -142,13 +127,11 @@ Layer::~Layer() { c->detachLayer(this); } - for (auto& point : mRemoteSyncPoints) { - point->setTransactionApplied(); - } - for (auto& point : mLocalSyncPoints) { - point->setFrameAvailable(); - } mFrameTracker.logAndResetStats(mName); + + destroyAllHwcLayersPlusChildren(); + + mFlinger->onLayerDestroyed(); } // --------------------------------------------------------------------------- @@ -163,17 +146,37 @@ Layer::~Layer() { void Layer::onLayerDisplayed(const sp<Fence>& /*releaseFence*/) {} void Layer::onRemovedFromCurrentState() { - // the layer is removed from SF mCurrentState to mLayersPendingRemoval - - mPendingRemoval = true; + mRemovedFromCurrentState = true; - if (mCurrentState.zOrderRelativeOf != nullptr) { - sp<Layer> strongRelative = mCurrentState.zOrderRelativeOf.promote(); - if (strongRelative != nullptr) { - strongRelative->removeZOrderRelative(this); - mFlinger->setTransactionFlags(eTraversalNeeded); + { + Mutex::Autolock lock(mStateMutex); + // the layer is removed from SF mState.current to mLayersPendingRemoval + if (mState.current.zOrderRelativeOf != nullptr) { + sp<Layer> strongRelative = mState.current.zOrderRelativeOf.promote(); + if (strongRelative != nullptr) { + strongRelative->removeZOrderRelative(this); + mFlinger->setTransactionFlags(eTraversalNeeded); + } + mState.current.zOrderRelativeOf = nullptr; } - mCurrentState.zOrderRelativeOf = nullptr; + } + + // Since we are no longer reachable from CurrentState SurfaceFlinger + // will no longer invoke doTransaction for us, and so we will + // never finish applying transactions. We signal the sync point + // now so that another layer will not become indefinitely + // blocked. + for (auto& point: mRemoteSyncPoints) { + point->setTransactionApplied(); + } + mRemoteSyncPoints.clear(); + + { + Mutex::Autolock syncLock(mLocalSyncPointMutex); + for (auto& point : mLocalSyncPoints) { + point->setFrameAvailable(); + } + mLocalSyncPoints.clear(); } for (const auto& child : mCurrentChildren) { @@ -181,14 +184,11 @@ void Layer::onRemovedFromCurrentState() { } } -void Layer::onRemoved() { - // the layer is removed from SF mLayersPendingRemoval - abandon(); - - destroyAllHwcLayers(); +void Layer::addToCurrentState() { + mRemovedFromCurrentState = false; for (const auto& child : mCurrentChildren) { - child->onRemoved(); + child->addToCurrentState(); } } @@ -213,44 +213,52 @@ sp<IBinder> Layer::getHandle() { // 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); +bool Layer::createHwcLayer(HWComposer* hwc, DisplayId displayId) { + LOG_ALWAYS_FATAL_IF(hasHwcLayer(displayId), "Already have a layer for display %s", + to_string(displayId).c_str()); + auto layer = std::shared_ptr<HWC2::Layer>( + hwc->createLayer(displayId), + [hwc, displayId](HWC2::Layer* layer) { + hwc->destroyLayer(displayId, layer); }); if (!layer) { return false; } - LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers[hwcId]; + LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers[displayId]; hwcInfo.hwc = hwc; hwcInfo.layer = layer; layer->setLayerDestroyedListener( - [this, hwcId](HWC2::Layer* /*layer*/) { getBE().mHwcLayers.erase(hwcId); }); + [this, displayId](HWC2::Layer* /*layer*/) { getBE().mHwcLayers.erase(displayId); }); return true; } -bool Layer::destroyHwcLayer(int32_t hwcId) { - if (getBE().mHwcLayers.count(hwcId) == 0) { +bool Layer::destroyHwcLayer(DisplayId displayId) { + if (!hasHwcLayer(displayId)) { return false; } - auto& hwcInfo = getBE().mHwcLayers[hwcId]; + auto& hwcInfo = getBE().mHwcLayers[displayId]; 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"); + hwcInfo.layer = nullptr; + return true; } -void Layer::destroyAllHwcLayers() { +void Layer::destroyHwcLayersForAllDisplays() { 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); } +} + +void Layer::destroyAllHwcLayersPlusChildren() { + destroyHwcLayersForAllDisplays(); LOG_ALWAYS_FATAL_IF(!getBE().mHwcLayers.empty(), "All hardware composer layers should have been destroyed"); + + for (const sp<Layer>& child : mDrawingChildren) { + child->destroyAllHwcLayersPlusChildren(); + } } Rect Layer::getContentCrop() const { @@ -289,83 +297,103 @@ static FloatRect reduce(const FloatRect& win, const Region& exclude) { } 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); - } + Mutex::Autolock lock(mStateMutex); + const State& s(getDrawingState()); + Region transparentRegion = reduceTransparentRegion ? getActiveTransparentRegion(s) : Region(); + FloatRect bounds = computeBoundsLocked(transparentRegion); + ui::Transform t = getTransformLocked(); + // Transform to screen space. + bounds = t.transform(bounds); + return Rect{bounds}; +} - Transform t = getTransform(); - win = t.transform(win); +FloatRect Layer::computeBounds() const { + Mutex::Autolock lock(mStateMutex); + return computeBoundsLocked(); +} - if (!s.finalCrop.isEmpty()) { - win.intersect(s.finalCrop, &win); - } +FloatRect Layer::computeBoundsLocked() const { + const State& s(getDrawingState()); + return computeBoundsLocked(getActiveTransparentRegion(s)); +} - 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); - } +FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const { + Mutex::Autolock lock(mStateMutex); + return computeBoundsLocked(activeTransparentRegion); +} - if (reduceTransparentRegion) { - auto const screenTransparentRegion = t.transform(s.activeTransparentRegion); - win = reduce(win, screenTransparentRegion); +FloatRect Layer::computeBoundsLocked(const Region& activeTransparentRegion) const { + const State& s(getDrawingState()); + Rect bounds = getCroppedBufferSize(s); + FloatRect floatBounds = bounds.toFloatRect(); + if (bounds.isValid()) { + // Layer has bounds. Pass in our bounds as a special case. Then pass on to our parents so + // that they can clip it. + floatBounds = cropChildBounds(floatBounds); + } else { + // Layer does not have bounds, so we fill to our parent bounds. This is done by getting our + // parent bounds and inverting the transform to get the maximum bounds we can have that + // will fit within our parent bounds. + const auto& p = mDrawingParent.promote(); + if (p != nullptr) { + ui::Transform t = s.active_legacy.transform; + // 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. + // One of the main uses is a parent window with a child sitting behind the parent + // window, marked by a transparent region. When computing the parent bounds from the + // parent's perspective we pass in the transparent region to reduce buffer allocation + // size. When computing the parent bounds from the child's perspective, we pass in an + // empty transparent region in order to extend into the the parent bounds. + floatBounds = p->computeBounds(Region()); + // Transform back to layer space. + floatBounds = t.inverse().transform(floatBounds); + } } - return win; -} - -FloatRect Layer::computeBounds() const { - const Layer::State& s(getDrawingState()); - return computeBounds(s.activeTransparentRegion); + // Subtract the transparent region and snap to the bounds. + return reduce(floatBounds, activeTransparentRegion); } -FloatRect Layer::computeBounds(const Region& activeTransparentRegion) const { - const Layer::State& s(getDrawingState()); - Rect win(s.active.w, s.active.h); +FloatRect Layer::cropChildBounds(const FloatRect& childBounds) const { + const State& s(getDrawingState()); + Rect bounds = getCroppedBufferSize(s); + FloatRect croppedBounds = childBounds; - if (!s.crop.isEmpty()) { - win.intersect(s.crop, &win); + // If the layer has bounds, then crop the passed in child bounds and pass + // it to our parents so they can crop it as well. If the layer has no bounds, + // then pass on the child bounds. + if (bounds.isValid()) { + croppedBounds = croppedBounds.intersect(bounds.toFloatRect()); } 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 to parent space and allow parent layer to crop the + // child bounds as well. + ui::Transform t = s.active_legacy.transform; + croppedBounds = t.transform(croppedBounds); + Mutex::Autolock lock(p->mStateMutex); + croppedBounds = p->cropChildBounds(croppedBounds); + croppedBounds = t.inverse().transform(croppedBounds); } + return croppedBounds; +} - 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); +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 - return reduce(floatWin, activeTransparentRegion); + return size; } -Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const { +Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const { // the crop is the area of the window that gets cropped, but not // scaled in any ways. const State& s(getDrawingState()); @@ -374,35 +402,44 @@ Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& hw) const { // 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. - Rect activeCrop(s.active.w, s.active.h); - if (!s.crop.isEmpty()) { - activeCrop.intersect(s.crop, &activeCrop); - } - - Transform t = getTransform(); - activeCrop = t.transform(activeCrop); - if (!activeCrop.intersect(hw->getViewport(), &activeCrop)) { + FloatRect activeCropFloat = computeBoundsLocked(); + ui::Transform t = getTransformLocked(); + // Transform to screen space. + activeCropFloat = t.transform(activeCropFloat); + activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect()); + // Back to layer space to work with the content crop. + activeCropFloat = t.inverse().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(getBufferSize(s), &activeCrop)) { activeCrop.clear(); } - if (!s.finalCrop.isEmpty()) { - if (!activeCrop.intersect(s.finalCrop, &activeCrop)) { - activeCrop.clear(); - } - } + return activeCrop; +} - const auto& p = mDrawingParent.promote(); - if (p != nullptr) { - auto parentCrop = p->computeInitialCrop(hw); - activeCrop.intersect(parentCrop, &activeCrop); - } +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; - return activeCrop; + renderengine::Mesh::VertexArray<vec2> cropCoords(getBE().mMesh.getCropCoordArray<vec2>()); + cropCoords[0] = vec2(win.left, win.top); + cropCoords[1] = vec2(win.left, win.top + win.getHeight()); + cropCoords[2] = vec2(win.right, win.top + win.getHeight()); + cropCoords[3] = vec2(win.right, win.top); } -FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { +FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) 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(); @@ -410,30 +447,14 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { // 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(); - } - - // subtract the transparent region and snap to the bounds - activeCrop = reduce(activeCrop, s.activeTransparentRegion); + Rect activeCrop = computeInitialCrop(display); + Rect bufferSize = getBufferSize(s); // 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()) { + if (getTransformToDisplayInverseLocked()) { /* * the code below applies the primary display's inverse transform to the * buffer @@ -444,11 +465,12 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; } // and apply to the current transform - invTransform = (Transform(invTransformOrient) * Transform(invTransform)).getOrientation(); + invTransform = (ui::Transform(invTransformOrient) * + ui::Transform(invTransform)).getOrientation(); } - int winWidth = s.active.w; - int winHeight = s.active.h; + int winWidth = bufferSize.getWidth(); + int winHeight = bufferSize.getHeight(); 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 @@ -460,10 +482,10 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { 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; + std::swap(winWidth, winHeight); } - const Rect winCrop = activeCrop.transform(invTransform, s.active.w, s.active.h); + 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); @@ -482,18 +504,17 @@ FloatRect Layer::computeCrop(const sp<const DisplayDevice>& hw) const { return crop; } -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::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) { + Mutex::Autolock lock(mStateMutex); + const auto displayId = display->getId(); + LOG_ALWAYS_FATAL_IF(!displayId); + RETURN_IF_NO_HWC_LAYER(*displayId); + auto& hwcInfo = getBE().mHwcLayers[*displayId]; // enable this layer hwcInfo.forceClientComposition = false; - if (isSecure() && !displayDevice->isSecure()) { + if (isSecureLocked() && !display->isSecure()) { hwcInfo.forceClientComposition = true; } @@ -501,8 +522,9 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z // this gives us only the "orientation" component of the transform const State& s(getDrawingState()); + const Rect bufferSize = getBufferSize(s); auto blendMode = HWC2::BlendMode::None; - if (!isOpaque(s) || getAlpha() != 1.0f) { + if (!isOpaque(s) || getAlphaLocked() != 1.0f) { blendMode = mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage; } @@ -512,15 +534,16 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z " %s (%d)", mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(), static_cast<int32_t>(error)); + getBE().compositionInfo.hwc.blendMode = blendMode; // 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); + Region activeTransparentRegion(getActiveTransparentRegion(s)); + ui::Transform t = getTransformLocked(); + Rect activeCrop = getCrop(s); + if (!activeCrop.isEmpty() && bufferSize.isValid()) { activeCrop = t.transform(activeCrop); - if (!activeCrop.intersect(displayDevice->getViewport(), &activeCrop)) { + if (!activeCrop.intersect(display->getViewport(), &activeCrop)) { activeCrop.clear(); } activeCrop = t.inverse().transform(activeCrop, true); @@ -530,29 +553,25 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z // 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)) { + if (!activeCrop.intersect(bufferSize, &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, 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, s.active.w, activeCrop.bottom)); + Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), 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)) { + Rect frame{t.transform(computeBoundsLocked(activeTransparentRegion))}; + if (!frame.intersect(display->getViewport(), &frame)) { frame.clear(); } - const Transform& tr(displayDevice->getTransform()); + const ui::Transform& tr = display->getTransform(); Rect transformedFrame = tr.transform(frame); error = hwcLayer->setDisplayFrame(transformedFrame); if (error != HWC2::Error::None) { @@ -562,8 +581,9 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z } else { hwcInfo.displayFrame = transformedFrame; } + getBE().compositionInfo.hwc.displayFrame = transformedFrame; - FloatRect sourceCrop = computeCrop(displayDevice); + FloatRect sourceCrop = computeCrop(display); error = hwcLayer->setSourceCrop(sourceCrop); if (error != HWC2::Error::None) { ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " @@ -573,22 +593,26 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z } else { hwcInfo.sourceCrop = sourceCrop; } + getBE().compositionInfo.hwc.sourceCrop = sourceCrop; - float alpha = static_cast<float>(getAlpha()); + float alpha = static_cast<float>(getAlphaLocked()); 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)); + getBE().compositionInfo.hwc.alpha = alpha; 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)); + getBE().compositionInfo.hwc.z = z; int type = s.type; int appId = s.appId; sp<Layer> parent = mDrawingParent.promote(); if (parent.get()) { + Mutex::Autolock lock(parent->mStateMutex); auto& parentState = parent->getDrawingState(); if (parentState.type >= 0 || parentState.appId >= 0) { type = parentState.type; @@ -600,6 +624,9 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(), static_cast<int32_t>(error)); + getBE().compositionInfo.hwc.type = type; + getBE().compositionInfo.hwc.appId = appId; + /* * Transformations are applied in this order: * 1) buffer orientation/flip/mirror @@ -608,10 +635,10 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z * (NOTE: the matrices are multiplied in reverse order) */ - const Transform bufferOrientation(mCurrentTransform); - Transform transform(tr * t * bufferOrientation); + const ui::Transform bufferOrientation(mCurrentTransform); + ui::Transform transform(tr * t * bufferOrientation); - if (getTransformToDisplayInverse()) { + if (getTransformToDisplayInverseLocked()) { /* * the code below applies the primary display's inverse transform to the * buffer @@ -628,14 +655,15 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z * 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; + transform = ui::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) { + if (orientation & ui::Transform::ROT_INVALID) { // we can only handle simple transformation hwcInfo.forceClientComposition = true; + getBE().mHwcLayers[*displayId].compositionType = HWC2::Composition::Client; } else { auto transform = static_cast<HWC2::Transform>(orientation); hwcInfo.transform = transform; @@ -645,31 +673,25 @@ void Layer::setGeometry(const sp<const DisplayDevice>& displayDevice, uint32_t z "%s (%d)", mName.string(), to_string(transform).c_str(), to_string(error).c_str(), static_cast<int32_t>(error)); + getBE().compositionInfo.hwc.transform = transform; } } -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; +void Layer::forceClientComposition(DisplayId displayId) { + RETURN_IF_NO_HWC_LAYER(displayId); + getBE().mHwcLayers[displayId].forceClientComposition = true; } -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; +bool Layer::getForceClientComposition(DisplayId displayId) { + RETURN_IF_NO_HWC_LAYER(displayId, false); + return getBE().mHwcLayers[displayId].forceClientComposition; } -void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) { - auto hwcId = displayDevice->getHwcDisplayId(); - if (getBE().mHwcLayers.count(hwcId) == 0 || - getCompositionType(hwcId) != HWC2::Composition::Cursor) { +void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) { + Mutex::Autolock lock(mStateMutex); + const auto displayId = display->getId(); + LOG_ALWAYS_FATAL_IF(!displayId); + if (!hasHwcLayer(*displayId) || getCompositionType(displayId) != HWC2::Composition::Cursor) { return; } @@ -678,22 +700,17 @@ void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) { // 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 frame(getTransform().transform(bounds)); - frame.intersect(displayDevice->getViewport(), &frame); - if (!s.finalCrop.isEmpty()) { - frame.intersect(s.finalCrop, &frame); - } - auto& displayTransform(displayDevice->getTransform()); + Rect bounds = reduce(win, getActiveTransparentRegion(s)); + Rect frame(getTransformLocked().transform(bounds)); + 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 = + getBE().mHwcLayers[*displayId].layer->setCursorPosition(position.left, position.top); + ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set cursor position " "to (%d, %d): %s (%d)", @@ -705,21 +722,18 @@ void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) { // drawing... // --------------------------------------------------------------------------- -void Layer::draw(const RenderArea& renderArea, const Region& clip) const { +void Layer::draw(const RenderArea& renderArea, const Region& clip) { onDraw(renderArea, clip, false); } -void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) const { +void Layer::draw(const RenderArea& renderArea, bool useIdentityTransform) { onDraw(renderArea, Region(renderArea.getBounds()), useIdentityTransform); } -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()); + Mutex::Autolock lock(mStateMutex); computeGeometry(renderArea, getBE().mMesh, false); engine.setupFillWithColor(red, green, blue, alpha); engine.drawMesh(getBE().mMesh); @@ -729,20 +743,20 @@ 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) { +void Layer::setCompositionType(DisplayId displayId, HWC2::Composition type, bool callIntoHwc) { + if (getBE().mHwcLayers.count(displayId) == 0) { ALOGE("setCompositionType called without a valid HWC layer"); return; } - auto& hwcInfo = getBE().mHwcLayers[hwcId]; + auto& hwcInfo = getBE().mHwcLayers[displayId]; auto& hwcLayer = hwcInfo.layer; - ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", hwcLayer->getId(), to_string(type).c_str(), + ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", (hwcLayer)->getId(), to_string(type).c_str(), static_cast<int>(callIntoHwc)); if (hwcInfo.compositionType != type) { ALOGV(" actually setting"); hwcInfo.compositionType = type; if (callIntoHwc) { - auto error = hwcLayer->setCompositionType(type); + auto error = (hwcLayer)->setCompositionType(type); ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set " "composition type %s: %s (%d)", @@ -752,33 +766,33 @@ void Layer::setCompositionType(int32_t hwcId, HWC2::Composition type, bool callI } } -HWC2::Composition Layer::getCompositionType(int32_t hwcId) const { - if (hwcId == DisplayDevice::DISPLAY_ID_INVALID) { +HWC2::Composition Layer::getCompositionType(const std::optional<DisplayId>& displayId) const { + if (!displayId) { // 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) { + if (getBE().mHwcLayers.count(*displayId) == 0) { ALOGE("getCompositionType called with an invalid HWC layer"); return HWC2::Composition::Invalid; } - return getBE().mHwcLayers.at(hwcId).compositionType; + return getBE().mHwcLayers.at(*displayId).compositionType; } -void Layer::setClearClientTarget(int32_t hwcId, bool clear) { - if (getBE().mHwcLayers.count(hwcId) == 0) { +void Layer::setClearClientTarget(DisplayId displayId, bool clear) { + if (getBE().mHwcLayers.count(displayId) == 0) { ALOGE("setClearClientTarget called without a valid HWC layer"); return; } - getBE().mHwcLayers[hwcId].clearClientTarget = clear; + getBE().mHwcLayers[displayId].clearClientTarget = clear; } -bool Layer::getClearClientTarget(int32_t hwcId) const { - if (getBE().mHwcLayers.count(hwcId) == 0) { +bool Layer::getClearClientTarget(DisplayId displayId) const { + if (getBE().mHwcLayers.count(displayId) == 0) { ALOGE("getClearClientTarget called without a valid HWC layer"); return false; } - return getBE().mHwcLayers.at(hwcId).clearClientTarget; + return getBE().mHwcLayers.at(displayId).clearClientTarget; } bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) { @@ -787,52 +801,31 @@ bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) { // relevant frame return false; } + if (isRemovedFromCurrentState()) { + return false; + } Mutex::Autolock lock(mLocalSyncPointMutex); mLocalSyncPoints.push_back(point); return true; } -void Layer::setFiltering(bool filtering) { - mFiltering = filtering; -} - -bool Layer::getFiltering() const { - return mFiltering; -} - // ---------------------------------------------------------------------------- // 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 = computeBoundsLocked(); 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 = getTransformLocked(); if (!useIdentityTransform) { lt = layerTransform.transform(lt); lb = layerTransform.transform(lb); @@ -840,25 +833,20 @@ void Layer::computeGeometry(const RenderArea& renderArea, Mesh& mesh, 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); + Mutex::Autolock lock(mStateMutex); + return isSecureLocked(); +} + +bool Layer::isSecureLocked() const { + const State& s(mState.drawing); return (s.flags & layer_state_t::eLayerSecure); } @@ -886,30 +874,36 @@ void Layer::clearVisibilityRegions() { // ---------------------------------------------------------------------------- // transaction // ---------------------------------------------------------------------------- - void Layer::pushPendingState() { - if (!mCurrentState.modified) { + Mutex::Autolock lock(mStateMutex); + pushPendingStateLocked(); +} + +void Layer::pushPendingStateLocked() { + if (!mState.current.modified) { return; } // 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 (mState.current.barrierLayer_legacy != nullptr && !isRemovedFromCurrentState()) { + sp<Layer> barrierLayer = mState.current.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; + mState.current.barrierLayer_legacy = nullptr; } else { - auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber); + auto syncPoint = std::make_shared<SyncPoint>(mState.current.frameNumber_legacy); if (barrierLayer->addSyncPoint(syncPoint)) { mRemoteSyncPoints.push_back(std::move(syncPoint)); } else { // We already missed the frame we're supposed to synchronize // on, so go ahead and apply the state update - mCurrentState.barrierLayer = nullptr; + mState.current.barrierLayer_legacy = nullptr; } } @@ -917,21 +911,21 @@ void Layer::pushPendingState() { setTransactionFlags(eTransactionNeeded); mFlinger->setTransactionFlags(eTraversalNeeded); } - mPendingStates.push_back(mCurrentState); - ATRACE_INT(mTransactionName.string(), mPendingStates.size()); + mState.pending.push_back(mState.current); + ATRACE_INT(mTransactionName.string(), mState.pending.size()); } void Layer::popPendingState(State* stateToCommit) { - *stateToCommit = mPendingStates[0]; + *stateToCommit = mState.pending[0]; - mPendingStates.removeAt(0); - ATRACE_INT(mTransactionName.string(), mPendingStates.size()); + mState.pending.removeAt(0); + ATRACE_INT(mTransactionName.string(), mState.pending.size()); } bool Layer::applyPendingStates(State* stateToCommit) { bool stateUpdateAvailable = false; - while (!mPendingStates.empty()) { - if (mPendingStates[0].barrierLayer != nullptr) { + while (!mState.pending.empty()) { + if (mState.pending[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 @@ -942,7 +936,8 @@ bool Layer::applyPendingStates(State* stateToCommit) { continue; } - if (mRemoteSyncPoints.front()->getFrameNumber() != mPendingStates[0].frameNumber) { + if (mRemoteSyncPoints.front()->getFrameNumber() != + mState.pending[0].frameNumber_legacy) { ALOGE("[%s] Unexpected sync point frame number found", mName.string()); // Signal our end of the sync point and then dispose of it @@ -970,27 +965,20 @@ bool Layer::applyPendingStates(State* stateToCommit) { // If we still have pending updates, wake SurfaceFlinger back up and point // it at this layer so we can process them - if (!mPendingStates.empty()) { + if (!mState.pending.empty()) { setTransactionFlags(eTransactionNeeded); mFlinger->setTransactionFlags(eTraversalNeeded); } - mCurrentState.modified = false; + mState.current.modified = false; return stateUpdateAvailable; } -uint32_t Layer::doTransaction(uint32_t flags) { - ATRACE_CALL(); - - pushPendingState(); - Layer::State c = getCurrentState(); - if (!applyPendingStates(&c)) { - return 0; - } - - const Layer::State& s(getDrawingState()); +uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) { + const 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 @@ -1000,16 +988,15 @@ uint32_t Layer::doTransaction(uint32_t flags) { " 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 @@ -1030,7 +1017,9 @@ uint32_t Layer::doTransaction(uint32_t flags) { // 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)) && + const bool resizePending = + ((stateToCommit->requested_legacy.w != stateToCommit->active_legacy.w) || + (stateToCommit->requested_legacy.h != stateToCommit->active_legacy.h)) && (getBE().compositionInfo.mBuffer != nullptr); if (!isFixedSize()) { if (resizePending && getBE().compositionInfo.hwc.sidebandStream == nullptr) { @@ -1042,7 +1031,7 @@ uint32_t Layer::doTransaction(uint32_t flags) { // 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, @@ -1054,21 +1043,42 @@ uint32_t Layer::doTransaction(uint32_t flags) { // 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 0; + } + + Mutex::Autolock lock(mStateMutex); + pushPendingStateLocked(); + State c = getCurrentState(); + if (!applyPendingStates(&c)) { + return 0; + } + + flags = doTransactionResize(flags, &c); + + const State& s(getDrawingState()); + + if (getActiveGeometry(c) != getActiveGeometry(s)) { // invalidate and recompute the visible regions if needed flags |= Layer::eVisibleRegion; } @@ -1079,8 +1089,8 @@ uint32_t Layer::doTransaction(uint32_t flags) { 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 @@ -1090,42 +1100,60 @@ uint32_t Layer::doTransaction(uint32_t flags) { clearSyncPoints(); } + if (mState.current.inputInfoChanged) { + flags |= eInputInfoChanged; + mState.current.inputInfoChanged = false; + } + // Commit the transaction commitTransaction(c); + mState.current.callbackHandles = {}; return flags; } void Layer::commitTransaction(const State& stateToCommit) { - mDrawingState = stateToCommit; + mState.drawing = stateToCommit; +} + +uint32_t Layer::getTransactionFlags() const { + Mutex::Autolock lock(mStateMutex); + return mState.transactionFlags; } uint32_t Layer::getTransactionFlags(uint32_t flags) { - return android_atomic_and(~flags, &mTransactionFlags) & flags; + Mutex::Autolock lock(mStateMutex); + uint32_t and_flags = mState.transactionFlags & flags; + mState.transactionFlags &= ~flags; + return and_flags; } uint32_t Layer::setTransactionFlags(uint32_t flags) { - return android_atomic_or(flags, &mTransactionFlags); + uint32_t old_flags = mState.transactionFlags; + mState.transactionFlags |= flags; + return old_flags; } bool Layer::setPosition(float x, float y, bool immediate) { - if (mCurrentState.requested.transform.tx() == x && mCurrentState.requested.transform.ty() == y) + Mutex::Autolock lock(mStateMutex); + if (mState.current.requested_legacy.transform.tx() == x && + mState.current.requested_legacy.transform.ty() == y) return false; - mCurrentState.sequence++; + mState.current.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); + mState.current.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); + mState.current.active_legacy.transform.set(x, y); } mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate; - mCurrentState.modified = true; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -1158,38 +1186,44 @@ bool Layer::setChildRelativeLayer(const sp<Layer>& childLayer, } bool Layer::setLayer(int32_t z) { - if (mCurrentState.z == z && !usingRelativeZ(LayerVector::StateSet::Current)) return false; - mCurrentState.sequence++; - mCurrentState.z = z; - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + if (mState.current.z == z && !usingRelativeZLocked(LayerVector::StateSet::Current)) + return false; + + mState.current.sequence++; + mState.current.z = z; + mState.current.modified = true; // Discard all relative layering. - if (mCurrentState.zOrderRelativeOf != nullptr) { - sp<Layer> strongRelative = mCurrentState.zOrderRelativeOf.promote(); + if (mState.current.zOrderRelativeOf != nullptr) { + sp<Layer> strongRelative = mState.current.zOrderRelativeOf.promote(); if (strongRelative != nullptr) { strongRelative->removeZOrderRelative(this); } - mCurrentState.zOrderRelativeOf = nullptr; + mState.current.zOrderRelativeOf = nullptr; } setTransactionFlags(eTransactionNeeded); return true; } void Layer::removeZOrderRelative(const wp<Layer>& relative) { - mCurrentState.zOrderRelatives.remove(relative); - mCurrentState.sequence++; - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + mState.current.zOrderRelatives.remove(relative); + mState.current.sequence++; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); } void Layer::addZOrderRelative(const wp<Layer>& relative) { - mCurrentState.zOrderRelatives.add(relative); - mCurrentState.modified = true; - mCurrentState.sequence++; + Mutex::Autolock lock(mStateMutex); + mState.current.zOrderRelatives.add(relative); + mState.current.modified = true; + mState.current.sequence++; setTransactionFlags(eTransactionNeeded); } bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) { + Mutex::Autolock lock(mStateMutex); sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get()); if (handle == nullptr) { return false; @@ -1199,20 +1233,20 @@ bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relati return false; } - if (mCurrentState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) && - mCurrentState.zOrderRelativeOf == relative) { + if (mState.current.z == relativeZ && usingRelativeZLocked(LayerVector::StateSet::Current) && + mState.current.zOrderRelativeOf == relative) { return false; } - mCurrentState.sequence++; - mCurrentState.modified = true; - mCurrentState.z = relativeZ; + mState.current.sequence++; + mState.current.modified = true; + mState.current.z = relativeZ; - auto oldZOrderRelativeOf = mCurrentState.zOrderRelativeOf.promote(); + auto oldZOrderRelativeOf = mState.current.zOrderRelativeOf.promote(); if (oldZOrderRelativeOf != nullptr) { oldZOrderRelativeOf->removeZOrderRelative(this); } - mCurrentState.zOrderRelativeOf = relative; + mState.current.zOrderRelativeOf = relative; relative->addZOrderRelative(this); setTransactionFlags(eTransactionNeeded); @@ -1221,96 +1255,108 @@ bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relati } 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; - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + if (mState.current.requested_legacy.w == w && mState.current.requested_legacy.h == h) + return false; + mState.current.requested_legacy.w = w; + mState.current.requested_legacy.h = h; + mState.current.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(mState.current.requested_legacy.w, mState.current.requested_legacy.h); return true; } bool Layer::setAlpha(float alpha) { - if (mCurrentState.color.a == alpha) return false; - mCurrentState.sequence++; - mCurrentState.color.a = alpha; - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + if (mState.current.color.a == alpha) return false; + mState.current.sequence++; + mState.current.color.a = alpha; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); return true; } bool Layer::setColor(const half3& color) { - if (color.r == mCurrentState.color.r && color.g == mCurrentState.color.g && - color.b == mCurrentState.color.b) + Mutex::Autolock lock(mStateMutex); + if (color.r == mState.current.color.r && color.g == mState.current.color.g && + color.b == mState.current.color.b) return false; - mCurrentState.sequence++; - mCurrentState.color.r = color.r; - mCurrentState.color.g = color.g; - mCurrentState.color.b = color.b; - mCurrentState.modified = true; + mState.current.sequence++; + mState.current.color.r = color.r; + mState.current.color.g = color.g; + mState.current.color.b = color.b; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +bool Layer::setCornerRadius(float cornerRadius) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.cornerRadius == cornerRadius) return false; + + mState.current.sequence++; + mState.current.cornerRadius = cornerRadius; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); return true; } 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()) { ALOGW("Attempt to set rotation matrix without permission ACCESS_SURFACE_FLINGER ignored"); return false; } - mCurrentState.sequence++; - mCurrentState.requested.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + mState.current.sequence++; + mState.current.requested_legacy.transform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, + matrix.dsdy); + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); return true; } + bool Layer::setTransparentRegionHint(const Region& transparent) { - mCurrentState.requestedTransparentRegion = transparent; - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + mState.current.requestedTransparentRegion_legacy = transparent; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); return true; } bool Layer::setFlags(uint8_t flags, uint8_t mask) { - const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask); - if (mCurrentState.flags == newFlags) return false; - mCurrentState.sequence++; - mCurrentState.flags = newFlags; - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + const uint32_t newFlags = (mState.current.flags & ~mask) | (flags & mask); + if (mState.current.flags == newFlags) return false; + mState.current.sequence++; + mState.current.flags = newFlags; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); return true; } -bool Layer::setCrop(const Rect& crop, bool immediate) { - if (mCurrentState.requestedCrop == crop) return false; - mCurrentState.sequence++; - mCurrentState.requestedCrop = crop; +bool Layer::setCrop_legacy(const Rect& crop, bool immediate) { + Mutex::Autolock lock(mStateMutex); + if (mState.current.requestedCrop_legacy == crop) return false; + mState.current.sequence++; + mState.current.requestedCrop_legacy = crop; if (immediate && !mFreezeGeometryUpdates) { - mCurrentState.crop = crop; + mState.current.crop_legacy = 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; - } - mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate; - - mCurrentState.modified = true; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); return true; } bool Layer::setOverrideScalingMode(int32_t scalingMode) { + Mutex::Autolock lock(mStateMutex); if (scalingMode == mOverrideScalingMode) return false; mOverrideScalingMode = scalingMode; setTransactionFlags(eTransactionNeeded); @@ -1318,22 +1364,29 @@ bool Layer::setOverrideScalingMode(int32_t scalingMode) { } void Layer::setInfo(int32_t type, int32_t appId) { - mCurrentState.appId = appId; - mCurrentState.type = type; - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + mState.current.appId = appId; + mState.current.type = type; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); } bool Layer::setLayerStack(uint32_t layerStack) { - if (mCurrentState.layerStack == layerStack) return false; - mCurrentState.sequence++; - mCurrentState.layerStack = layerStack; - mCurrentState.modified = true; + Mutex::Autolock lock(mStateMutex); + if (mState.current.layerStack == layerStack) return false; + mState.current.sequence++; + mState.current.layerStack = layerStack; + mState.current.modified = true; setTransactionFlags(eTransactionNeeded); return true; } uint32_t Layer::getLayerStack() const { + Mutex::Autolock lock(mStateMutex); + return getLayerStackLocked(); +} + +uint32_t Layer::getLayerStackLocked() const { auto p = mDrawingParent.promote(); if (p == nullptr) { return getDrawingState().layerStack; @@ -1341,30 +1394,31 @@ uint32_t Layer::getLayerStack() const { 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) { + Mutex::Autolock lock(mStateMutex); + mState.current.barrierLayer_legacy = barrierLayer; + mState.current.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.modified = false; + mState.current.modified = true; + pushPendingStateLocked(); + mState.current.barrierLayer_legacy = nullptr; + mState.current.frameNumber_legacy = 0; + mState.current.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); + Mutex::Autolock lock(mStateMutex); + const State& s(mState.drawing); const auto& parent = mDrawingParent.promote(); if (parent != nullptr && parent->isHiddenByPolicy()) { return true; @@ -1385,15 +1439,18 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const { return usage; } -void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { +void Layer::updateTransformHint(const sp<const DisplayDevice>& display) const { uint32_t orientation = 0; - if (!mFlinger->mDebugDisableTransformHint) { + // Disable setting transform hint if the debug flag is set or if the + // getTransformToDisplayInverse flag is set and the client wants to submit buffers + // in one orientation. + if (!mFlinger->mDebugDisableTransformHint && !getTransformToDisplayInverse()) { // The transform hint is used to improve performance, but we can // only have a single transform hint, it cannot // apply to all displays. - const Transform& planeTransform(hw->getTransform()); + const ui::Transform& planeTransform = display->getTransform(); orientation = planeTransform.getOrientation(); - if (orientation & Transform::ROT_INVALID) { + if (orientation & ui::Transform::ROT_INVALID) { orientation = 0; } } @@ -1404,34 +1461,35 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { // debugging // ---------------------------------------------------------------------------- +// TODO(marissaw): add new layer state info to layer debugging LayerDebugInfo Layer::getLayerDebugInfo() const { + Mutex::Autolock lock(mStateMutex); 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.mLayerStack = getLayerStackLocked(); + 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(); @@ -1451,54 +1509,74 @@ LayerDebugInfo Layer::getLayerDebugInfo() const { return info; } -void Layer::miniDumpHeader(String8& result) { - result.append("----------------------------------------"); - result.append("---------------------------------------\n"); +std::tuple<uint32_t, int32_t> Layer::getLayerStackAndZ(StateSet stateSet) { + Mutex::Autolock lock(mStateMutex); + const State& state = (stateSet == StateSet::Current) ? mState.current : mState.drawing; + + return {state.layerStack, state.z}; +} + +void Layer::miniDumpHeader(std::string& result) { + result.append("-------------------------------"); + result.append("-------------------------------"); + result.append("-----------------------------\n"); result.append(" Layer name\n"); result.append(" Z | "); result.append(" Comp Type | "); + result.append(" Transform | "); result.append(" Disp Frame (LTRB) | "); result.append(" Source Crop (LTRB)\n"); - result.append("----------------------------------------"); - result.append("---------------------------------------\n"); + result.append("-------------------------------"); + result.append("-------------------------------"); + result.append("-----------------------------\n"); } -void Layer::miniDump(String8& result, int32_t hwcId) const { - if (getBE().mHwcLayers.count(hwcId) == 0) { +void Layer::miniDump(std::string& result, DisplayId displayId) const { + Mutex::Autolock lock(mStateMutex); + if (!hasHwcLayer(displayId)) { 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 LayerBE::HWCInfo& hwcInfo = getBE().mHwcLayers.at(displayId); 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()); + StringAppendF(&result, "%10s | ", to_string(getCompositionType(displayId)).c_str()); + StringAppendF(&result, "%10s | ", to_string(hwcInfo.transform).c_str()); const Rect& frame = hwcInfo.displayFrame; - result.appendFormat("%4d %4d %4d %4d | ", frame.left, frame.top, frame.right, frame.bottom); + StringAppendF(&result, "%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, "%6.1f %6.1f %6.1f %6.1f\n", crop.left, crop.top, crop.right, + crop.bottom); + + result.append("- - - - - - - - - - - - - - - -\n"); - result.append("- - - - - - - - - - - - - - - - - - - - "); - result.append("- - - - - - - - - - - - - - - - - - - -\n"); + std::string compositionInfoStr; + getBE().compositionInfo.dump(compositionInfoStr, "compositionInfo"); + result.append(compositionInfoStr); + + result.append("- - - - - - - - - - - - - - - -"); + result.append("- - - - - - - - - - - - - - - -"); + result.append("- - - - - - - - - - - - - - -\n"); } -void Layer::dumpFrameStats(String8& result) const { +void Layer::dumpFrameStats(std::string& result) const { mFrameTracker.dumpStats(result); } @@ -1514,8 +1592,8 @@ void Layer::getFrameStats(FrameStats* outStats) const { 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); @@ -1524,14 +1602,14 @@ void Layer::dumpFrameEvents(String8& result) { 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); @@ -1582,6 +1660,10 @@ bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) { return false; } + if (attachChildren()) { + Mutex::Autolock lock(mStateMutex); + setTransactionFlags(eTransactionNeeded); + } for (const sp<Layer>& child : mCurrentChildren) { newParent->addChild(child); @@ -1619,6 +1701,10 @@ bool Layer::reparent(const sp<IBinder>& newParentHandle) { } newParent->addChild(this); + if (!newParent->isRemovedFromCurrentState()) { + addToCurrentState(); + } + sp<Client> client(mClientRef.promote()); sp<Client> newParentClient(newParent->mClientRef.promote()); @@ -1626,6 +1712,14 @@ bool Layer::reparent(const sp<IBinder>& newParentHandle) { client->updateParent(newParent); } + Mutex::Autolock lock(mStateMutex); + if (mLayerDetached) { + mLayerDetached = false; + setTransactionFlags(eTransactionNeeded); + } + if (attachChildren()) { + setTransactionFlags(eTransactionNeeded); + } return true; } @@ -1634,7 +1728,7 @@ bool Layer::detachChildren() { 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(); } } @@ -1642,6 +1736,60 @@ bool Layer::detachChildren() { 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(); + + Mutex::Autolock lock(mStateMutex); + if (mState.current.colorTransform == matrix) { + return false; + } + ++mState.current.sequence; + mState.current.colorTransform = matrix; + mState.current.hasColorTransform = matrix != identityMatrix; + mState.current.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + +mat4 Layer::getColorTransform() const { + Mutex::Autolock lock(mStateMutex); + return getColorTransformLocked(); +} + +mat4 Layer::getColorTransformLocked() const { + mat4 colorTransform = mat4(getDrawingState().colorTransform); + if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) { + colorTransform = parent->getColorTransform() * colorTransform; + } + return colorTransform; +} + +bool Layer::hasColorTransform() const { + Mutex::Autolock lock(mStateMutex); + 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 | @@ -1665,12 +1813,18 @@ void Layer::clearSyncPoints() { } int32_t Layer::getZ() const { - return mDrawingState.z; + Mutex::Autolock lock(mStateMutex); + return mState.drawing.z; } bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) { + Mutex::Autolock lock(mStateMutex); + return usingRelativeZLocked(stateSet); +} + +bool Layer::usingRelativeZLocked(LayerVector::StateSet stateSet) { const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const State& state = useDrawing ? mDrawingState : mCurrentState; + const State& state = useDrawing ? mState.drawing : mState.current; return state.zOrderRelativeOf != nullptr; } @@ -1680,7 +1834,7 @@ __attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::mak "makeTraversalList received invalid stateSet"); const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mCurrentState; + const State& state = useDrawing ? mState.drawing : mState.current; if (state.zOrderRelatives.size() == 0) { *outSkipRelativeZUsers = true; @@ -1696,7 +1850,7 @@ __attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::mak } for (const sp<Layer>& child : children) { - const State& childState = useDrawing ? child->mDrawingState : child->mCurrentState; + const State& childState = useDrawing ? child->mState.drawing : child->mState.current; if (childState.zOrderRelativeOf != nullptr) { continue; } @@ -1735,7 +1889,6 @@ void Layer::traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector:: visitor(this); for (; i < list.size(); i++) { const auto& relative = list[i]; - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { continue; } @@ -1783,7 +1936,7 @@ LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet, "makeTraversalList received invalid stateSet"); const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mCurrentState; + const State& state = useDrawing ? mState.drawing : mState.current; LayerVector traverse(stateSet); for (const wp<Layer>& weakRelative : state.zOrderRelatives) { @@ -1796,7 +1949,7 @@ LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet, } for (const sp<Layer>& child : children) { - const State& childState = useDrawing ? child->mDrawingState : child->mCurrentState; + const State& childState = useDrawing ? child->mState.drawing : child->mState.current; // If a layer has a relativeOf layer, only ignore if the layer it's relative to is a // descendent of the top most parent of the tree. If it's not a descendent, then just add // the child here since it won't be added later as a relative. @@ -1852,11 +2005,17 @@ void Layer::traverseChildrenInZOrder(LayerVector::StateSet stateSet, traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); } -Transform Layer::getTransform() const { - Transform t; +ui::Transform Layer::getTransform() const { + Mutex::Autolock lock(mStateMutex); + return getTransformLocked(); +} + +ui::Transform Layer::getTransformLocked() const { + ui::Transform t; const auto& p = mDrawingParent.promote(); if (p != nullptr) { - t = p->getTransform(); + Mutex::Autolock lock(p->mStateMutex); + t = p->getTransformLocked(); // If the parent is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g. // it isFixedSize) then there may be additional scaling not accounted @@ -1873,26 +2032,55 @@ Transform Layer::getTransform() const { 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; + float sx = p->getActiveWidth(p->getDrawingState()) / static_cast<float>(bufferWidth); + float sy = p->getActiveHeight(p->getDrawingState()) / static_cast<float>(bufferHeight); + ui::Transform extraParentScaling; extraParentScaling.set(sx, 0, 0, sy); t = t * extraParentScaling; } } - return t * getDrawingState().active.transform; + return t * getActiveTransform(getDrawingState()); } half Layer::getAlpha() const { - const auto& p = mDrawingParent.promote(); + Mutex::Autolock lock(mStateMutex); + return getAlphaLocked(); +} +half Layer::getAlphaLocked() const { + const auto& p = mDrawingParent.promote(); half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf; return parentAlpha * getDrawingState().color.a; } half4 Layer::getColor() const { const half4 color(getDrawingState().color); - return half4(color.r, color.g, color.b, getAlpha()); + return half4(color.r, color.g, color.b, getAlphaLocked()); +} + +Layer::RoundedCornerState Layer::getRoundedCornerState() const { + Mutex::Autolock lock(mStateMutex); + return getRoundedCornerStateLocked(); +} + +Layer::RoundedCornerState Layer::getRoundedCornerStateLocked() 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 ? RoundedCornerState(computeBoundsLocked(), radius) : RoundedCornerState(); } void Layer::commitChildList() { @@ -1904,13 +2092,22 @@ void Layer::commitChildList() { mDrawingParent = mCurrentParent; } +void Layer::setInputInfo(const InputWindowInfo& info) { + Mutex::Autolock lock(mStateMutex); + mState.current.inputInfo = info; + mState.current.modified = true; + mState.current.inputInfoChanged = true; + setTransactionFlags(eTransactionNeeded); +} + void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) { + Mutex::Autolock lock(mStateMutex); const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mCurrentState; + const State& state = useDrawing ? mState.drawing : mState.current; - Transform requestedTransform = state.active.transform; - Transform transform = getTransform(); + ui::Transform requestedTransform = state.active_legacy.transform; + ui::Transform transform = getTransformLocked(); layerInfo->set_id(sequence); layerInfo->set_name(getName().c_str()); @@ -1927,12 +2124,12 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) } } - LayerProtoHelper::writeToProto(state.activeTransparentRegion, + LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy, 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_layer_stack(getLayerStackLocked()); layerInfo->set_z(state.z); PositionProto* position = layerInfo->mutable_position(); @@ -1944,11 +2141,11 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) requestedPosition->set_y(requestedTransform.ty()); SizeProto* size = layerInfo->mutable_size(); - size->set_w(state.active.w); - size->set_h(state.active.h); + size->set_w(state.active_legacy.w); + size->set_h(state.active_legacy.h); - LayerProtoHelper::writeToProto(state.crop, layerInfo->mutable_crop()); - LayerProtoHelper::writeToProto(state.finalCrop, layerInfo->mutable_final_crop()); + LayerProtoHelper::writeToProto(state.crop_legacy, layerInfo->mutable_crop()); + layerInfo->set_corner_radius(getRoundedCornerStateLocked().radius); layerInfo->set_is_opaque(isOpaque(state)); layerInfo->set_invalidate(contentDirty); @@ -1978,6 +2175,8 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) auto buffer = getBE().compositionInfo.mBuffer; if (buffer != nullptr) { LayerProtoHelper::writeToProto(buffer, layerInfo->mutable_active_buffer()); + LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform), + layerInfo->mutable_buffer_transform()); } layerInfo->set_queued_frames(getQueuedFrameCount()); @@ -1985,24 +2184,26 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) layerInfo->set_window_type(state.type); layerInfo->set_app_id(state.appId); layerInfo->set_curr_frame(mCurrentFrameNumber); + layerInfo->set_effective_scaling_mode(getEffectiveScalingMode()); - for (const auto& pendingState : mPendingStates) { - auto barrierLayer = pendingState.barrierLayer.promote(); + for (const auto& pendingState : mState.pending) { + auto barrierLayer = pendingState.barrierLayer_legacy.promote(); if (barrierLayer != nullptr) { BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer(); barrierLayerProto->set_id(barrierLayer->sequence); - barrierLayerProto->set_frame_number(pendingState.frameNumber); + barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy); } } } -void Layer::writeToProto(LayerProto* layerInfo, int32_t hwcId) { - if (!hasHwcLayer(hwcId)) { +void Layer::writeToProto(LayerProto* layerInfo, DisplayId displayId) { + if (!hasHwcLayer(displayId)) { return; } + writeToProto(layerInfo, LayerVector::StateSet::Drawing); - const auto& hwcInfo = getBE().mHwcLayers.at(hwcId); + const auto& hwcInfo = getBE().mHwcLayers.at(displayId); const Rect& frame = hwcInfo.displayFrame; LayerProtoHelper::writeToProto(frame, layerInfo->mutable_hwc_frame()); @@ -2024,6 +2225,56 @@ void Layer::writeToProto(LayerProto* layerInfo, int32_t hwcId) { } } +bool Layer::isRemovedFromCurrentState() const { + return mRemovedFromCurrentState; +} + +InputWindowInfo Layer::fillInputInfo(const Rect& screenBounds) { + InputWindowInfo info; + ui::Transform t; + Rect layerBounds; + { + Mutex::Autolock lock(mStateMutex); + info = mState.drawing.inputInfo; + t = getTransformLocked(); + const float xScale = t.sx(); + const float yScale = t.sy(); + if (xScale != 1.0f || yScale != 1.0f) { + info.windowXScale *= 1.0f / xScale; + info.windowYScale *= 1.0f / yScale; + info.touchableRegion.scaleSelf(xScale, yScale); + } + + // Transform layer size to screen space and inset it by surface insets. + layerBounds = getCroppedBufferSize(getDrawingState()); + } + + layerBounds = t.transform(layerBounds); + layerBounds.inset(info.surfaceInset, info.surfaceInset, info.surfaceInset, info.surfaceInset); + + // Intersect with screen bounds to shrink the frame by the surface insets. The surface insets + // are not set on the screen bounds directly since the surface inset region may already be + // cropped by a parent layer. + Rect frame; + screenBounds.intersect(layerBounds, &frame); + + info.frameLeft = frame.left; + info.frameTop = frame.top; + info.frameRight = frame.right; + info.frameBottom = frame.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 = isVisible(); + return info; +} + +bool Layer::hasInput() const { + Mutex::Autolock lock(mStateMutex); + return mState.drawing.inputInfo.token != nullptr; +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 301f190e16..fb75e4c694 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -19,42 +19,43 @@ #include <sys/types.h> -#include <utils/RefBase.h> -#include <utils/String8.h> -#include <utils/Timers.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 "LayerBE.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/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; +using StateSet = android::LayerVector::StateSet; namespace android { @@ -74,77 +75,27 @@ class SurfaceInterceptor; // --------------------------------------------------------------------------- -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; -}; - -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; +struct LayerCreationArgs { + LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags) + : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags) {} + + SurfaceFlinger* flinger; + const sp<Client>& client; + const String8& name; + uint32_t w; + uint32_t h; + uint32_t flags; }; class Layer : public virtual RefBase { - static int32_t sSequence; + static std::atomic<int32_t> sSequence; public: + friend class LayerBE; 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 +105,18 @@ public: // 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 +125,20 @@ public: 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,23 +154,19 @@ public: 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; @@ -219,10 +178,37 @@ public: SortedVector<wp<Layer>> zOrderRelatives; half4 color; + float cornerRadius; + + bool inputInfoChanged; + InputWindowInfo inputInfo; + + // The fields below this point are only used by BufferStateLayer + Geometry active; + + uint32_t transform; + bool transformToDisplayInverse; + + Rect crop; + Region transparentRegionHint; + + sp<GraphicBuffer> buffer; + sp<Fence> acquireFence; + ui::Dataspace dataspace; + HdrMetadata hdrMetadata; + Region surfaceDamageRegion; + int32_t api; + + sp<NativeHandle> sidebandStream; + mat4 colorTransform; + bool hasColorTransform; + + // The deque of callback handles for this frame. The back of the deque contains the most + // recent callback handle. + std::deque<sp<CallbackHandle>> callbackHandles; }; - 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; } @@ -254,11 +240,12 @@ public: // 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) EXCLUDES(mStateMutex); // 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 +255,78 @@ public: // 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) EXCLUDES(mStateMutex); // 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) EXCLUDES(mStateMutex); // 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); - - 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 setLayer(int32_t z) EXCLUDES(mStateMutex); + virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) + EXCLUDES(mStateMutex); + + virtual bool setAlpha(float alpha) EXCLUDES(mStateMutex); + virtual bool setColor(const half3& color) EXCLUDES(mStateMutex); + + // 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) EXCLUDES(mStateMutex); + virtual bool setTransparentRegionHint(const Region& transparent) EXCLUDES(mStateMutex); + virtual bool setFlags(uint8_t flags, uint8_t mask) EXCLUDES(mStateMutex); + virtual bool setLayerStack(uint32_t layerStack) EXCLUDES(mStateMutex); + virtual uint32_t getLayerStack() const EXCLUDES(mStateMutex); + virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle, + uint64_t frameNumber); + virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) + EXCLUDES(mStateMutex); + virtual bool setOverrideScalingMode(int32_t overrideScalingMode) EXCLUDES(mStateMutex); + virtual void setInfo(int32_t type, int32_t appId) EXCLUDES(mStateMutex); + virtual bool reparentChildren(const sp<IBinder>& layer) EXCLUDES(mStateMutex); + virtual void setChildrenDrawingParent(const sp<Layer>& layer); + virtual bool reparent(const sp<IBinder>& newParentHandle) EXCLUDES(mStateMutex); + virtual bool detachChildren(); + bool attachChildren(); + bool isLayerDetached() const { return mLayerDetached; } + virtual bool setColorTransform(const mat4& matrix) EXCLUDES(mStateMutex); + mat4 getColorTransform() const EXCLUDES(mStateMutex); + virtual mat4 getColorTransformLocked() const REQUIRES(mStateMutex); + virtual bool hasColorTransform() const EXCLUDES(mStateMutex); + ; + + // Used only to set BufferStateLayer state + virtual bool setTransform(uint32_t /*transform*/) EXCLUDES(mStateMutex) { return false; }; + virtual bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/) + EXCLUDES(mStateMutex) { + return false; + }; + virtual bool setCrop(const Rect& /*crop*/) EXCLUDES(mStateMutex) { return false; }; + virtual bool setFrame(const Rect& /*frame*/) EXCLUDES(mStateMutex) { return false; }; + virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/) EXCLUDES(mStateMutex) { + return false; + }; + virtual bool setAcquireFence(const sp<Fence>& /*fence*/) EXCLUDES(mStateMutex) { + return false; + }; + virtual bool setDataspace(ui::Dataspace /*dataspace*/) EXCLUDES(mStateMutex) { return false; }; + virtual bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/) EXCLUDES(mStateMutex) { + return false; + }; + virtual bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/) EXCLUDES(mStateMutex) { + return false; + }; + virtual bool setApi(int32_t /*api*/) EXCLUDES(mStateMutex) { return false; }; + virtual bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/) + EXCLUDES(mStateMutex) { + return false; + }; + virtual bool setTransactionCompletedListeners( + const std::vector<sp<CallbackHandle>>& /*handles*/) EXCLUDES(mStateMutex) { + return false; + }; ui::Dataspace getDataSpace() const { return mCurrentDataSpace; } @@ -310,16 +343,21 @@ public: virtual void useSurfaceDamage() {} virtual void useEmptyDamage() {} - uint32_t getTransactionFlags(uint32_t flags); - uint32_t setTransactionFlags(uint32_t flags); + uint32_t getTransactionFlags() const EXCLUDES(mStateMutex); + uint32_t getTransactionFlags(uint32_t flags) EXCLUDES(mStateMutex); + uint32_t setTransactionFlags(uint32_t flags) REQUIRES(mStateMutex); - bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const { + bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const EXCLUDES(mStateMutex) { 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 REQUIRES(mStateMutex); + FloatRect computeBounds(const Region& activeTransparentRegion) const EXCLUDES(mStateMutex); + FloatRect computeBoundsLocked(const Region& activeTransparentRegion) const + REQUIRES(mStateMutex); + FloatRect computeBounds() const EXCLUDES(mStateMutex); + FloatRect computeBoundsLocked() const REQUIRES(mStateMutex); int32_t getSequence() const { return sequence; } @@ -336,16 +374,21 @@ public: */ virtual bool isOpaque(const Layer::State&) const { return false; } + virtual bool isDrawingOpaque() const EXCLUDES(mStateMutex) { + Mutex::Autolock lock(mStateMutex); + return isOpaque(mState.drawing); + } + /* * isSecure - true if this surface is secure, that is if it prevents * screenshots or VNC servers. */ - bool isSecure() const; + bool isSecure() const EXCLUDES(mStateMutex); /* * isVisible - true if this layer is visible, false otherwise */ - virtual bool isVisible() const = 0; + virtual bool isVisible() const EXCLUDES(mStateMutex) = 0; /* * isHiddenByPolicy - true if this layer has been forced invisible. @@ -353,7 +396,13 @@ public: * For example if this layer has no active buffer, it may not be hidden by * policy, but it still can not be visible. */ - bool isHiddenByPolicy() const; + bool isHiddenByPolicy() const EXCLUDES(mStateMutex); + + /* + * 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 @@ -365,62 +414,81 @@ public: // to avoid grabbing the lock again to avoid deadlock virtual bool isCreatedFromMainThread() const { return false; } - - bool isPendingRemoval() const { return mPendingRemoval; } + bool isRemovedFromCurrentState() const; void writeToProto(LayerProto* layerInfo, - LayerVector::StateSet stateSet = LayerVector::StateSet::Drawing); + LayerVector::StateSet stateSet = LayerVector::StateSet::Drawing) + EXCLUDES(mStateMutex); - void writeToProto(LayerProto* layerInfo, int32_t hwcId); + void writeToProto(LayerProto* layerInfo, DisplayId displayId); + + 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 Region getDrawingActiveTransparentRegion() const EXCLUDES(mStateMutex) { + Mutex::Autolock lock(mStateMutex); + return getActiveTransparentRegion(mState.drawing); + } + + virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; } protected: /* * onDraw - draws the surface. */ - virtual void onDraw(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform) const = 0; + virtual void onDraw(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform) + EXCLUDES(mStateMutex) = 0; 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 setGeometry(const sp<const DisplayDevice>& display, uint32_t z) EXCLUDES(mStateMutex); + void forceClientComposition(DisplayId displayId); + bool getForceClientComposition(DisplayId displayId); + virtual void setPerFrameData(DisplayId displayId, const ui::Transform& transform, + const Rect& viewport, int32_t supportedPerFrameMetadata) + EXCLUDES(mStateMutex) = 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(DisplayId displayId, HWC2::Composition type, bool callIntoHwc = true); + HWC2::Composition getCompositionType(const std::optional<DisplayId>& displayId) const; + void setClearClientTarget(DisplayId displayId, bool clear); + bool getClearClientTarget(DisplayId displayId) const; + void updateCursorPosition(const sp<const DisplayDevice>& display) EXCLUDES(mStateMutex); /* * 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*/) { + const CompositorTiming& /*compositorTiming*/) + EXCLUDES(mStateMutex) { return false; } @@ -432,15 +500,14 @@ public: * draw - performs some global clipping optimizations * and calls onDraw(). */ - void draw(const RenderArea& renderArea, const Region& clip) const; - void draw(const RenderArea& renderArea, bool useIdentityTransform) const; - void draw(const RenderArea& renderArea) const; + void draw(const RenderArea& renderArea, const Region& clip) EXCLUDES(mStateMutex); + void draw(const RenderArea& renderArea, bool useIdentityTransform) EXCLUDES(mStateMutex); /* * doTransaction - process the transaction. This is a good place to figure * out which attributes of the surface have changed. */ - uint32_t doTransaction(uint32_t transactionFlags); + uint32_t doTransaction(uint32_t transactionFlags) EXCLUDES(mStateMutex); /* * setVisibleRegion - called to set the new visible region. This gives @@ -472,28 +539,27 @@ public: * 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 Region latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/, + const sp<Fence>& /*releaseFence*/) EXCLUDES(mStateMutex) { return {}; } virtual bool isBufferLatched() const { return false; } - bool isPotentialCursor() const { return mPotentialCursor; } /* - * called with the state lock from a binder thread when the layer is + * called with SurfaceFlinger mStateLock a binder thread when the layer is * removed from the current list to the pending removal list */ - void onRemovedFromCurrentState(); + void onRemovedFromCurrentState() EXCLUDES(mStateMutex); /* - * 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,49 +568,65 @@ public: 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 EXCLUDES(mStateMutex) { 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 createHwcLayer(HWComposer* hwc, DisplayId displayId); + bool destroyHwcLayer(DisplayId displayId); + void destroyHwcLayersForAllDisplays(); + void destroyAllHwcLayersPlusChildren(); - bool hasHwcLayer(int32_t hwcId) { - return getBE().mHwcLayers.count(hwcId) > 0; - } + bool hasHwcLayer(DisplayId displayId) const { return getBE().mHwcLayers.count(displayId) > 0; } - HWC2::Layer* getHwcLayer(int32_t hwcId) { - if (getBE().mHwcLayers.count(hwcId) == 0) { + HWC2::Layer* getHwcLayer(DisplayId displayId) { + if (!hasHwcLayer(displayId)) { return nullptr; } - return getBE().mHwcLayers[hwcId].layer; + return getBE().mHwcLayers[displayId].layer.get(); + } + + bool setHwcLayer(DisplayId displayId) { + if (!hasHwcLayer(displayId)) { + return false; + } + getBE().compositionInfo.hwc.hwcLayer = getBE().mHwcLayers[displayId].layer; + return true; } // ----------------------------------------------------------------------- + void clearWithOpenGL(const RenderArea& renderArea) const EXCLUDES(mStateMutex); + + inline const State& getDrawingState() const REQUIRES(mStateMutex) { return mState.drawing; } - void clearWithOpenGL(const RenderArea& renderArea) const; - void setFiltering(bool filtering); - bool getFiltering() const; + inline const State& getCurrentState() const REQUIRES(mStateMutex) { return mState.current; } + inline State& getCurrentState() REQUIRES(mStateMutex) { return mState.current; } + + std::tuple<uint32_t, int32_t> getLayerStackAndZ(StateSet stateSet) EXCLUDES(mStateMutex); + wp<Layer> getZOrderRelativeOf(StateSet stateSet) EXCLUDES(mStateMutex) { + Mutex::Autolock lock(mStateMutex); + const State& state = (stateSet == StateSet::Current) ? mState.current : mState.drawing; + + return state.zOrderRelativeOf; + } - inline const State& getDrawingState() const { return mDrawingState; } - inline const State& getCurrentState() const { return mCurrentState; } - inline State& getCurrentState() { return mCurrentState; } + uint8_t getCurrentFlags() EXCLUDES(mStateMutex) { + Mutex::Autolock lock(mStateMutex); + return mState.current.flags; + } - LayerDebugInfo getLayerDebugInfo() const; + LayerDebugInfo getLayerDebugInfo() const EXCLUDES(mStateMutex); /* 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, DisplayId displayId) const EXCLUDES(mStateMutex); + void dumpFrameStats(std::string& result) const; + void dumpFrameEvents(std::string& result); void clearFrameStats(); void logFrameStats(); void getFrameStats(FrameStats* outStats) const; @@ -557,15 +639,29 @@ public: void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry, FrameEventHistoryDelta* outDelta); - virtual bool getTransformToDisplayInverse() const { return false; } + bool getTransformToDisplayInverse() const EXCLUDES(mStateMutex) { + Mutex::Autolock lock(mStateMutex); + return getTransformToDisplayInverseLocked(); + } + + virtual bool getTransformToDisplayInverseLocked() const REQUIRES(mStateMutex) { return false; } - Transform getTransform() const; + ui::Transform getTransform() const EXCLUDES(mStateMutex); + ui::Transform getTransformLocked() const REQUIRES(mStateMutex); // Returns the Alpha of the Surface, accounting for the Alpha // of parent Surfaces in the hierarchy (alpha's will be multiplied // down the hierarchy). - half getAlpha() const; - half4 getColor() const; + half getAlpha() const EXCLUDES(mStateMutex); + half4 getColor() const REQUIRES(mStateMutex); + + // 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 EXCLUDES(mStateMutex); + RoundedCornerState getRoundedCornerStateLocked() const REQUIRES(mStateMutex); void traverseInReverseZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor); @@ -585,7 +681,7 @@ public: 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 computeScreenBounds(bool reduceTransparentRegion = true) const EXCLUDES(mStateMutex); bool setChildLayer(const sp<Layer>& childLayer, int32_t z); bool setChildRelativeLayer(const sp<Layer>& childLayer, const sp<IBinder>& relativeToHandle, int32_t relativeZ); @@ -593,8 +689,23 @@ public: // Copy the current list of children to the drawing state. Called by // SurfaceFlinger to complete a transaction. void commitChildList(); - int32_t getZ() const; - void pushPendingState(); + int32_t getZ() const EXCLUDES(mStateMutex); + void pushPendingState() EXCLUDES(mStateMutex); + virtual void pushPendingStateLocked() REQUIRES(mStateMutex); + + /** + * 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 REQUIRES(mStateMutex) { + return Rect::INVALID_RECT; + } + + virtual Rect getBufferSize(StateSet stateSet) const EXCLUDES(mStateMutex) { + Mutex::Autolock lock(mStateMutex); + const State& state = (stateSet == StateSet::Current) ? mState.current : mState.drawing; + return getBufferSize(state); + } protected: // constant @@ -605,12 +716,12 @@ protected: */ class LayerCleaner { sp<SurfaceFlinger> mFlinger; - wp<Layer> mLayer; + sp<Layer> mLayer; protected: ~LayerCleaner() { // destroy client resources - mFlinger->onLayerDestroyed(mLayer); + mFlinger->onHandleDestroyed(mLayer); } public: @@ -620,26 +731,34 @@ protected: friend class impl::SurfaceInterceptor; - void commitTransaction(const State& stateToCommit); + // For unit tests + friend class TestableSurfaceFlinger; + + void commitTransaction(const State& stateToCommit) REQUIRES(mStateMutex); uint32_t getEffectiveUsage(uint32_t usage) const; - FloatRect computeCrop(const sp<const DisplayDevice>& hw) const; + virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const + REQUIRES(mStateMutex); // 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; + Rect computeInitialCrop(const sp<const DisplayDevice>& display) const REQUIRES(mStateMutex); + /** + * Setup rounded corners coordinates of this layer, taking into account the layer bounds and + * crop coordinates, transforming them into layer space. + */ + void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const; // drawing - void clearWithOpenGL(const RenderArea& renderArea, float r, float g, float b, - float alpha) const; - + void clearWithOpenGL(const RenderArea& renderArea, float r, float g, float b, float alpha) const + EXCLUDES(mStateMutex); 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); + void addZOrderRelative(const wp<Layer>& relative) EXCLUDES(mStateMutex); + void removeZOrderRelative(const wp<Layer>& relative) EXCLUDES(mStateMutex); class SyncPoint { public: @@ -675,8 +794,10 @@ protected: // Returns false if the relevant frame has already been latched bool addSyncPoint(const std::shared_ptr<SyncPoint>& point); - void popPendingState(State* stateToCommit); - bool applyPendingStates(State* stateToCommit); + void popPendingState(State* stateToCommit) REQUIRES(mStateMutex); + virtual bool applyPendingStates(State* stateToCommit) REQUIRES(mStateMutex); + virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit) + REQUIRES(mStateMutex); void clearSyncPoints(); @@ -708,28 +829,31 @@ public: virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; } bool getPremultipledAlpha() const; + bool mPendingHWCDestroy{false}; + void setInputInfo(const InputWindowInfo& info) EXCLUDES(mStateMutex); + + InputWindowInfo fillInputInfo(const Rect& screenBounds) EXCLUDES(mStateMutex); + bool hasInput() const EXCLUDES(mStateMutex); + protected: // ----------------------------------------------------------------------- - bool usingRelativeZ(LayerVector::StateSet stateSet); + bool usingRelativeZ(LayerVector::StateSet stateSet) EXCLUDES(mStateMutex); + bool usingRelativeZLocked(LayerVector::StateSet stateSet) REQUIRES(mStateMutex); - bool mPremultipliedAlpha; + bool mPremultipliedAlpha{true}; String8 mName; String8 mTransactionName; // A cached version of "TX - " + mName for systraces bool mPrimaryDisplayOnly = false; - // these are protected by an external lock - State mCurrentState; - State mDrawingState; - volatile int32_t mTransactionFlags; - // 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 + mutable Mutex mStateMutex; + struct { + State current; + State drawing; + uint32_t transactionFlags{0}; + Vector<State> pending; + } mState GUARDED_BY(mStateMutex); // Timestamp history for UIAutomation. Thread safe. FrameTracker mFrameTracker; @@ -741,29 +865,22 @@ protected: FenceTimeline mAcquireTimeline; FenceTimeline mReleaseTimeline; - TimeStats& mTimeStats = TimeStats::getInstance(); - // main thread - int mActiveBufferSlot; sp<GraphicBuffer> mActiveBuffer; - sp<NativeHandle> mSidebandStream; 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; - // Whether filtering is forced on or not - bool mFiltering; + 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; @@ -771,26 +888,23 @@ protected: 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}; + private: /** * Returns an unsorted vector of all layers that are part of this tree. @@ -806,10 +920,37 @@ private: const LayerVector::Visitor& visitor); LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet, const std::vector<Layer*>& layersInTree); + + /** + * Retuns the child bounds in layer space cropped to its bounds as well all its parent bounds. + * The cropped bounds must be transformed back from parent layer space to child layer space by + * applying the inverse of the child's transformation. + */ + FloatRect cropChildBounds(const FloatRect& childBounds) const REQUIRES(mStateMutex); + + /** + * 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 REQUIRES(mStateMutex); + + // locked version of public methods + bool isSecureLocked() const REQUIRES(mStateMutex); + virtual uint32_t getLayerStackLocked() const REQUIRES(mStateMutex); + half getAlphaLocked() const REQUIRES(mStateMutex); }; -// --------------------------------------------------------------------------- +} // namespace android -}; // namespace android +#define RETURN_IF_NO_HWC_LAYER(displayId, ...) \ + do { \ + if (!hasHwcLayer(displayId)) { \ + ALOGE("[%s] %s failed: no HWC layer found for display %s", mName.string(), \ + __FUNCTION__, to_string(displayId).c_str()); \ + return __VA_ARGS__; \ + } \ + } while (false) #endif // ANDROID_LAYER_H diff --git a/services/surfaceflinger/LayerBE.cpp b/services/surfaceflinger/LayerBE.cpp new file mode 100644 index 0000000000..e39babe117 --- /dev/null +++ b/services/surfaceflinger/LayerBE.cpp @@ -0,0 +1,168 @@ +/* + * 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 "LayerBE" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "Layer.h" + +#include <android-base/stringprintf.h> +#include <renderengine/RenderEngine.h> + +#include <string> + +namespace { + +const char* getCompositionName(HWC2::Composition compositionType) { + switch (compositionType) { + case HWC2::Composition::Invalid: + return "Invalid"; + case HWC2::Composition::Client: + return "Client"; + case HWC2::Composition::Device: + return "Device"; + case HWC2::Composition::SolidColor: + return "Solid Color"; + case HWC2::Composition::Cursor: + return "Cursor"; + case HWC2::Composition::Sideband: + return "Sideband"; + } + return "Invalid"; +} + +} // namespace anonymous + +namespace android { + +LayerBE::LayerBE(Layer* layer, std::string layerName) + : mLayer(layer), + mMesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2, 2) { + compositionInfo.layer = std::make_shared<LayerBE>(*this); + compositionInfo.layerName = layerName; +} + +LayerBE::LayerBE(const LayerBE& layer) + : mLayer(layer.mLayer), + mMesh(renderengine::Mesh::TRIANGLE_FAN, 4, 2, 2) { + compositionInfo.layer = layer.compositionInfo.layer; + compositionInfo.layerName = layer.mLayer->getName().string(); +} + +void LayerBE::onLayerDisplayed(const sp<Fence>& releaseFence) { + mLayer->onLayerDisplayed(releaseFence); +} + +void LayerBE::clear(renderengine::RenderEngine& engine) { + engine.setupFillWithColor(0, 0, 0, 0); + engine.drawMesh(mMesh); +} + +void CompositionInfo::dump(const char* tag) const +{ + std::string logString; + dump(logString, tag); + ALOGV("%s", logString.c_str()); +} + +void CompositionInfo::dumpHwc(std::string& result, const char* tag) const { + if (tag == nullptr) { + result += base::StringPrintf("HWC parameters\n"); + } else { + result += base::StringPrintf("[%s]HWC parameters\n", tag); + } + + result += base::StringPrintf("\thwcLayer=%p\n", static_cast<HWC2::Layer*>(&*hwc.hwcLayer)); + result += base::StringPrintf("\tfence=%p\n", hwc.fence.get()); + result += base::StringPrintf("\tblendMode=%d\n", hwc.blendMode); + result += base::StringPrintf("\ttransform=%d\n", hwc.transform); + result += base::StringPrintf("\tz=%d\n", hwc.z); + result += base::StringPrintf("\ttype=%d\n", hwc.type); + result += base::StringPrintf("\tappId=%d\n", hwc.appId); + result += base::StringPrintf("\tdisplayFrame=%4d %4d %4d %4d\n", hwc.displayFrame.left, + hwc.displayFrame.top, hwc.displayFrame.right, + hwc.displayFrame.bottom); + result += base::StringPrintf("\talpha=%.3f", hwc.alpha); + result += base::StringPrintf("\tsourceCrop=%6.1f %6.1f %6.1f %6.1f\n", hwc.sourceCrop.left, + hwc.sourceCrop.top, hwc.sourceCrop.right, hwc.sourceCrop.bottom); + + hwc.visibleRegion.dump(result, "visibleRegion"); + hwc.surfaceDamage.dump(result, "surfaceDamage"); + + result += base::StringPrintf("\tcolor transform matrix:\n" + "\t\t[%f, %f, %f, %f,\n" + "\t\t %f, %f, %f, %f,\n" + "\t\t %f, %f, %f, %f,\n" + "\t\t %f, %f, %f, %f]\n", + hwc.colorTransform[0][0], hwc.colorTransform[1][0], + hwc.colorTransform[2][0], hwc.colorTransform[3][0], + hwc.colorTransform[0][1], hwc.colorTransform[1][1], + hwc.colorTransform[2][1], hwc.colorTransform[3][1], + hwc.colorTransform[0][2], hwc.colorTransform[1][2], + hwc.colorTransform[2][2], hwc.colorTransform[3][2], + hwc.colorTransform[0][3], hwc.colorTransform[1][3], + hwc.colorTransform[2][3], hwc.colorTransform[3][3]); +} + +void CompositionInfo::dumpRe(std::string& result, const char* tag) const { + if (tag == nullptr) { + result += base::StringPrintf("RenderEngine parameters:\n"); + } else { + result += base::StringPrintf("[%s]RenderEngine parameters:\n", tag); + } + + result += base::StringPrintf("\tblackoutLayer=%d\n", re.blackoutLayer); + result += base::StringPrintf("\tclearArea=%d\n", re.clearArea); + result += base::StringPrintf("\tpreMultipliedAlpha=%d\n", re.preMultipliedAlpha); + result += base::StringPrintf("\topaque=%d\n", re.opaque); + result += base::StringPrintf("\tdisableTexture=%d\n", re.disableTexture); + result += base::StringPrintf("\tuseIdentityTransform=%d\n", re.useIdentityTransform); +} + +void CompositionInfo::dump(std::string& result, const char* tag) const +{ + if (tag == nullptr) { + result += base::StringPrintf("CompositionInfo\n"); + } else { + result += base::StringPrintf("[%s]CompositionInfo\n", tag); + } + result += base::StringPrintf("\tLayerName: %s\n", layerName.c_str()); + result += base::StringPrintf("\tCompositionType: %s\n", + getCompositionName(compositionType)); + result += base::StringPrintf("\tmBuffer = %p\n", mBuffer.get()); + result += base::StringPrintf("\tmBufferSlot=%d\n", mBufferSlot); + result += base::StringPrintf("\tdisplayFrame=%4d %4d %4d %4d\n", hwc.displayFrame.left, + hwc.displayFrame.top, hwc.displayFrame.right, + hwc.displayFrame.bottom); + result += base::StringPrintf("\talpha=%f\n", hwc.alpha); + result += base::StringPrintf("\tsourceCrop=%6.1f %6.1f %6.1f %6.1f\n", hwc.sourceCrop.left, + hwc.sourceCrop.top, hwc.sourceCrop.right, hwc.sourceCrop.bottom); + + switch (compositionType) { + case HWC2::Composition::Device: + dumpHwc(result, tag); + break; + case HWC2::Composition::Client: + dumpRe(result, tag); + break; + default: + break; + } +} + +}; // namespace android diff --git a/services/surfaceflinger/LayerBE.h b/services/surfaceflinger/LayerBE.h new file mode 100644 index 0000000000..3f5134ecdc --- /dev/null +++ b/services/surfaceflinger/LayerBE.h @@ -0,0 +1,137 @@ +/* + * 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 <stdint.h> +#include <sys/types.h> + +#include <renderengine/Mesh.h> +#include <renderengine/RenderEngine.h> +#include <renderengine/Texture.h> +#include <ui/Region.h> + +#include "DisplayHardware/DisplayIdentification.h" +#include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/HWComposerBufferCache.h" +#include "SurfaceFlinger.h" + +namespace android { + +class LayerBE; + +struct CompositionInfo { + std::string layerName; + HWC2::Composition compositionType; + bool firstClear = false; + sp<GraphicBuffer> mBuffer = nullptr; + int mBufferSlot = BufferQueue::INVALID_BUFFER_SLOT; + std::shared_ptr<LayerBE> layer; + struct { + std::shared_ptr<HWC2::Layer> hwcLayer; + DisplayId displayId; + sp<Fence> fence; + HWC2::BlendMode blendMode = HWC2::BlendMode::Invalid; + Rect displayFrame; + float alpha; + FloatRect sourceCrop; + HWC2::Transform transform = HWC2::Transform::None; + int z; + int type; + int appId; + Region visibleRegion; + Region surfaceDamage; + sp<NativeHandle> sidebandStream; + ui::Dataspace dataspace; + hwc_color_t color; + bool clearClientTarget = false; + bool supportedPerFrameMetadata = false; + HdrMetadata hdrMetadata; + mat4 colorTransform; + } hwc; + struct { + bool blackoutLayer = false; + bool clearArea = false; + bool preMultipliedAlpha = false; + bool opaque = false; + bool disableTexture = false; + half4 color; + bool useIdentityTransform = false; + bool Y410BT2020 = false; + } re; + + void dump(const char* tag) const; + void dump(std::string& result, const char* tag = nullptr) const; + void dumpHwc(std::string& result, const char* tag = nullptr) const; + void dumpRe(std::string& result, const char* tag = nullptr) const; +}; + +class LayerBE { +public: + friend class Layer; + friend class BufferLayer; + friend class BufferQueueLayer; + friend class BufferStateLayer; + friend class ColorLayer; + friend class SurfaceFlinger; + + // For unit tests + friend class TestableSurfaceFlinger; + + LayerBE(Layer* layer, std::string layerName); + explicit LayerBE(const LayerBE& layer); + + void onLayerDisplayed(const sp<Fence>& releaseFence); + void clear(renderengine::RenderEngine& renderEngine); + renderengine::Mesh& getMesh() { return mMesh; } + + Layer*const mLayer; +private: + // The mesh used to draw the layer in GLES composition mode + renderengine::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; + std::shared_ptr<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. + std::unordered_map<DisplayId, HWCInfo> mHwcLayers; + + CompositionInfo compositionInfo; +}; + +}; // namespace android + diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index cc3955087a..3289e8f583 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -51,7 +51,8 @@ void LayerProtoHelper::writeToProto(const half4 color, ColorProto* colorProto) { colorProto->set_a(color.a); } -void LayerProtoHelper::writeToProto(const Transform& transform, TransformProto* transformProto) { +void LayerProtoHelper::writeToProto(const ui::Transform& transform, + TransformProto* transformProto) { transformProto->set_dsdx(transform[0][0]); transformProto->set_dtdx(transform[0][1]); transformProto->set_dsdy(transform[1][0]); diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 860da63ed2..6df5aeaebf 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -16,13 +16,11 @@ #include <layerproto/LayerProtoHeader.h> +#include <math/vec4.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> #include <ui/Region.h> - -#include <Transform.h> - -#include <math/vec4.h> +#include <ui/Transform.h> namespace android { namespace surfaceflinger { @@ -32,7 +30,7 @@ public: 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 ui::Transform& transform, TransformProto* transformProto); static void writeToProto(const sp<GraphicBuffer>& buffer, ActiveBufferProto* activeBufferProto); }; diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp index a5f0b9878c..72abea8b2d 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 @@ LayerRejecter::LayerRejecter(Layer::State& front, bool stickySet, const char* name, int32_t overrideScalingMode, + bool transformToDisplayInverse, bool& freezePositionUpdates) : mFront(front), mCurrent(current), @@ -38,6 +37,7 @@ LayerRejecter::LayerRejecter(Layer::State& front, mStickyTransformSet(stickySet), mName(name), mOverrideScalingMode(overrideScalingMode), + mTransformToDisplayInverse(transformToDisplayInverse), mFreezeGeometryUpdates(freezePositionUpdates) {} bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) { @@ -50,26 +50,34 @@ bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) // 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 @@ bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) 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 @@ bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) // 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 40972aac87..63d51de0ca 100644 --- a/services/surfaceflinger/LayerRejecter.h +++ b/services/surfaceflinger/LayerRejecter.h @@ -29,6 +29,7 @@ namespace android { 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 @@ namespace android { 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 2a679552eb..a2d1feb40d 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 @@ bool LayerStats::isEnabled() { } 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 @@ void LayerStats::logLayerStats(const LayersProto& layersProto) { 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 @@ void LayerStats::logLayerStats(const LayersProto& layersProto) { 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 @@ void LayerStats::dump(String8& result) { 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 bd17d82781..62b2688936 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 @@ public: 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/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp index 84945247a6..a7db23ed4e 100644 --- a/services/surfaceflinger/LayerVector.cpp +++ b/services/surfaceflinger/LayerVector.cpp @@ -38,18 +38,12 @@ int LayerVector::do_compare(const void* lhs, const void* rhs) const const auto& l = *reinterpret_cast<const sp<Layer>*>(lhs); const auto& r = *reinterpret_cast<const sp<Layer>*>(rhs); - const auto& lState = - (mStateSet == StateSet::Current) ? l->getCurrentState() : l->getDrawingState(); - const auto& rState = - (mStateSet == StateSet::Current) ? r->getCurrentState() : r->getDrawingState(); + const auto& [ls, lz] = l->getLayerStackAndZ(mStateSet); + const auto& [rs, rz] = r->getLayerStackAndZ(mStateSet); - uint32_t ls = lState.layerStack; - uint32_t rs = rState.layerStack; if (ls != rs) return (ls > rs) ? 1 : -1; - int32_t lz = lState.z; - int32_t rz = rState.z; if (lz != rz) return (lz > rz) ? 1 : -1; @@ -62,9 +56,8 @@ int LayerVector::do_compare(const void* lhs, const void* rhs) const void LayerVector::traverseInZOrder(StateSet stateSet, const Visitor& visitor) const { for (size_t i = 0; i < size(); i++) { const auto& layer = (*this)[i]; - auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState() - : layer->getDrawingState(); - if (state.zOrderRelativeOf != nullptr) { + auto zOrderRelativeOf = layer->getZOrderRelativeOf(stateSet); + if (zOrderRelativeOf != nullptr) { continue; } layer->traverseInZOrder(stateSet, visitor); @@ -74,9 +67,8 @@ void LayerVector::traverseInZOrder(StateSet stateSet, const Visitor& visitor) co void LayerVector::traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const { for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) { const auto& layer = (*this)[i]; - auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState() - : layer->getDrawingState(); - if (state.zOrderRelativeOf != nullptr) { + auto zOrderRelativeOf = layer->getZOrderRelativeOf(stateSet); + if (zOrderRelativeOf != nullptr) { continue; } layer->traverseInReverseZOrder(stateSet, visitor); diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index 389fbd23e3..06e3d9c154 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ -#include "MessageQueue.h" #include "MonitoredProducer.h" -#include "SurfaceFlinger.h" #include "Layer.h" +#include "SurfaceFlinger.h" + +#include "Scheduler/MessageQueue.h" namespace android { diff --git a/services/surfaceflinger/NativeWindowSurface.cpp b/services/surfaceflinger/NativeWindowSurface.cpp new file mode 100644 index 0000000000..3fff9283ee --- /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 0000000000..f34a45abed --- /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/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp index 1a8edf3e79..93759e8ad7 100644 --- a/services/surfaceflinger/RenderArea.cpp +++ b/services/surfaceflinger/RenderArea.cpp @@ -1,7 +1,5 @@ #include "RenderArea.h" -#include <gui/LayerState.h> - namespace android { float RenderArea::getCaptureFillValue(CaptureFill captureFill) { @@ -13,37 +11,5 @@ float RenderArea::getCaptureFillValue(CaptureFill captureFill) { return 1.0f; } } -/* - * Checks that the requested width and height are valid and updates them to the render area - * dimensions if they are set to 0 - */ -status_t RenderArea::updateDimensions(int displayRotation) { - // get screen geometry - - uint32_t width = getWidth(); - uint32_t height = getHeight(); - - if (mRotationFlags & Transform::ROT_90) { - std::swap(width, height); - } - - if (displayRotation & DisplayState::eOrientationSwapMask) { - std::swap(width, height); - } - - if ((mReqWidth > width) || (mReqHeight > height)) { - ALOGE("size mismatch (%d, %d) > (%d, %d)", mReqWidth, mReqHeight, width, height); - return BAD_VALUE; - } - - if (mReqWidth == 0) { - mReqWidth = width; - } - if (mReqHeight == 0) { - mReqHeight = height; - } - - return NO_ERROR; -} } // namespace android diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index 96e4b5f48b..9bad6dee04 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -1,50 +1,87 @@ #pragma once #include <ui/GraphicTypes.h> - -#include "Transform.h" +#include <ui/Transform.h> #include <functional> namespace android { +// 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 +// rendered to the render area, it is first transformed and clipped to the logical +// render area. The transformed and clipped layer is then projected onto the +// physical render area. class RenderArea { - public: enum class CaptureFill {CLEAR, OPAQUE}; static float getCaptureFillValue(CaptureFill captureFill); - RenderArea(uint32_t reqHeight, uint32_t reqWidth, CaptureFill captureFill, - ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone) - : mReqHeight(reqHeight), mReqWidth(reqWidth), mCaptureFill(captureFill) { - mRotationFlags = Transform::fromRotation(rotation); - } + RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill, + ui::Dataspace reqDataSpace, + ui::Transform::orientation_flags rotation = ui::Transform::ROT_0) + : mReqWidth(reqWidth), + mReqHeight(reqHeight), + mReqDataSpace(reqDataSpace), + mCaptureFill(captureFill), + mRotationFlags(rotation) {} virtual ~RenderArea() = default; - virtual const Transform& getTransform() const = 0; - virtual Rect getBounds() const = 0; - virtual int getHeight() const = 0; - virtual int getWidth() const = 0; + // Invoke drawLayers to render layers into the render area. + virtual void render(std::function<void()> drawLayers) { drawLayers(); } + + // Returns true if the render area is secure. A secure layer should be + // blacked out / skipped when rendered to an insecure render area. virtual bool isSecure() const = 0; + + // Returns true if the otherwise disabled layer filtering should be + // enabled when rendering to this render area. virtual bool needsFiltering() const = 0; + + // Returns the transform to be applied on layers to transform them into + // the logical render area. + virtual const ui::Transform& getTransform() const = 0; + + // Returns the size of the logical render area. Layers are clipped to the + // logical render area. + virtual int getWidth() const = 0; + virtual int getHeight() const = 0; + virtual Rect getBounds() const = 0; + + // Returns the source crop of the render area. The source crop defines + // how layers are projected from the logical render area onto the physical + // render area. It can be larger than the logical render area. It can + // also be optionally rotated. + // + // Layers are first clipped to the source crop (in addition to being + // clipped to the logical render area already). The source crop and the + // layers are then rotated around the center of the source crop, and + // scaled to the physical render area linearly. virtual Rect getSourceCrop() const = 0; - virtual void render(std::function<void()> drawLayers) { drawLayers(); } + // Returns the rotation of the source crop and the layers. + ui::Transform::orientation_flags getRotationFlags() const { return mRotationFlags; }; - int getReqHeight() const { return mReqHeight; }; + // Returns the size of the physical render area. int getReqWidth() const { return mReqWidth; }; - Transform::orientation_flags getRotationFlags() const { return mRotationFlags; }; - status_t updateDimensions(int displayRotation); + 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; }; private: - uint32_t mReqHeight; - uint32_t mReqWidth; - Transform::orientation_flags mRotationFlags; - CaptureFill mCaptureFill; + const uint32_t mReqWidth; + const uint32_t mReqHeight; + const ui::Dataspace mReqDataSpace; + const CaptureFill mCaptureFill; + 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 c218e4da50..0000000000 --- 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 6ebb34018d..0000000000 --- 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 744a70c66c..0000000000 --- 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 cc8eb1dfab..0000000000 --- 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/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp deleted file mode 100644 index d745770bb7..0000000000 --- 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 1786155486..0000000000 --- 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 0d20f1fd87..0000000000 --- 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 d4d3d8c0f5..0000000000 --- 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 index 37dc27d80d..b74b9010d2 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,15 @@ #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; + +namespace impl { // Setting this to true adds a zero-phase tracer for correlating with hardware // vsync events @@ -59,19 +60,20 @@ static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared #define LOG_TAG "DispSyncThread" class DispSyncThread : public Thread { public: - explicit DispSyncThread(const char* name) + DispSyncThread(const char* name, bool showTraceDetailedInfo) : mName(name), mStop(false), mPeriod(0), mPhase(0), mReferenceTime(0), mWakeupLatency(0), - mFrameNumber(0) {} + mFrameNumber(0), + mTraceDetailedInfo(showTraceDetailedInfo) {} virtual ~DispSyncThread() {} void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); mPeriod = period; mPhase = phase; @@ -83,7 +85,7 @@ public: } void stop() { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); mStop = true; mCond.signal(); @@ -94,14 +96,14 @@ public: 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 +127,7 @@ public: 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 +153,7 @@ public: 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); } @@ -169,7 +171,7 @@ public: } status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); for (size_t i = 0; i < mEventListeners.size(); i++) { @@ -187,7 +189,7 @@ public: // allowing any past events to fire listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency; - mEventListeners.push(listener); + mEventListeners.push_back(listener); mCond.signal(); @@ -195,12 +197,13 @@ public: } status_t removeEventListener(DispSync::Callback* callback) { - 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) { - mEventListeners.removeAt(i); + for (std::vector<EventListener>::iterator it = mEventListeners.begin(); + it != mEventListeners.end(); ++it) { + if (it->mCallback == callback) { + mEventListeners.erase(it); mCond.signal(); return NO_ERROR; } @@ -210,14 +213,13 @@ public: } status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) { - if (kTraceDetailedInfo) ATRACE_CALL(); + if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); - for (size_t i = 0; i < mEventListeners.size(); i++) { - if (mEventListeners[i].mCallback == callback) { - EventListener& listener = mEventListeners.editItemAt(i); - const nsecs_t oldPhase = listener.mPhase; - listener.mPhase = phase; + for (auto& eventListener : mEventListeners) { + if (eventListener.mCallback == callback) { + const nsecs_t oldPhase = eventListener.mPhase; + eventListener.mPhase = phase; // Pretend that the last time this event was handled at the same frame but with the // new offset to allow for a seamless offset change without double-firing or @@ -228,7 +230,7 @@ public: } else if (diff < -mPeriod / 2) { diff += mPeriod; } - listener.mLastEventTime -= diff; + eventListener.mLastEventTime -= diff; mCond.signal(); return NO_ERROR; } @@ -237,14 +239,6 @@ public: 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; @@ -259,7 +253,7 @@ private: }; 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 +268,23 @@ private: return nextEventTime; } - Vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) { - if (kTraceDetailedInfo) ATRACE_CALL(); + 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) { 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", mName, eventListener.mName); + callbackInvocations.push_back(ci); + eventListener.mLastEventTime = t; } } @@ -298,7 +292,7 @@ private: } 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)); @@ -348,8 +342,8 @@ private: return t; } - void fireCallbackInvocations(const Vector<CallbackInvocation>& callbacks) { - if (kTraceDetailedInfo) ATRACE_CALL(); + void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) { + if (mTraceDetailedInfo) ATRACE_CALL(); for (size_t i = 0; i < callbacks.size(); i++) { callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime); } @@ -366,10 +360,13 @@ private: 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,8 +385,13 @@ private: 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() {} @@ -408,22 +410,18 @@ void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { 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()); } } void DispSync::reset() { Mutex::Autolock lock(mMutex); + resetLocked(); +} +void DispSync::resetLocked() { mPhase = 0; mReferenceTime = 0; mModelUpdated = false; @@ -436,6 +434,10 @@ void DispSync::reset() { bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { Mutex::Autolock lock(mMutex); + if (mIgnorePresentFences) { + return true; + } + mPresentFences[mPresentSampleOffset] = fenceTime; mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES; mNumResyncSamplesSincePresent = 0; @@ -481,12 +483,10 @@ bool DispSync::addResyncSample(nsecs_t timestamp) { } 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 @@ -580,7 +580,7 @@ void DispSync::updateModelLocked() { ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase)); } - if (kTraceDetailedInfo) { + if (mTraceDetailedInfo) { ATRACE_INT64("DispSync:Period", mPeriod); ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2); } @@ -639,7 +639,7 @@ void DispSync::updateErrorLocked() { "No present times for model error."); } - if (kTraceDetailedInfo) { + if (mTraceDetailedInfo) { ATRACE_INT64("DispSync:Error", mError); } } @@ -660,54 +660,114 @@ nsecs_t DispSync::computeNextRefresh(int periodOffset) const { return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase; } -void DispSync::dump(String8& result) const { +void DispSync::setIgnorePresentFences(bool ignore) { + Mutex::Autolock lock(mMutex); + if (mIgnorePresentFences != ignore) { + mIgnorePresentFences = ignore; + resetLocked(); + } +} + +void DispSync::dump(std::string& result) const { 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); - - result.appendFormat("mResyncSamples:\n"); + 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); +} + +// TODO(b/113612090): Figure out how much of this is still relevant. +// 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 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). + const nsecs_t nextRefresh = 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; } +} // namespace impl + } // namespace android diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h index 077256ac11..4a90f10215 100644 --- a/services/surfaceflinger/DispSync.h +++ b/services/surfaceflinger/Scheduler/DispSync.h @@ -29,8 +29,38 @@ namespace android { -class String8; class FenceTime; + +class DispSync { +public: + class Callback { + public: + virtual ~Callback() = default; + virtual void onDispSyncEvent(nsecs_t when) = 0; + }; + + virtual ~DispSync(); + + virtual void reset() = 0; + virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0; + virtual void beginResync() = 0; + virtual bool addResyncSample(nsecs_t timestamp) = 0; + virtual void endResync() = 0; + virtual void setPeriod(nsecs_t period) = 0; + virtual nsecs_t getPeriod() = 0; + virtual void setRefreshSkipCount(int count) = 0; + virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0; + virtual status_t removeEventListener(Callback* callback) = 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; +}; + +namespace impl { + class DispSyncThread; // DispSync maintains a model of the periodic hardware-based vsync events of a @@ -46,21 +76,15 @@ class DispSyncThread; // 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 +95,7 @@ public: // // 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 +108,64 @@ public: // is turned on (i.e. once immediately after it's turned on) and whenever // addPresentFence returns true indicating that the model has drifted away // from the hardware vsync events. - void beginResync(); - bool addResyncSample(nsecs_t timestamp); - void endResync(); + void beginResync() override; + bool addResyncSample(nsecs_t timestamp) override; + void endResync() override; // The setPeriod method sets the vsync event model's period to a specific // value. This should be used to prime the model when a display is first // turned on. It should NOT be used after that. - void setPeriod(nsecs_t period); + void setPeriod(nsecs_t period) override; // The getPeriod method returns the current vsync period. - nsecs_t getPeriod(); + nsecs_t getPeriod() override; // setRefreshSkipCount specifies an additional number of refresh // cycles to skip. For example, on a 60Hz display, a skip count of 1 // will result in events happening at 30Hz. Default is zero. The idea // is to sacrifice smoothness for battery life. - void setRefreshSkipCount(int count); + void setRefreshSkipCount(int count) override; // addEventListener registers a callback to be called repeatedly at the // given phase offset from the hardware vsync events. The callback is // called from a separate thread and it should return reasonably quickly // (i.e. within a few hundred microseconds). - status_t addEventListener(const char* name, nsecs_t phase, Callback* callback); + status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) 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); + status_t removeEventListener(Callback* callback) 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 }; @@ -195,8 +231,13 @@ private: bool mIgnorePresentFences; std::unique_ptr<Callback> mZeroPhaseTracer; + + // Flag to turn on logging in systrace. + bool mTraceDetailedInfo = false; }; +} // namespace impl + } // namespace android #endif // ANDROID_DISPSYNC_H diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp new file mode 100644 index 0000000000..697d6344fa --- /dev/null +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#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)); + 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)); + 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 0000000000..0fd84a2321 --- /dev/null +++ b/services/surfaceflinger/Scheduler/DispSyncSource.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 <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; + + 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 index fb6cff5705..fb6cff5705 100644 --- a/services/surfaceflinger/EventControlThread.cpp +++ b/services/surfaceflinger/Scheduler/EventControlThread.cpp diff --git a/services/surfaceflinger/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h index cafae53400..cafae53400 100644 --- a/services/surfaceflinger/EventControlThread.h +++ b/services/surfaceflinger/Scheduler/EventControlThread.h diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index bc271c8ec5..1683982485 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -22,18 +22,20 @@ #include <chrono> #include <cstdint> +#include <android-base/stringprintf.h> + #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; +using android::base::StringAppendF; // --------------------------------------------------------------------------- @@ -41,15 +43,69 @@ namespace android { // --------------------------------------------------------------------------- +EventThreadConnection::EventThreadConnection(EventThread* eventThread) + : count(-1), 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 count) { + mEventThread->setVsyncRate(count, this); + return NO_ERROR; +} + +void EventThreadConnection::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, + const ResyncWithRateLimitCallback& resyncWithRateLimitCallback, + const InterceptVSyncsCallback& interceptVSyncsCallback, + const ResetIdleTimerCallback& resetIdleTimerCallback, + const char* threadName) + : EventThread(nullptr, std::move(src), resyncWithRateLimitCallback, interceptVSyncsCallback, + threadName) { + mResetIdleTimer = resetIdleTimerCallback; +} + EventThread::EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) + : EventThread(src, nullptr, resyncWithRateLimitCallback, interceptVSyncsCallback, + threadName) {} + +EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc, + ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) : mVSyncSource(src), + mVSyncSourceUnique(std::move(uniqueSrc)), mResyncWithRateLimitCallback(resyncWithRateLimitCallback), mInterceptVSyncsCallback(interceptVSyncsCallback) { + if (src == nullptr) { + mVSyncSource = mVSyncSourceUnique.get(); + } for (auto& event : mVSyncEvent) { event.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; event.header.id = 0; @@ -88,23 +144,36 @@ void EventThread::setPhaseOffset(nsecs_t phaseOffset) { mVSyncSource->setPhaseOffset(phaseOffset); } -sp<BnDisplayEventConnection> EventThread::createEventConnection() const { - return new Connection(const_cast<EventThread*>(this)); +sp<EventThreadConnection> EventThread::createEventConnection() const { + return new EventThreadConnection(const_cast<EventThread*>(this)); } -status_t EventThread::registerDisplayEventConnection( - const sp<EventThread::Connection>& connection) { +status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) { std::lock_guard<std::mutex> lock(mMutex); - mDisplayEventConnections.add(connection); + + // 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<EventThread::Connection>& connection) { - mDisplayEventConnections.remove(connection); +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 count, const sp<EventThread::Connection>& connection) { +void EventThread::setVsyncRate(uint32_t count, const sp<EventThreadConnection>& 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; @@ -115,8 +184,11 @@ void EventThread::setVsyncRate(uint32_t count, const sp<EventThread::Connection> } } -void EventThread::requestNextVsync(const sp<EventThread::Connection>& connection) { +void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) { std::lock_guard<std::mutex> lock(mMutex); + if (mResetIdleTimer) { + mResetIdleTimer(); + } if (mResyncWithRateLimitCallback) { mResyncWithRateLimitCallback(); @@ -155,33 +227,28 @@ void EventThread::onVSyncEvent(nsecs_t timestamp) { 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); - +void EventThread::onHotplugReceived(DisplayType displayType, bool connected) { 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(); - } + + DisplayEventReceiver::Event event; + event.header.type = DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG; + event.header.id = displayType == DisplayType::Primary ? 0 : 1; + event.header.timestamp = systemTime(); + event.hotplug.connected = connected; + + mPendingEvents.push(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; + std::vector<sp<EventThreadConnection>> 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]); + for (const sp<EventThreadConnection>& conn : signalConnections) { // now see if we still need to report this event status_t err = conn->postEvent(event); if (err == -EAGAIN || err == -EWOULDBLOCK) { @@ -196,7 +263,7 @@ void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { // 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]); + removeDisplayEventConnectionLocked(conn); } } } @@ -204,44 +271,44 @@ void EventThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { // 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; +std::vector<sp<EventThreadConnection>> EventThread::waitForEventLocked( + std::unique_lock<std::mutex>* lock, DisplayEventReceiver::Event* outEvent) { + std::vector<sp<EventThreadConnection>> signalConnections; - while (signalConnections.isEmpty() && mKeepRunning) { + while (signalConnections.empty() && 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; + for (auto& event : mVSyncEvent) { + timestamp = event.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; + *outEvent = event; + event.header.timestamp = 0; + vsyncCount = event.vsync.count; break; } } if (!timestamp) { // no vsync event, see if there are some other event - eventPending = !mPendingEvents.isEmpty(); + eventPending = !mPendingEvents.empty(); if (eventPending) { // we have some other event to dispatch - *event = mPendingEvents[0]; - mPendingEvents.removeAt(0); + *outEvent = mPendingEvents.front(); + mPendingEvents.pop(); } } // find out connections waiting for events - size_t count = mDisplayEventConnections.size(); - for (size_t i = 0; i < count;) { - sp<Connection> connection(mDisplayEventConnections[i].promote()); + auto it = mDisplayEventConnections.begin(); + while (it != mDisplayEventConnections.end()) { + sp<EventThreadConnection> connection(it->promote()); if (connection != nullptr) { bool added = false; if (connection->count >= 0) { @@ -254,12 +321,12 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( if (connection->count == 0) { // fired this time around connection->count = -1; - signalConnections.add(connection); + signalConnections.push_back(connection); added = true; } else if (connection->count == 1 || (vsyncCount % connection->count) == 0) { // continuous event, and time to report it - signalConnections.add(connection); + signalConnections.push_back(connection); added = true; } } @@ -269,14 +336,13 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( // we don't have a vsync event to process // (timestamp==0), but we have some pending // messages. - signalConnections.add(connection); + signalConnections.push_back(connection); } - ++i; + ++it; } else { // we couldn't promote this reference, the connection has // died, so clean-up! - mDisplayEventConnections.removeAt(i); - --count; + it = mDisplayEventConnections.erase(it); } } @@ -319,7 +385,7 @@ Vector<sp<EventThread::Connection> > EventThread::waitForEventLocked( // 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.id = 0; mVSyncEvent[0].header.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); mVSyncEvent[0].vsync.count++; } @@ -359,55 +425,19 @@ void EventThread::disableVSyncLocked() { } } -void EventThread::dump(String8& result) const { +void EventThread::dump(std::string& 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); + StringAppendF(&result, "VSYNC state: %s\n", mDebugVsyncEnabled ? "enabled" : "disabled"); + StringAppendF(&result, " soft-vsync: %s\n", mUseSoftwareVSync ? "enabled" : "disabled"); + StringAppendF(&result, " numListeners=%zu,\n events-delivered: %u\n", + mDisplayEventConnections.size(), mVSyncEvent[0].vsync.count); + for (const wp<EventThreadConnection>& weak : mDisplayEventConnections) { + sp<EventThreadConnection> connection = weak.promote(); + StringAppendF(&result, " %p: count=%d\n", connection.get(), + connection != nullptr ? connection->count : 0); } + StringAppendF(&result, " other-events-pending: %zu\n", mPendingEvents.size()); } -// --------------------------------------------------------------------------- - -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/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h new file mode 100644 index 0000000000..66f54bddd0 --- /dev/null +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -0,0 +1,198 @@ +/* + * 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 <array> +#include <condition_variable> +#include <cstdint> +#include <mutex> +#include <queue> +#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; + +// --------------------------------------------------------------------------- + +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: + explicit EventThreadConnection(EventThread* eventThread); + virtual ~EventThreadConnection(); + + virtual status_t postEvent(const DisplayEventReceiver::Event& event); + + status_t stealReceiveChannel(gui::BitTube* outChannel) override; + status_t setVsyncRate(uint32_t count) override; + void requestNextVsync() override; // asynchronous + + // 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(); + EventThread* const mEventThread; + gui::BitTube mChannel; +}; + +class EventThread { +public: + // TODO: Remove once stable display IDs are plumbed through SF/WM interface. + enum class DisplayType { Primary, External }; + + virtual ~EventThread(); + + virtual sp<EventThreadConnection> 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(DisplayType displayType, bool connected) = 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 count, const sp<EventThreadConnection>& connection) = 0; + virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0; +}; + +namespace impl { + +class EventThread : public android::EventThread, private VSyncSource::Callback { +public: + using ResyncWithRateLimitCallback = std::function<void()>; + using InterceptVSyncsCallback = std::function<void(nsecs_t)>; + using ResetIdleTimerCallback = std::function<void()>; + + // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete. + EventThread(VSyncSource* src, ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); + EventThread(std::unique_ptr<VSyncSource> src, + const ResyncWithRateLimitCallback& resyncWithRateLimitCallback, + const InterceptVSyncsCallback& interceptVSyncsCallback, + const ResetIdleTimerCallback& resetIdleTimerCallback, const char* threadName); + ~EventThread(); + + sp<EventThreadConnection> createEventConnection() const override; + + status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override; + void setVsyncRate(uint32_t count, 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; + + // called when receiving a hotplug event + void onHotplugReceived(DisplayType displayType, bool connected) override; + + void dump(std::string& result) const override; + + void setPhaseOffset(nsecs_t phaseOffset) override; + +private: + friend EventThreadTest; + + // TODO(b/113612090): Once the Scheduler is complete this constructor will become obsolete. + EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc, + ResyncWithRateLimitCallback resyncWithRateLimitCallback, + InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); + + + void threadMain(); + std::vector<sp<EventThreadConnection>> waitForEventLocked(std::unique_lock<std::mutex>* lock, + DisplayEventReceiver::Event* event) + REQUIRES(mMutex); + + void removeDisplayEventConnectionLocked(const wp<EventThreadConnection>& connection) + REQUIRES(mMutex); + void enableVSyncLocked() REQUIRES(mMutex); + void disableVSyncLocked() REQUIRES(mMutex); + + // Implements VSyncSource::Callback + void onVSyncEvent(nsecs_t timestamp) override; + + // TODO(b/113612090): 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; + // constants + const ResyncWithRateLimitCallback mResyncWithRateLimitCallback; + const InterceptVSyncsCallback mInterceptVSyncsCallback; + + std::thread mThread; + mutable std::mutex mMutex; + mutable std::condition_variable mCondition; + + // protected by mLock + std::vector<wp<EventThreadConnection>> mDisplayEventConnections GUARDED_BY(mMutex); + std::queue<DisplayEventReceiver::Event> mPendingEvents GUARDED_BY(mMutex); + std::array<DisplayEventReceiver::Event, 2> mVSyncEvent 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; + + // Callback that resets the idle timer when the next vsync is received. + ResetIdleTimerCallback mResetIdleTimer; +}; + +// --------------------------------------------------------------------------- + +} // namespace impl +} // namespace android diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp new file mode 100644 index 0000000000..5a76dbc4ff --- /dev/null +++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp @@ -0,0 +1,79 @@ +/* + * 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 TimeoutCallback& timeoutCallback) + : mInterval(interval), 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() { + std::lock_guard<std::mutex> lock(mMutex); + while (mState != TimerState::STOPPED) { + if (mState == TimerState::IDLE) { + mCondition.wait(mMutex); + } else if (mState == TimerState::RESET) { + mState = TimerState::WAITING; + if (mCondition.wait_for(mMutex, mInterval) == std::cv_status::timeout) { + if (mTimeoutCallback) { + mTimeoutCallback(); + } + } + if (mState == TimerState::WAITING) { + mState = TimerState::IDLE; + } + } + } +} + +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 0000000000..aee3fa35e5 --- /dev/null +++ b/services/surfaceflinger/Scheduler/IdleTimer.h @@ -0,0 +1,70 @@ +/* + * 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 TimeoutCallback = std::function<void()>; + + IdleTimer(const Interval& interval, const TimeoutCallback& timeoutCallback); + ~IdleTimer(); + + void start(); + void stop(); + void reset(); + +private: + // Enum to track in what state is the timer. + enum class TimerState { STOPPED = 0, RESET = 1, WAITING = 2, IDLE = 3 }; + + // Function that loops until the condition for stopping is met. + void loop(); + + // Thread waiting for timer to expire. + std::thread mThread; + + // Condition used to notify mThread. + std::condition_variable_any mCondition; + + // Lock used for synchronizing the waiting thread with the application thread. + std::mutex mMutex; + + TimerState mState GUARDED_BY(mMutex) = TimerState::RESET; + + // Interval after which timer expires. + const Interval mInterval; + + // Callback that happens when timer 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 0000000000..a0e14478ab --- /dev/null +++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h @@ -0,0 +1,53 @@ +/* + * 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 {} + +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 0000000000..d5ccbe186b --- /dev/null +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LayerHistory.h" + +#include <cinttypes> +#include <cstdint> +#include <numeric> +#include <string> +#include <unordered_map> + +#include <utils/Log.h> +#include <utils/Timers.h> +#include <utils/Trace.h> + +#include "SchedulerUtils.h" + +namespace android { + +LayerHistory::LayerHistory() {} + +LayerHistory::~LayerHistory() = default; + +void LayerHistory::insert(const std::string layerName, nsecs_t presentTime) { + mElements[mCounter].insert(std::make_pair(layerName, presentTime)); +} + +void LayerHistory::incrementCounter() { + mCounter++; + mCounter = mCounter % scheduler::ARRAY_SIZE; + // Clear all the previous data from the history. This is a ring buffer, so we are + // reusing memory. + mElements[mCounter].clear(); +} + +const std::unordered_map<std::string, nsecs_t>& LayerHistory::get(size_t index) const { + // For the purposes of the layer history, the index = 0 always needs to start at the + // current counter, and then decrement to access the layers in correct historical order. + return mElements.at((scheduler::ARRAY_SIZE + (mCounter - (index % scheduler::ARRAY_SIZE))) % + scheduler::ARRAY_SIZE); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h new file mode 100644 index 0000000000..c6fab07c87 --- /dev/null +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -0,0 +1,64 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <array> +#include <cinttypes> +#include <cstdint> +#include <numeric> +#include <string> +#include <unordered_map> + +#include <utils/Timers.h> + +#include "SchedulerUtils.h" + +namespace android { + +/* + * This class represents a circular buffer in which we keep layer history for + * the past ARRAY_SIZE frames. Each time, a signal for new frame comes, the counter + * gets incremented and includes all the layers that are requested to draw in that + * frame. + * + * Once the buffer reaches the end of the array, it starts overriding the elements + * at the beginning of the array. + */ +class LayerHistory { +public: + LayerHistory(); + ~LayerHistory(); + + // Method for inserting layers and their requested present time into the ring buffer. + // The elements are going to be inserted into an unordered_map at the position 'now'. + void insert(const std::string layerName, nsecs_t presentTime); + // Method for incrementing the current slot in the ring buffer. It also clears the + // unordered_map, if it was created previously. + void incrementCounter(); + // Returns unordered_map at the given at index. The index is decremented from 'now'. For + // example, 0 is now, 1 is previous frame. + const std::unordered_map<std::string, nsecs_t>& get(size_t index) const; + // Returns the total size of the ring buffer. The value is always the same regardless + // of how many slots we filled in. + static constexpr size_t getSize() { return scheduler::ARRAY_SIZE; } + +private: + size_t mCounter = 0; + std::array<std::unordered_map<std::string, nsecs_t>, scheduler::ARRAY_SIZE> mElements; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index 056d381eb9..36403cc346 100644 --- a/services/surfaceflinger/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -101,6 +101,17 @@ void MessageQueue::setEventThread(android::EventThread* eventThread) { 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); +} + void MessageQueue::waitMessage() { do { IPCThreadState::self()->flushCommands(); diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 90d1c72450..24a383422a 100644 --- a/services/surfaceflinger/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -85,7 +85,9 @@ public: virtual ~MessageQueue(); virtual void init(const sp<SurfaceFlinger>& flinger) = 0; + // TODO(akrulec): Remove this function once everything is migrated to Scheduler. virtual void setEventThread(EventThread* events) = 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 @@ class MessageQueue final : public android::MessageQueue { sp<SurfaceFlinger> mFlinger; sp<Looper> mLooper; android::EventThread* mEventThread; - sp<IDisplayEventConnection> mEvents; + sp<EventThreadConnection> mEvents; gui::BitTube mEventTube; sp<Handler> mHandler; @@ -125,6 +127,7 @@ public: ~MessageQueue() override = default; void init(const sp<SurfaceFlinger>& flinger) override; void setEventThread(android::EventThread* events) override; + void setEventConnection(const sp<EventThreadConnection>& connection) override; void waitMessage() override; status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) override; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp new file mode 100644 index 0000000000..c363ba57f7 --- /dev/null +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -0,0 +1,340 @@ +/* + * 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 <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h> +#include <configstore/Utils.h> + +#include <cutils/properties.h> +#include <gui/ISurfaceComposer.h> +#include <ui/DisplayStatInfo.h> +#include <utils/Timers.h> +#include <utils/Trace.h> + +#include "DispSync.h" +#include "DispSyncSource.h" +#include "EventControlThread.h" +#include "EventThread.h" +#include "IdleTimer.h" +#include "InjectVSyncSource.h" +#include "SchedulerUtils.h" + +namespace android { + +using namespace android::hardware::configstore; +using namespace android::hardware::configstore::V1_0; + +#define RETURN_VALUE_IF_INVALID(value) \ + if (handle == nullptr || mConnections.count(handle->id) == 0) return value +#define RETURN_IF_INVALID() \ + if (handle == nullptr || mConnections.count(handle->id) == 0) return + +std::atomic<int64_t> Scheduler::sNextId = 0; + +Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function) + : mHasSyncFramework( + getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasSyncFramework>(true)), + mDispSyncPresentTimeOffset( + getInt64<ISurfaceFlingerConfigs, + &ISurfaceFlingerConfigs::presentTimeOffsetFromVSyncNs>(0)), + mPrimaryHWVsyncEnabled(false), + mHWVsyncAvailable(false) { + // Note: We create a local temporary with the real DispSync implementation + // type temporarily so we can initialize it with the configured values, + // before storing it for more generic use using the interface type. + auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync"); + primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset); + mPrimaryDispSync = std::move(primaryDispSync); + mEventControlThread = std::make_unique<impl::EventControlThread>(function); + + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.set_idle_timer_ms", value, "0"); + mSetIdleTimerMs = atoi(value); + + if (mSetIdleTimerMs > 0) { + mIdleTimer = + std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs), + [this] { expiredTimerCallback(); }); + mIdleTimer->start(); + } +} + +Scheduler::~Scheduler() = default; + +sp<Scheduler::ConnectionHandle> Scheduler::createConnection( + const std::string& connectionName, int64_t phaseOffsetNs, + impl::EventThread::ResyncWithRateLimitCallback 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, resyncCallback, + interceptCallback); + auto connection = std::make_unique<Connection>(new ConnectionHandle(id), + eventThread->createEventConnection(), + std::move(eventThread)); + mConnections.insert(std::make_pair(id, std::move(connection))); + return mConnections[id]->handle; +} + +std::unique_ptr<EventThread> Scheduler::makeEventThread( + const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs, + impl::EventThread::ResyncWithRateLimitCallback resyncCallback, + impl::EventThread::InterceptVSyncsCallback interceptCallback) { + const std::string sourceName = connectionName + "Source"; + std::unique_ptr<VSyncSource> eventThreadSource = + std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, true, sourceName.c_str()); + const std::string threadName = connectionName + "Thread"; + return std::make_unique<impl::EventThread>(std::move(eventThreadSource), resyncCallback, + interceptCallback, [this] { resetIdleTimer(); }, + threadName.c_str()); +} + +sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( + const sp<Scheduler::ConnectionHandle>& handle) { + RETURN_VALUE_IF_INVALID(nullptr); + return mConnections[handle->id]->thread->createEventConnection(); +} + +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, + EventThread::DisplayType displayType, bool connected) { + RETURN_IF_INVALID(); + mConnections[handle->id]->thread->onHotplugReceived(displayType, 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::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::setVsyncPeriod(const nsecs_t period) { + mPrimaryDispSync->reset(); + mPrimaryDispSync->setPeriod(period); + enableHardwareVsync(); +} + +void Scheduler::addResyncSample(const nsecs_t timestamp) { + bool needsHwVsync = false; + { // Scope for the lock + std::lock_guard<std::mutex> lock(mHWVsyncLock); + if (mPrimaryHWVsyncEnabled) { + needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp); + } + } + + if (needsHwVsync) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } +} + +void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { + if (mPrimaryDispSync->addPresentFence(fenceTime)) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } +} + +void Scheduler::setIgnorePresentFences(bool ignore) { + mPrimaryDispSync->setIgnorePresentFences(ignore); +} + +void Scheduler::makeHWSyncAvailable(bool makeAvailable) { + std::lock_guard<std::mutex> lock(mHWVsyncLock); + mHWVsyncAvailable = makeAvailable; +} + +void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp, + const std::string layerName) { + // This is V1 logic. It calculates the average FPS based on the timestamp frequency + // regardless of which layer the timestamp came from. + // For now, the averages and FPS are recorded in the systrace. + determineTimestampAverage(isAutoTimestamp, framePresentTime); + + // This is V2 logic. It calculates the average and median timestamp difference based on the + // individual layer history. The results are recorded in the systrace. + determineLayerTimestampStats(layerName, framePresentTime); +} + +void Scheduler::incrementFrameCounter() { + mLayerHistory.incrementCounter(); +} + +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::determineLayerTimestampStats(const std::string layerName, + const nsecs_t framePresentTime) { + mLayerHistory.insert(layerName, framePresentTime); + std::vector<int64_t> differencesMs; + + // Traverse through the layer history, and determine the differences in present times. + nsecs_t newestPresentTime = framePresentTime; + std::string differencesText = ""; + for (int i = 1; i < mLayerHistory.getSize(); i++) { + std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i); + for (auto layer : layers) { + if (layer.first != layerName) { + continue; + } + int64_t differenceMs = (newestPresentTime - layer.second) / 1000000; + // Dismiss noise. + if (differenceMs > 10 && differenceMs < 60) { + differencesMs.push_back(differenceMs); + } + IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); } + newestPresentTime = layer.second; + } + } + ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str()); + + if (!differencesMs.empty()) { + // Mean/Average is a good indicator for when 24fps videos are playing, because the frames + // come in 33, and 49 ms intervals with occasional 41ms. + const int64_t meanMs = scheduler::calculate_mean(differencesMs); + const auto tagMean = "TimestampMean_" + layerName; + ATRACE_INT(tagMean.c_str(), meanMs); + + // Mode and median are good indicators for 30 and 60 fps videos, because the majority of + // frames come in 16, or 33 ms intervals. + const auto tagMedian = "TimestampMedian_" + layerName; + ATRACE_INT(tagMedian.c_str(), scheduler::calculate_median(&differencesMs)); + + const auto tagMode = "TimestampMode_" + layerName; + ATRACE_INT(tagMode.c_str(), scheduler::calculate_mode(differencesMs)); + } +} + +void Scheduler::determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime) { + ATRACE_INT("AutoTimestamp", isAutoTimestamp); + + // Video does not have timestamp automatically set, so we discard timestamps that are + // coming in from other sources for now. + if (isAutoTimestamp) { + return; + } + int64_t differenceMs = (framePresentTime - mPreviousFrameTimestamp) / 1000000; + mPreviousFrameTimestamp = framePresentTime; + + if (differenceMs < 10 || differenceMs > 100) { + // Dismiss noise. + return; + } + ATRACE_INT("TimestampDiff", differenceMs); + + mTimeDifferences[mCounter % scheduler::ARRAY_SIZE] = differenceMs; + mCounter++; + int64_t mean = scheduler::calculate_mean(mTimeDifferences); + ATRACE_INT("AutoTimestampMean", mean); + + // TODO(b/113612090): This are current numbers from trial and error while running videos + // from YouTube at 24, 30, and 60 fps. + if (mean > 14 && mean < 18) { + ATRACE_INT("FPS", 60); + } else if (mean > 31 && mean < 34) { + ATRACE_INT("FPS", 30); + return; + } else if (mean > 39 && mean < 42) { + ATRACE_INT("FPS", 24); + } +} + +void Scheduler::resetIdleTimer() { + if (mIdleTimer) { + mIdleTimer->reset(); + ATRACE_INT("ExpiredIdleTimer", 0); + } +} + +void Scheduler::expiredTimerCallback() { + // TODO(b/113612090): Each time a timer expired, we should record the information into + // a circular buffer. Once this has happened a given amount (TBD) of times, we can comfortably + // say that the device is sitting in idle. + ATRACE_INT("ExpiredIdleTimer", 1); +} + +} // namespace android diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h new file mode 100644 index 0000000000..9d7dd4df64 --- /dev/null +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -0,0 +1,178 @@ +/* + * 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 <memory> + +#include <gui/ISurfaceComposer.h> +#include <ui/DisplayStatInfo.h> + +#include "DispSync.h" +#include "EventControlThread.h" +#include "EventThread.h" +#include "IdleTimer.h" +#include "InjectVSyncSource.h" +#include "LayerHistory.h" +#include "SchedulerUtils.h" + +namespace android { + +class EventControlThread; + +class Scheduler { +public: + // 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; + }; + + explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function); + + virtual ~Scheduler(); + + /** Creates an EventThread connection. */ + sp<ConnectionHandle> createConnection( + const std::string& connectionName, int64_t phaseOffsetNs, + impl::EventThread::ResyncWithRateLimitCallback resyncCallback, + impl::EventThread::InterceptVSyncsCallback interceptCallback); + + sp<IDisplayEventConnection> createDisplayEventConnection(const sp<ConnectionHandle>& handle); + + // Getter methods. + EventThread* getEventThread(const sp<ConnectionHandle>& handle); + + sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle); + + // Should be called when receiving a hotplug event. + void hotplugReceived(const sp<ConnectionHandle>& handle, EventThread::DisplayType displayType, + 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 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 setVsyncPeriod(const nsecs_t period); + void addResyncSample(const nsecs_t timestamp); + void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime); + void setIgnorePresentFences(bool ignore); + void makeHWSyncAvailable(bool makeAvailable); + // Adds the present time for given layer to the history of present times. + void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp, + const std::string layerName); + // Increments counter in the layer history to indicate that SF has started a new frame. + void incrementFrameCounter(); + +protected: + virtual std::unique_ptr<EventThread> makeEventThread( + const std::string& connectionName, DispSync* dispSync, int64_t phaseOffsetNs, + impl::EventThread::ResyncWithRateLimitCallback resyncCallback, + impl::EventThread::InterceptVSyncsCallback interceptCallback); + +private: + nsecs_t calculateAverage() const; + void updateFrameSkipping(const int64_t skipCount); + // Collects the statistical mean (average) and median between timestamp + // intervals for each frame for each layer. + void determineLayerTimestampStats(const std::string layerName, const nsecs_t framePresentTime); + // Collects the average difference between timestamps for each frame regardless + // of which layer the timestamp came from. + void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime); + // Function that resets the idle timer. + void resetIdleTimer(); + // Function that is called when the timer expires. + void expiredTimerCallback(); + + // TODO(b/113612090): Instead of letting BufferQueueLayer to access mDispSync directly, it + // should make request to Scheduler to compute next refresh. + friend class BufferQueueLayer; + + // If fences from sync Framework are supported. + const bool mHasSyncFramework; + + // The offset in nanoseconds to use, when DispSync timestamps present fence + // signaling time. + const 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); + + 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; + + 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; +}; + +} // namespace android diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp new file mode 100644 index 0000000000..191022dbbf --- /dev/null +++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "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); +} + +int64_t calculate_mode(const std::vector<int64_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, int64_t> 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 std::max_element(counts.begin(), counts.end(), compareCounts)->first; +} + +} // namespace scheduler +} // namespace android diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h new file mode 100644 index 0000000000..17c57db375 --- /dev/null +++ b/services/surfaceflinger/Scheduler/SchedulerUtils.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 <cinttypes> +#include <numeric> +#include <vector> + +namespace android { +namespace scheduler { +// This number is used to set the size of the arrays in scheduler that hold information +// about layers. +static constexpr size_t ARRAY_SIZE = 30; + +// Calculates the statistical mean (average) in the data structure (array, vector). The +// function does not modify the contents of the array. +template <typename T> +auto calculate_mean(const T& v) { + using V = typename T::value_type; + V sum = std::accumulate(v.begin(), v.end(), 0); + return sum / static_cast<V>(v.size()); +} + +// Calculates the statistical median in the vector. Return 0 if the vector is empty. The +// function modifies the vector contents. +int64_t calculate_median(std::vector<int64_t>* v); + +// Calculates the statistical mode in the vector. Return 0 if the vector is empty. +int64_t calculate_mode(const std::vector<int64_t>& v); + +} // namespace scheduler +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h index e071a599c9..dde0c57f72 100644 --- a/services/surfaceflinger/VSyncModulator.h +++ b/services/surfaceflinger/Scheduler/VSyncModulator.h @@ -29,23 +29,16 @@ namespace android { */ 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 @@ -63,27 +56,31 @@ public: mOffsets = late; } - Offsets getEarlyOffsets() const { - return mEarlyOffsets; - } + Offsets getEarlyOffsets() const { return mEarlyOffsets; } - Offsets getEarlyGlOffsets() const { - return mEarlyGlOffsets; - } + Offsets getEarlyGlOffsets() const { return mEarlyGlOffsets; } void setEventThreads(EventThread* sfEventThread, EventThread* appEventThread) { mSfEventThread = sfEventThread; mAppEventThread = appEventThread; } - void setTransactionStart(TransactionStart transactionStart) { + void setSchedulerAndHandles(Scheduler* scheduler, + Scheduler::ConnectionHandle* appConnectionHandle, + Scheduler::ConnectionHandle* sfConnectionHandle) { + mScheduler = scheduler; + mAppConnectionHandle = appConnectionHandle; + mSfConnectionHandle = sfConnectionHandle; + } - if (transactionStart == TransactionStart::EARLY) { + void setTransactionStart(Scheduler::TransactionStart transactionStart) { + if (transactionStart == Scheduler::TransactionStart::EARLY) { mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT; } // An early transaction stays an early transaction. - if (transactionStart == mTransactionStart || mTransactionStart == TransactionStart::EARLY) { + if (transactionStart == mTransactionStart || + mTransactionStart == Scheduler::TransactionStart::EARLY) { return; } mTransactionStart = transactionStart; @@ -91,8 +88,8 @@ public: } void onTransactionHandled() { - if (mTransactionStart == TransactionStart::NORMAL) return; - mTransactionStart = TransactionStart::NORMAL; + if (mTransactionStart == Scheduler::TransactionStart::NORMAL) return; + mTransactionStart = Scheduler::TransactionStart::NORMAL; updateOffsets(); } @@ -112,18 +109,25 @@ public: } private: - void updateOffsets() { const Offsets desired = getOffsets(); const Offsets current = mOffsets; bool changed = false; if (desired.sf != current.sf) { - mSfEventThread->setPhaseOffset(desired.sf); + if (mSfConnectionHandle != nullptr) { + mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf); + } else { + mSfEventThread->setPhaseOffset(desired.sf); + } changed = true; } if (desired.app != current.app) { - mAppEventThread->setPhaseOffset(desired.app); + if (mSfConnectionHandle != nullptr) { + mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app); + } else { + mAppEventThread->setPhaseOffset(desired.app); + } changed = true; } @@ -133,7 +137,8 @@ private: } Offsets getOffsets() { - if (mTransactionStart == TransactionStart::EARLY || mRemainingEarlyFrameCount > 0) { + if (mTransactionStart == Scheduler::TransactionStart::EARLY || + mRemainingEarlyFrameCount > 0) { return mEarlyOffsets; } else if (mLastFrameUsedRenderEngine) { return mEarlyGlOffsets; @@ -149,9 +154,14 @@ private: EventThread* mSfEventThread = nullptr; EventThread* mAppEventThread = nullptr; + Scheduler* mScheduler = nullptr; + Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr; + Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr; + std::atomic<Offsets> mOffsets; - std::atomic<TransactionStart> mTransactionStart = TransactionStart::NORMAL; + std::atomic<Scheduler::TransactionStart> mTransactionStart = + Scheduler::TransactionStart::NORMAL; std::atomic<bool> mLastFrameUsedRenderEngine = false; std::atomic<int> mRemainingEarlyFrameCount = 0; }; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 82bf19f12f..4a93be6fb4 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -37,6 +37,8 @@ #include <dvr/vr_flinger.h> +#include <input/IInputFlinger.h> + #include <ui/ColorSpace.h> #include <ui/DebugUtils.h> #include <ui/DisplayInfo.h> @@ -45,9 +47,10 @@ #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 <renderengine/RenderEngine.h> #include <ui/GraphicBufferAllocator.h> #include <ui/PixelFormat.h> #include <ui/UiConfig.h> @@ -63,56 +66,103 @@ #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 "DdmConnection.h" -#include "DispSync.h" #include "DisplayDevice.h" -#include "EventControlThread.h" -#include "EventThread.h" #include "Layer.h" #include "LayerVector.h" #include "MonitoredProducer.h" +#include "NativeWindowSurface.h" +#include "StartPropertySetThread.h" #include "SurfaceFlinger.h" -#include "clz.h" +#include "SurfaceInterceptor.h" #include "DisplayHardware/ComposerHal.h" +#include "DisplayHardware/DisplayIdentification.h" #include "DisplayHardware/FramebufferSurface.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/VirtualDisplaySurface.h" - #include "Effects/Daltonizer.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/DispSyncSource.h" +#include "Scheduler/EventControlThread.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/InjectVSyncSource.h" +#include "Scheduler/MessageQueue.h" +#include "Scheduler/Scheduler.h" +#include "TimeStats/TimeStats.h" -#include "RenderEngine/RenderEngine.h" #include <cutils/compiler.h> +#include "android-base/stringprintf.h" + #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> +#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> #include <configstore/Utils.h> #include <layerproto/LayerProtoParser.h> -#define DISPLAY_COUNT 1 - -/* - * DEBUG_SCREENSHOTS: set to true to check that screenshots are not all - * black pixels. - */ -#define DEBUG_SCREENSHOTS false - namespace android { using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; +using base::StringAppendF; using ui::ColorMode; using ui::Dataspace; using ui::Hdr; using ui::RenderIntent; namespace { + +#pragma clang diagnostic push +#pragma clang diagnostic error "-Wswitch-enum" + +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; +} + +ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) { + switch (rotation) { + case ISurfaceComposer::eRotateNone: + return ui::Transform::ROT_0; + case ISurfaceComposer::eRotate90: + return ui::Transform::ROT_90; + case ISurfaceComposer::eRotate180: + return ui::Transform::ROT_180; + case ISurfaceComposer::eRotate270: + return ui::Transform::ROT_270; + } + ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation); + return ui::Transform::ROT_0; +} + +#pragma clang diagnostic pop + class ConditionalLock { public: ConditionalLock(Mutex& mutex, bool lock) : mMutex(mutex), mLocked(lock) { @@ -125,6 +175,12 @@ private: 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 // --------------------------------------------------------------------------- @@ -143,9 +199,14 @@ 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] = {}; @@ -175,32 +236,6 @@ std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { } } -NativeWindowSurface::~NativeWindowSurface() = default; - -namespace impl { - -class NativeWindowSurface final : public android::NativeWindowSurface { -public: - static std::unique_ptr<android::NativeWindowSurface> create( - const sp<IGraphicBufferProducer>& producer) { - return std::make_unique<NativeWindowSurface>(producer); - } - - explicit NativeWindowSurface(const sp<IGraphicBufferProducer>& producer) - : surface(new Surface(producer, false)) {} - - ~NativeWindowSurface() override = default; - -private: - sp<ANativeWindow> getNativeWindow() const override { return surface; } - - void preallocateBuffers() override { surface->allocateBuffers(); } - - sp<Surface> surface; -}; - -} // namespace impl - SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()), mRenderEngine(nullptr), @@ -210,16 +245,15 @@ SurfaceFlingerBE::SurfaceFlingerBE() mComposerSequenceId(0) { } -SurfaceFlinger::SurfaceFlinger(SurfaceFlinger::SkipInitializationTag) +SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory, + SurfaceFlinger::SkipInitializationTag) : BnSurfaceComposer(), - mTransactionFlags(0), + mFactory(factory), mTransactionPending(false), mAnimTransactionPending(false), mLayersRemoved(false), mLayersAdded(false), - mRepaintEverything(0), mBootTime(systemTime()), - mBuiltinDisplays(), mVisibleRegionsDirty(false), mGeometryInvalid(false), mAnimCompositionPending(false), @@ -228,22 +262,20 @@ SurfaceFlinger::SurfaceFlinger(SurfaceFlinger::SkipInitializationTag) mDebugDDMS(0), mDebugDisableHWC(0), mDebugDisableTransformHint(0), - mDebugInSwapBuffers(0), - mLastSwapBufferTime(0), mDebugInTransaction(0), mLastTransactionTime(0), mForceFullDamage(false), - mPrimaryDispSync("PrimaryDispSync"), + mTimeStats(factory.createTimeStats()), mPrimaryHWVsyncEnabled(false), mHWVsyncAvailable(false), + mRefreshStartTime(0), mHasPoweredOff(false), mNumLayers(0), mVrFlingerRequestsDisplay(false), - mMainThreadId(std::this_thread::get_id()), - mCreateBufferQueue(&BufferQueue::createBufferQueue), - mCreateNativeWindowSurface(&impl::NativeWindowSurface::create) {} + mMainThreadId(std::this_thread::get_id()) {} -SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { +SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory) + : SurfaceFlinger(factory, SkipInitialization) { ALOGI("SurfaceFlinger is starting"); vsyncPhaseOffsetNs = getInt64< ISurfaceFlingerConfigs, @@ -273,28 +305,51 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { hasWideColorDisplay = getBool<ISurfaceFlingerConfigs, &ISurfaceFlingerConfigs::hasWideColorDisplay>(false); + useColorManagement = + getBool<V1_2::ISurfaceFlingerConfigs, + &V1_2::ISurfaceFlingerConfigs::useColorManagement>(false); + + auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService(); + if (surfaceFlingerConfigsServiceV1_2) { + surfaceFlingerConfigsServiceV1_2->getCompositionPreference( + [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat, + auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) { + defaultCompositionDataspace = tmpDefaultDataspace; + defaultCompositionPixelFormat = tmpDefaultPixelFormat; + wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace; + wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat; + }); + } + mDefaultCompositionDataspace = defaultCompositionDataspace; + mWideColorGamutCompositionDataspace = wideColorGamutCompositionDataspace; + + useContextPriority = getBool<ISurfaceFlingerConfigs, + &ISurfaceFlingerConfigs::useContextPriority>(true); V1_1::DisplayOrientation primaryDisplayOrientation = - getDisplayOrientation< V1_1::ISurfaceFlingerConfigs, &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>( + getDisplayOrientation<V1_1::ISurfaceFlingerConfigs, + &V1_1::ISurfaceFlingerConfigs::primaryDisplayOrientation>( V1_1::DisplayOrientation::ORIENTATION_0); switch (primaryDisplayOrientation) { case V1_1::DisplayOrientation::ORIENTATION_90: - mPrimaryDisplayOrientation = DisplayState::eOrientation90; + SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90; break; case V1_1::DisplayOrientation::ORIENTATION_180: - mPrimaryDisplayOrientation = DisplayState::eOrientation180; + SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180; break; case V1_1::DisplayOrientation::ORIENTATION_270: - mPrimaryDisplayOrientation = DisplayState::eOrientation270; + SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270; break; default: - mPrimaryDisplayOrientation = DisplayState::eOrientationDefault; + SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault; break; } - ALOGV("Primary Display Orientation is set to %2d.", mPrimaryDisplayOrientation); + ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation); - mPrimaryDispSync.init(SurfaceFlinger::hasSyncFramework, SurfaceFlinger::dispSyncPresentTimeOffset); + mPrimaryDispSync = + getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework, + SurfaceFlinger::dispSyncPresentTimeOffset); // debugging stuff... char value[PROPERTY_VALUE_MAX]; @@ -322,9 +377,9 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { 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"); @@ -344,6 +399,9 @@ SurfaceFlinger::SurfaceFlinger() : SurfaceFlinger(SkipInitialization) { property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1"); const int earlyGlAppOffsetNs = atoi(value); + property_get("debug.sf.use_scheduler", value, "0"); + mUseScheduler = atoi(value); + const VSyncModulator::Offsets earlyOffsets = {earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs, earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs}; @@ -433,38 +491,57 @@ sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, 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); + std::optional<DisplayId> displayId; + + if (id == HWC_DISPLAY_PRIMARY) { + displayId = getInternalDisplayId(); + } else if (id == HWC_DISPLAY_EXTERNAL) { + displayId = getExternalDisplayId(); + } + + if (!displayId) { + ALOGE("%s: Invalid display %d", __FUNCTION__, id); return nullptr; } - return mBuiltinDisplays[id]; + + return getPhysicalDisplayToken(*displayId); +} + +status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const { + if (!outGetColorManagement) { + return BAD_VALUE; + } + *outGetColorManagement = useColorManagement; + return NO_ERROR; } void SurfaceFlinger::bootFinished() @@ -482,6 +559,13 @@ void SurfaceFlinger::bootFinished() if (window != 0) { window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); } + sp<IBinder> input(defaultServiceManager()->getService( + String16("inputflinger"))); + if (input == nullptr) { + ALOGE("Failed to link to input service"); + } else { + mInputFlinger = interface_cast<IInputFlinger>(input); + } if (mVrFlinger) { mVrFlinger->OnBootFinished(); @@ -496,11 +580,10 @@ void SurfaceFlinger::bootFinished() LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - sp<LambdaMessage> readProperties = new LambdaMessage([&]() { + postMessageAsync(new LambdaMessage([this] { readPersistentProperties(); mBootStage = BootStage::FINISHED; - }); - postMessageAsync(readProperties); + })); } uint32_t SurfaceFlinger::getNewTexture() { @@ -525,151 +608,9 @@ uint32_t SurfaceFlinger::getNewTexture() { } void SurfaceFlinger::deleteTextureAsync(uint32_t texture) { - class MessageDestroyGLTexture : public MessageBase { - RE::RenderEngine& engine; - uint32_t texture; - public: - MessageDestroyGLTexture(RE::RenderEngine& engine, uint32_t texture) - : engine(engine), texture(texture) {} - virtual bool handler() { - engine.deleteTextures(1, &texture); - return true; - } - }; - postMessageAsync(new MessageDestroyGLTexture(getRenderEngine(), texture)); + postMessageAsync(new LambdaMessage([=] { getRenderEngine().deleteTextures(1, &texture); })); } -class DispSyncSource final : public VSyncSource, private DispSync::Callback { -public: - DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, - const char* name) : - mName(name), - mValue(0), - mTraceVsync(traceVsync), - mVsyncOnLabel(String8::format("VsyncOn-%s", name)), - mVsyncEventLabel(String8::format("VSYNC-%s", name)), - mDispSync(dispSync), - mCallbackMutex(), - mVsyncMutex(), - mPhaseOffset(phaseOffset), - mEnabled(false) {} - - ~DispSyncSource() override = default; - - void setVSyncEnabled(bool enable) override { - Mutex::Autolock lock(mVsyncMutex); - if (enable) { - status_t err = mDispSync->addEventListener(mName, mPhaseOffset, - static_cast<DispSync::Callback*>(this)); - if (err != NO_ERROR) { - ALOGE("error registering vsync callback: %s (%d)", - strerror(-err), err); - } - //ATRACE_INT(mVsyncOnLabel.string(), 1); - } else { - status_t err = mDispSync->removeEventListener( - static_cast<DispSync::Callback*>(this)); - if (err != NO_ERROR) { - ALOGE("error unregistering vsync callback: %s (%d)", - strerror(-err), err); - } - //ATRACE_INT(mVsyncOnLabel.string(), 0); - } - mEnabled = enable; - } - - void setCallback(VSyncSource::Callback* callback) override{ - Mutex::Autolock lock(mCallbackMutex); - mCallback = callback; - } - - void setPhaseOffset(nsecs_t phaseOffset) override { - Mutex::Autolock lock(mVsyncMutex); - - // Normalize phaseOffset to [0, period) - auto period = mDispSync->getPeriod(); - phaseOffset %= period; - if (phaseOffset < 0) { - // If we're here, then phaseOffset is in (-period, 0). After this - // operation, it will be in (0, period) - phaseOffset += period; - } - mPhaseOffset = phaseOffset; - - // If we're not enabled, we don't need to mess with the listeners - if (!mEnabled) { - return; - } - - status_t err = mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), - mPhaseOffset); - if (err != NO_ERROR) { - ALOGE("error changing vsync offset: %s (%d)", - strerror(-err), err); - } - } - -private: - virtual void onDispSyncEvent(nsecs_t when) { - VSyncSource::Callback* callback; - { - Mutex::Autolock lock(mCallbackMutex); - callback = mCallback; - - if (mTraceVsync) { - mValue = (mValue + 1) % 2; - ATRACE_INT(mVsyncEventLabel.string(), mValue); - } - } - - if (callback != nullptr) { - callback->onVSyncEvent(when); - } - } - - const char* const mName; - - int mValue; - - const bool mTraceVsync; - const String8 mVsyncOnLabel; - const String8 mVsyncEventLabel; - - DispSync* mDispSync; - - Mutex mCallbackMutex; // Protects the following - VSyncSource::Callback* mCallback = nullptr; - - Mutex mVsyncMutex; // Protects the following - nsecs_t mPhaseOffset; - bool mEnabled; -}; - -class InjectVSyncSource final : public VSyncSource { -public: - InjectVSyncSource() = default; - ~InjectVSyncSource() override = default; - - void setCallback(VSyncSource::Callback* callback) override { - std::lock_guard<std::mutex> lock(mCallbackMutex); - mCallback = callback; - } - - void onInjectSyncEvent(nsecs_t when) { - std::lock_guard<std::mutex> lock(mCallbackMutex); - if (mCallback) { - mCallback->onVSyncEvent(when); - } - } - - void setVSyncEnabled(bool) override {} - void setPhaseOffset(nsecs_t) override {} - -private: - std::mutex mCallbackMutex; // Protects the following - VSyncSource::Callback* mCallback = nullptr; -}; - // Do not call property_set on main thread which will be blocked by init // Use StartPropertySetThread instead. void SurfaceFlinger::init() { @@ -681,74 +622,99 @@ void SurfaceFlinger::init() { 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"); - - 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()); + if (mUseScheduler) { + mScheduler = getFactory().createScheduler([this](bool enabled) { + setVsyncEnabled(EventThread::DisplayType::Primary, enabled); + }); + + mAppConnectionHandle = + mScheduler->createConnection("appConnection", SurfaceFlinger::vsyncPhaseOffsetNs, + [this] { resyncWithRateLimit(); }, + impl::EventThread::InterceptVSyncsCallback()); + mSfConnectionHandle = + mScheduler->createConnection("sfConnection", SurfaceFlinger::sfVsyncPhaseOffsetNs, + [this] { resyncWithRateLimit(); }, + [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }); + + mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); + mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(), + mSfConnectionHandle.get()); + } else { + mEventThreadSource = + std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), + SurfaceFlinger::vsyncPhaseOffsetNs, true, "app"); + mEventThread = + std::make_unique<impl::EventThread>(mEventThreadSource.get(), + [this] { resyncWithRateLimit(); }, + impl::EventThread::InterceptVSyncsCallback(), + "appEventThread"); + mSfEventThreadSource = + std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), + SurfaceFlinger::sfVsyncPhaseOffsetNs, true, "sf"); + + 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()); + } // Get a RenderEngine for the given display / config (can't fail) + int32_t renderEngineFeature = 0; + renderEngineFeature |= (useColorManagement ? + renderengine::RenderEngine::USE_COLOR_MANAGEMENT : 0); + renderEngineFeature |= (useContextPriority ? + renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT : 0); + + // TODO(b/77156734): We need to stop casting and use HAL types when possible. getBE().mRenderEngine = - RE::impl::RenderEngine::create(HAL_PIXEL_FORMAT_RGBA_8888, - hasWideColorDisplay - ? RE::RenderEngine::WIDE_COLOR_SUPPORT - : 0); + renderengine::RenderEngine::create(static_cast<int32_t>(defaultCompositionPixelFormat), + renderEngineFeature); LOG_ALWAYS_FATAL_IF(getBE().mRenderEngine == nullptr, "couldn't create RenderEngine"); 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 = getFactory().createHWComposer(getBE().mHwcServiceName); getBE().mHwc->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); }); + mEventControlThread = getFactory().createEventControlThread( + [this](bool enabled) { setVsyncEnabled(EventThread::DisplayType::Primary, enabled); }); // initialize our drawing state mDrawingState = mCurrentState; @@ -759,35 +725,15 @@ void SurfaceFlinger::init() { getBE().mRenderEngine->primeCache(); // Inform native graphics APIs whether the present timestamp is supported: - if (getHwComposer().hasCapability( - HWC2::Capability::PresentFenceIsNotReliable)) { - mStartPropertySetThread = new StartPropertySetThread(false); - } else { - mStartPropertySetThread = new StartPropertySetThread(true); - } + + const bool presentFenceReliable = + !getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable); + mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable); if (mStartPropertySetThread->Start() != NO_ERROR) { ALOGE("Run StartPropertySetThread failed!"); } - // This is a hack. Per definition of getDataspaceSaturationMatrix, the returned matrix - // is used to saturate legacy sRGB content. However, to make sure the same color under - // Display P3 will be saturated to the same color, we intentionally break the API spec - // and apply this saturation matrix on Display P3 content. Unless the risk of applying - // such saturation matrix on Display P3 is understood fully, the API should always return - // identify matrix. - mEnhancedSaturationMatrix = getBE().mHwc->getDataspaceSaturationMatrix(HWC_DISPLAY_PRIMARY, - Dataspace::SRGB_LINEAR); - - // we will apply this on Display P3. - if (mEnhancedSaturationMatrix != mat4()) { - ColorSpace srgb(ColorSpace::sRGB()); - ColorSpace displayP3(ColorSpace::DisplayP3()); - mat4 srgbToP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform()); - mat4 p3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform()); - mEnhancedSaturationMatrix = srgbToP3 * mEnhancedSaturationMatrix * p3ToSrgb; - } - ALOGV("Done initializing"); } @@ -858,41 +804,31 @@ status_t SurfaceFlinger::getSupportedFrameTimestamps( 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()) + const auto displayId = getPhysicalDisplayId(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"); } }; @@ -900,13 +836,19 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, 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 == getInternalDisplayId()) { // The density of the device is provided by a build property float density = Density::getBuildDensity() / 160.0f; if (density == 0) { @@ -923,8 +865,15 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, 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; @@ -932,8 +881,6 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, info.orientation = 0; } - info.w = hwConfig->getWidth(); - info.h = hwConfig->getHeight(); info.xdpi = xdpi; info.ydpi = ydpi; info.fps = 1e9 / hwConfig->getVsyncPeriod(); @@ -957,8 +904,8 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, // All non-virtual displays are currently considered secure. info.secure = true; - if (type == DisplayDevice::DISPLAY_PRIMARY && - mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) { + if (displayId == getInternalDisplayId() && + primaryDisplayOrientation & DisplayState::eOrientationSwapMask) { std::swap(info.w, info.h); } @@ -968,129 +915,87 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display, return NO_ERROR; } -status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */, - DisplayStatInfo* stats) { - if (stats == nullptr) { - 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(); - return NO_ERROR; -} - -status_t SurfaceFlinger::getDisplayViewport(const sp<IBinder>& display, Rect* outViewport) { - if (outViewport == nullptr || display.get() == nullptr) { +status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* stats) { + if (!stats) { return BAD_VALUE; } - sp<const DisplayDevice> device(getDisplayDevice(display)); - if (device == nullptr) { - return BAD_VALUE; + if (mUseScheduler) { + mScheduler->getDisplayStatInfo(stats); + } else { + stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0); + stats->vsyncPeriod = mPrimaryDispSync->getPeriod(); } - - *outViewport = device->getViewport(); - return NO_ERROR; } -int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) { - if (display == nullptr) { - ALOGE("%s : display is nullptr", __func__); +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 device->getActiveConfig(); - } - - return BAD_VALUE; + return display->getActiveConfig(); } -void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode) { - ALOGD("Set active config mode=%d, type=%d flinger=%p", mode, hw->getDisplayType(), - this); - int32_t type = hw->getDisplayType(); - int currentMode = hw->getActiveConfig(); - - if (mode == currentMode) { - ALOGD("Screen type=%d is already mode=%d", hw->getDisplayType(), mode); +void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& display, int mode) { + if (display->isVirtual()) { + ALOGE("%s: Invalid operation on virtual display", __FUNCTION__); return; } - if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { - ALOGW("Trying to set config for virtual display"); + int currentMode = display->getActiveConfig(); + if (mode == currentMode) { return; } - hw->setActiveConfig(mode); - getHwComposer().setActiveConfig(type, mode); + const auto displayId = display->getId(); + LOG_ALWAYS_FATAL_IF(!displayId); + + display->setActiveConfig(mode); + getHwComposer().setActiveConfig(*displayId, mode); } -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); - } - return true; +status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) { + postMessageSync(new LambdaMessage([&] { + Vector<DisplayInfo> configs; + getDisplayConfigs(displayToken, &configs); + if (mode < 0 || mode >= static_cast<int>(configs.size())) { + ALOGE("Attempt to set active config %d for display with %zu configs", mode, + configs.size()); + return; } - }; - sp<MessageBase> msg = new MessageSetActiveConfig(*this, display, mode); - postMessageSync(msg); + const auto display = getDisplayDevice(displayToken); + if (!display) { + ALOGE("Attempt to set active config %d for invalid display token %p", mode, + displayToken.get()); + } else if (display->isVirtual()) { + ALOGW("Attempt to set active config %d for virtual display", mode); + } else { + setActiveConfigInternal(display, mode); + } + })); + return NO_ERROR; } -status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display, - Vector<ColorMode>* outColorModes) { - if ((outColorModes == nullptr) || (display.get() == nullptr)) { + +status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken, + Vector<ColorMode>* outColorModes) { + if (!displayToken || !outColorModes) { return BAD_VALUE; } - if (!display.get()) { + const auto displayId = getPhysicalDisplayId(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; - } - std::vector<ColorMode> modes; { ConditionalLock _l(mStateLock, std::this_thread::get_id() != mMainThreadId); - modes = getHwComposer().getColorModes(type); + modes = getHwComposer().getColorModes(*displayId); } outColorModes->clear(); std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes)); @@ -1098,79 +1003,65 @@ status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& display, return NO_ERROR; } -ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& display) { - sp<const DisplayDevice> device(getDisplayDevice(display)); - if (device != nullptr) { - return device->getActiveColorMode(); +ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) { + if (const auto display = getDisplayDevice(displayToken)) { + return display->getActiveColorMode(); } 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(); +void SurfaceFlinger::setActiveColorModeInternal(const sp<DisplayDevice>& display, ColorMode mode, + Dataspace dataSpace, RenderIntent renderIntent) { + if (display->isVirtual()) { + ALOGE("%s: Invalid operation on virtual display", __FUNCTION__); + return; + } + + ColorMode currentMode = display->getActiveColorMode(); + Dataspace currentDataSpace = display->getCompositionDataSpace(); + RenderIntent currentRenderIntent = display->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; - } + display->setActiveColorMode(mode); + display->setCompositionDataSpace(dataSpace); + display->setActiveRenderIntent(renderIntent); - hw->setActiveColorMode(mode); - hw->setCompositionDataSpace(dataSpace); - hw->setActiveRenderIntent(renderIntent); - getHwComposer().setActiveColorMode(type, mode, renderIntent); + const auto displayId = display->getId(); + LOG_ALWAYS_FATAL_IF(!displayId); + getHwComposer().setActiveColorMode(*displayId, 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()); + ALOGV("Set active color mode: %s (%d), active render intent: %s (%d), display=%s", + decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(), + renderIntent, to_string(*displayId).c_str()); } - -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 { + setActiveColorModeInternal(display, mode, Dataspace::UNKNOWN, + RenderIntent::COLORIMETRIC); + } + })); + return NO_ERROR; } @@ -1186,20 +1077,20 @@ status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const { 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(), @@ -1208,21 +1099,64 @@ status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& display, 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::enableVSyncInjections(bool enable) { - sp<LambdaMessage> enableVSyncInjections = new LambdaMessage([&]() { + postMessageSync(new LambdaMessage([&] { Mutex::Autolock _l(mStateLock); if (mInjectVSyncs == enable) { return; } + // TODO(akrulec): 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>(mVSyncInjector.get(), [this] { resyncWithRateLimit(); }, impl::EventThread::InterceptVSyncsCallback(), "injEventThread"); } @@ -1233,8 +1167,8 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { } mInjectVSyncs = enable; - }); - postMessageSync(enableVSyncInjections); + })); + return NO_ERROR; } @@ -1254,15 +1188,6 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { 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); @@ -1280,14 +1205,33 @@ status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayer 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; +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource) { - if (vsyncSource == eVsyncSourceSurfaceFlinger) { - return mSFEventThread->createEventConnection(); + if (mUseScheduler) { + if (vsyncSource == eVsyncSourceSurfaceFlinger) { + return mScheduler->createDisplayEventConnection(mSfConnectionHandle); + } else { + return mScheduler->createDisplayEventConnection(mAppConnectionHandle); + } } else { - return mEventThread->createEventConnection(); + if (vsyncSource == eVsyncSourceSurfaceFlinger) { + return mSFEventThread->createEventConnection(); + } else { + return mEventThread->createEventConnection(); + } } } @@ -1333,8 +1277,7 @@ void SurfaceFlinger::run() { void SurfaceFlinger::enableHardwareVsync() { Mutex::Autolock _l(mHWVsyncLock); if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { - mPrimaryDispSync.beginResync(); - //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); + mPrimaryDispSync->beginResync(); mEventControlThread->setVsyncEnabled(true); mPrimaryHWVsyncEnabled = true; } @@ -1345,32 +1288,43 @@ void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable) { if (makeAvailable) { mHWVsyncAvailable = true; + // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good. + if (mUseScheduler) { + mScheduler->makeHWSyncAvailable(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 auto displayId = getInternalDisplayId(); + if (!displayId || !getHwComposer().isConnected(*displayId)) { + return; + } + + const auto activeConfig = getHwComposer().getActiveConfig(*displayId); const nsecs_t period = activeConfig->getVsyncPeriod(); - mPrimaryDispSync.reset(); - mPrimaryDispSync.setPeriod(period); + if (mUseScheduler) { + mScheduler->setVsyncPeriod(period); + } else { + mPrimaryDispSync->reset(); + mPrimaryDispSync->setPeriod(period); - if (!mPrimaryHWVsyncEnabled) { - mPrimaryDispSync.beginResync(); - //eventControl(HWC_DISPLAY_PRIMARY, SurfaceFlinger::EVENT_VSYNC, true); - mEventControlThread->setVsyncEnabled(true); - mPrimaryHWVsyncEnabled = true; + if (!mPrimaryHWVsyncEnabled) { + mPrimaryDispSync->beginResync(); + 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(); + mPrimaryDispSync->endResync(); mPrimaryHWVsyncEnabled = false; } if (makeUnavailable) { @@ -1390,32 +1344,41 @@ void SurfaceFlinger::resyncWithRateLimit() { sLastResyncAttempted = now; } -void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, - hwc2_display_t displayId, int64_t timestamp) { +void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, + int64_t timestamp) { + ATRACE_NAME("SF onVsync"); + 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(); + if (mUseScheduler) { + mScheduler->addResyncSample(timestamp); } else { - disableHardwareVsync(false); + bool needsHwVsync = false; + { // Scope for the lock + Mutex::Autolock _l(mHWVsyncLock); + if (mPrimaryHWVsyncEnabled) { + needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp); + } + } + + if (needsHwVsync) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } } } @@ -1424,9 +1387,9 @@ void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { *compositorTiming = getBE().mCompositorTiming; } -void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t display, +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. @@ -1440,7 +1403,7 @@ void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t displa // 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. @@ -1450,8 +1413,7 @@ void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t displa 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; @@ -1459,22 +1421,27 @@ void SurfaceFlinger::onRefreshReceived(int sequenceId, repaintEverything(); } -void SurfaceFlinger::setVsyncEnabled(int disp, int enabled) { +void SurfaceFlinger::setVsyncEnabled(EventThread::DisplayType /*displayType*/, bool enabled) { ATRACE_CALL(); Mutex::Autolock lock(mStateLock); - getHwComposer().setVsyncEnabled(disp, - enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable); + if (const auto displayId = getInternalDisplayId()) { + 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); + if (mUseScheduler) { + mScheduler->disableHardwareVsync(true); + } else { + 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(); } @@ -1496,8 +1463,13 @@ void SurfaceFlinger::updateVrFlinger() { 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(); + // 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(); @@ -1505,8 +1477,8 @@ void SurfaceFlinger::updateVrFlinger() { 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 = getFactory().createHWComposer( + vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName); getBE().mHwc->registerCallback(this, ++getBE().mComposerSequenceId); LOG_ALWAYS_FATAL_IF(!getBE().mHwc->getComposer()->isRemote(), @@ -1514,28 +1486,38 @@ void SurfaceFlinger::updateVrFlinger() { 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 auto activeConfig = getHwComposer().getActiveConfig(*display->getId()); const nsecs_t period = activeConfig->getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(period); + // The present fences returned from vr_hwc are not an accurate + // representation of vsync times. + if (mUseScheduler) { + mScheduler->setIgnorePresentFences(getBE().mHwc->isUsingVrComposer() || !hasSyncFramework); + } else { + mPrimaryDispSync->setIgnorePresentFences(getBE().mHwc->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 */, period /* vsyncPeriod */}; + setCompositorTimingSnapped(stats, 0); + + resyncToHardwareVsync(false); - android_atomic_or(1, &mRepaintEverything); + mRepaintEverything = true; setTransactionFlags(eDisplayTransactionNeeded); } @@ -1543,13 +1525,18 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { ATRACE_CALL(); switch (what) { case MessageQueue::INVALIDATE: { + if (mUseScheduler) { + // This call is made each time SF wakes up and creates a new frame. + mScheduler->incrementFrameCounter(); + } bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE && (mPreviousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING); + mFrameMissedCount += frameMissed; ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); if (frameMissed) { - mTimeStats.incrementMissedFrames(); + mTimeStats->incrementMissedFrames(); if (mPropagateBackpressure) { signalLayerUpdate(); break; @@ -1588,84 +1575,199 @@ bool SurfaceFlinger::handleMessageTransaction() { return false; } -bool SurfaceFlinger::handleMessageInvalidate() { - ATRACE_CALL(); - return handlePageFlip(); -} - void SurfaceFlinger::handleMessageRefresh() { ATRACE_CALL(); mRefreshPending = false; - nsecs_t refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); - - preComposition(refreshStartTime); + const bool repaintEverything = mRepaintEverything.exchange(false); + preComposition(); rebuildLayerStacks(); - setUpHWComposer(); - doDebugFlashRegions(); + calculateWorkingSet(); + for (const auto& [token, display] : mDisplays) { + beginFrame(display); + prepareFrame(display); + doDebugFlashRegions(display, repaintEverything); + doComposition(display, repaintEverything); + } + doTracing("handleRefresh"); logLayerStats(); - doComposition(); - postComposition(refreshStartTime); - mPreviousPresentFence = getBE().mHwc->getPresentFence(HWC_DISPLAY_PRIMARY); + postFrame(); + postComposition(); mHadClientComposition = false; - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - const sp<DisplayDevice>& displayDevice = mDisplays[displayId]; + for (const auto& [token, display] : mDisplays) { mHadClientComposition = mHadClientComposition || - getBE().mHwc->hasClientComposition(displayDevice->getHwcDisplayId()); + getBE().mHwc->hasClientComposition(display->getId()); + } + + // Setup RenderEngine sync fences if native sync is supported. + if (getBE().mRenderEngine->useNativeFenceSync()) { + if (mHadClientComposition) { + base::unique_fd flushFence(getRenderEngine().flush()); + ALOGE_IF(flushFence < 0, "Failed to flush RenderEngine!"); + getBE().flushFence = new Fence(std::move(flushFence)); + } else { + // Cleanup for hygiene. + getBE().flushFence = Fence::NO_FENCE; + } } + mVsyncModulator.onRefreshed(mHadClientComposition); + getBE().mEndOfFrameCompositionInfo = std::move(getBE().mCompositionInfo); + for (const auto& [token, display] : mDisplays) { + for (auto& compositionInfo : getBE().mEndOfFrameCompositionInfo[token]) { + compositionInfo.hwc.hwcLayer = nullptr; + } + } + mLayersWithQueuedFrames.clear(); } -void SurfaceFlinger::doDebugFlashRegions() + +bool SurfaceFlinger::handleMessageInvalidate() { + ATRACE_CALL(); + return handlePageFlip(); +} + +void SurfaceFlinger::calculateWorkingSet() { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + // build the h/w work list + if (CC_UNLIKELY(mGeometryInvalid)) { + mGeometryInvalid = false; + for (const auto& [token, display] : mDisplays) { + const auto displayId = display->getId(); + if (!displayId) { + continue; + } + + const Vector<sp<Layer>>& currentLayers = display->getVisibleLayersSortedByZ(); + for (size_t i = 0; i < currentLayers.size(); i++) { + const auto& layer = currentLayers[i]; + + if (!layer->hasHwcLayer(*displayId)) { + if (!layer->createHwcLayer(&getHwComposer(), *displayId)) { + layer->forceClientComposition(*displayId); + continue; + } + } + + layer->setGeometry(display, i); + if (mDebugDisableHWC || mDebugRegion) { + layer->forceClientComposition(*displayId); + } + } + } + } + + // Set the per-frame data + for (const auto& [token, display] : mDisplays) { + const auto displayId = display->getId(); + if (!displayId) { + continue; + } + + if (mDrawingState.colorMatrixChanged) { + display->setColorTransform(mDrawingState.colorMatrix); + status_t result = + getHwComposer().setColorTransform(*displayId, mDrawingState.colorMatrix); + ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display %s: %d", + to_string(*displayId).c_str(), result); + } + for (auto& layer : display->getVisibleLayersSortedByZ()) { + if (layer->isHdrY410()) { + layer->forceClientComposition(*displayId); + } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ || + layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) && + !display->hasHDR10Support()) { + layer->forceClientComposition(*displayId); + } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG || + layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) && + !display->hasHLGSupport()) { + layer->forceClientComposition(*displayId); + } + + // TODO(b/111562338) remove when composer 2.3 is shipped. + if (layer->hasColorTransform()) { + layer->forceClientComposition(*displayId); + } + + if (layer->getRoundedCornerState().radius > 0.0f) { + layer->forceClientComposition(*displayId); + } + + if (layer->getForceClientComposition(*displayId)) { + ALOGV("[%s] Requesting Client composition", layer->getName().string()); + layer->setCompositionType(*displayId, HWC2::Composition::Client); + continue; + } + + layer->setPerFrameData(*displayId, display->getTransform(), display->getViewport(), + display->getSupportedPerFrameMetadata()); + } + + if (useColorManagement) { + ColorMode colorMode; + Dataspace dataSpace; + RenderIntent renderIntent; + pickColorMode(display, &colorMode, &dataSpace, &renderIntent); + setActiveColorModeInternal(display, colorMode, dataSpace, renderIntent); + } + } + + mDrawingState.colorMatrixChanged = false; + + for (const auto& [token, display] : mDisplays) { + for (auto& layer : display->getVisibleLayersSortedByZ()) { + const auto displayId = display->getId(); + layer->getBE().compositionInfo.compositionType = layer->getCompositionType(displayId); + + if (displayId) { + if (!layer->setHwcLayer(*displayId)) { + ALOGV("Need to create HWCLayer for %s", layer->getName().string()); + } + layer->getBE().compositionInfo.hwc.displayId = *displayId; + } + + getBE().mCompositionInfo[token].push_back(layer->getBE().compositionInfo); + layer->getBE().compositionInfo.hwc.hwcLayer = nullptr; + } + } +} + +void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything) { // 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); - - // and draw the dirty region - const int32_t height = hw->getHeight(); - auto& engine(getRenderEngine()); - engine.fillRegionWithColor(dirtyRegion, height, 1, 0, 1, 1); - - hw->swapBuffers(getHwComposer()); - } + if (display->isPoweredOn()) { + // transform the dirty region into this screen's coordinate space + const Region dirtyRegion(display->getDirtyRegion(repaintEverything)); + if (!dirtyRegion.isEmpty()) { + // redraw the whole screen + doComposeSurfaces(display); + + // and draw the dirty region + auto& engine(getRenderEngine()); + engine.fillRegionWithColor(dirtyRegion, 1, 0, 1, 1); + + display->queueBuffer(getHwComposer()); } } - postFramebuffer(); + postFramebuffer(display); 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)); - } + prepareFrame(display); } void SurfaceFlinger::doTracing(const char* where) { @@ -1679,30 +1781,27 @@ void SurfaceFlinger::doTracing(const char* where) { 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; } }); @@ -1712,9 +1811,8 @@ void SurfaceFlinger::preComposition(nsecs_t refreshStartTime) } } -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}); @@ -1736,21 +1834,20 @@ void SurfaceFlinger::updateCompositorTiming( 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 = (sfVsyncPhaseOffsetNs > 0) + ? (stats.vsyncPeriod - (sfVsyncPhaseOffsetNs % stats.vsyncPeriod)) + : ((-sfVsyncPhaseOffsetNs) % stats.vsyncPeriod); // Just in case sfVsyncPhaseOffsetNs == -vsyncInterval. if (idealLatency <= 0) { - idealLatency = vsyncInterval; + idealLatency = stats.vsyncPeriod; } // Snap the latency to a value that removes scheduling jitter from the @@ -1759,19 +1856,18 @@ void SurfaceFlinger::setCompositorTimingSnapped(nsecs_t vsyncPhase, // something (such as user input) to an accurate diasplay time. // Snapping also allows an app to precisely calculate sfVsyncPhaseOffsetNs // 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"); @@ -1783,31 +1879,36 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) } // |mStateLock| not needed as we are on the main thread - const sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked()); + const auto display = getDefaultDisplayDeviceLocked(); getBE().mGlCompositionDoneTimeline.updateSignalTimes(); std::shared_ptr<FenceTime> glCompositionDoneFenceTime; - if (hw && getBE().mHwc->hasClientComposition(HWC_DISPLAY_PRIMARY)) { + if (display && getHwComposer().hasClientComposition(display->getId())) { glCompositionDoneFenceTime = - std::make_shared<FenceTime>(hw->getClientTargetAcquireFence()); + std::make_shared<FenceTime>(display->getClientTargetAcquireFence()); getBE().mGlCompositionDoneTimeline.push(glCompositionDoneFenceTime); } else { glCompositionDoneFenceTime = FenceTime::NO_FENCE; } getBE().mDisplayTimeline.updateSignalTimes(); - sp<Fence> presentFence = getBE().mHwc->getPresentFence(HWC_DISPLAY_PRIMARY); - auto presentFenceTime = std::make_shared<FenceTime>(presentFence); + mPreviousPresentFence = + display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE; + auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFence); getBE().mDisplayTimeline.push(presentFenceTime); - nsecs_t vsyncPhase = mPrimaryDispSync.computeNextRefresh(0); - nsecs_t vsyncInterval = mPrimaryDispSync.getPeriod(); + DisplayStatInfo stats; + if (mUseScheduler) { + mScheduler->getDisplayStatInfo(&stats); + } else { + stats.vsyncTime = mPrimaryDispSync->computeNextRefresh(0); + stats.vsyncPeriod = mPrimaryDispSync->getPeriod(); + } - // 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); @@ -1815,8 +1916,8 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) } mDrawingState.traverseInZOrder([&](Layer* layer) { - bool frameLatched = layer->onPostComposition(glCompositionDoneFenceTime, - presentFenceTime, compositorTiming); + bool frameLatched = layer->onPostComposition(display->getId(), glCompositionDoneFenceTime, + presentFenceTime, compositorTiming); if (frameLatched) { recordBufferingStats(layer->getName().string(), layer->getOccupancyHistory(false)); @@ -1824,16 +1925,24 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) }); if (presentFenceTime->isValid()) { - if (mPrimaryDispSync.addPresentFence(presentFenceTime)) { - enableHardwareVsync(); + if (mUseScheduler) { + mScheduler->addPresentFence(presentFenceTime); } else { - disableHardwareVsync(false); + if (mPrimaryDispSync->addPresentFence(presentFenceTime)) { + enableHardwareVsync(); + } else { + disableHardwareVsync(false); + } } } if (!hasSyncFramework) { - if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY) && hw->isDisplayOn()) { - enableHardwareVsync(); + if (display && getHwComposer().isConnected(*display->getId()) && display->isPoweredOn()) { + if (mUseScheduler) { + mScheduler->enableHardwareVsync(); + } else { + enableHardwareVsync(); + } } } @@ -1843,23 +1952,23 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) if (presentFenceTime->isValid()) { mAnimFrameTracker.setActualPresentFence( std::move(presentFenceTime)); - } else if (getBE().mHwc->isConnected(HWC_DISPLAY_PRIMARY)) { + } else if (display && getHwComposer().isConnected(*display->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(*display->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 (display && getHwComposer().isConnected(*display->getId()) && !display->isPoweredOn()) { return; } @@ -1868,7 +1977,7 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) 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 { @@ -1888,33 +1997,47 @@ void SurfaceFlinger::postComposition(nsecs_t refreshStartTime) ATRACE_INT("TexturePoolSize", mTexturePool.size()); } } + + mTransactionCompletedThread.addPresentFence(mPreviousPresentFence); + mTransactionCompletedThread.sendCallbacks(); } void SurfaceFlinger::rebuildLayerStacks() { ATRACE_CALL(); ALOGV("rebuildLayerStacks"); + // We need to clear these out now as these may be holding on to a + // HWC2::Layer reference at the same time as the LayerBE::HWCInfo structure + // also holds a reference. When the set of visible layers is recomputed, + // some layers may be destroyed if the only thing keeping them alive was + // that list of visible layers associated with each display. The layer + // destruction code asserts that the HWC2::Layer is properly destroyed, but + // that doesn't happen if SurfaceFlingerBE::mCompositionInfo keeps it alive. + for (const auto& [token, display] : mDisplays) { + getBE().mCompositionInfo[token].clear(); + } + // rebuild the visible layer list per screen if (CC_UNLIKELY(mVisibleRegionsDirty)) { ATRACE_NAME("rebuildLayerStacks VR Dirty"); mVisibleRegionsDirty = false; invalidateHwcGeometry(); - for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { + for (const auto& pair : mDisplays) { + const auto& display = pair.second; Region opaqueRegion; Region dirtyRegion; Vector<sp<Layer>> layersSortedByZ; Vector<sp<Layer>> layersNeedingFences; - const sp<DisplayDevice>& displayDevice(mDisplays[dpy]); - const Transform& tr(displayDevice->getTransform()); - const Rect bounds(displayDevice->getBounds()); - if (displayDevice->isDisplayOn()) { - computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion); + const ui::Transform& tr = display->getTransform(); + const Rect bounds = display->getBounds(); + if (display->isPoweredOn()) { + computeVisibleRegions(display, dirtyRegion, opaqueRegion); mDrawingState.traverseInZOrder([&](Layer* layer) { bool hwcLayerDestroyed = false; - if (layer->belongsToDisplay(displayDevice->getLayerStack(), - displayDevice->isPrimary())) { + const auto displayId = display->getId(); + if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) { Region drawRegion(tr.transform( layer->visibleNonTransparentRegion)); drawRegion.andSelf(bounds); @@ -1923,15 +2046,13 @@ void SurfaceFlinger::rebuildLayerStacks() { } else { // Clear out the HWC layer if this layer was // previously visible, but no longer is - hwcLayerDestroyed = layer->destroyHwcLayer( - displayDevice->getHwcDisplayId()); + hwcLayerDestroyed = displayId && layer->destroyHwcLayer(*displayId); } } else { - // WM changes displayDevice->layerStack upon sleep/awake. + // WM changes display->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()); + hwcLayerDestroyed = displayId && layer->destroyHwcLayer(*displayId); } // If a layer is not going to get a release fence because @@ -1947,12 +2068,11 @@ void SurfaceFlinger::rebuildLayerStacks() { } }); } - displayDevice->setVisibleLayersSortedByZ(layersSortedByZ); - displayDevice->setLayersNeedingFences(layersNeedingFences); - displayDevice->undefinedRegion.set(bounds); - displayDevice->undefinedRegion.subtractSelf( - tr.transform(opaqueRegion)); - displayDevice->dirtyRegion.orSelf(dirtyRegion); + display->setVisibleLayersSortedByZ(layersSortedByZ); + display->setLayersNeedingFences(layersNeedingFences); + display->undefinedRegion.set(bounds); + display->undefinedRegion.subtractSelf(tr.transform(opaqueRegion)); + display->dirtyRegion.orSelf(dirtyRegion); } } } @@ -1961,19 +2081,26 @@ void SurfaceFlinger::rebuildLayerStacks() { // can only be one of // - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced) // - Dataspace::DISPLAY_P3 +// - Dataspace::DISPLAY_BT2020 // The returned HDR data space is one of // - Dataspace::UNKNOWN // - Dataspace::BT2020_HLG // - Dataspace::BT2020_PQ -Dataspace SurfaceFlinger::getBestDataspace( - const sp<const DisplayDevice>& displayDevice, Dataspace* outHdrDataSpace) const { - Dataspace bestDataSpace = Dataspace::SRGB; +Dataspace SurfaceFlinger::getBestDataspace(const sp<const DisplayDevice>& display, + Dataspace* outHdrDataSpace) const { + Dataspace bestDataSpace = Dataspace::V0_SRGB; *outHdrDataSpace = Dataspace::UNKNOWN; - for (const auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + for (const auto& layer : display->getVisibleLayersSortedByZ()) { switch (layer->getDataSpace()) { case Dataspace::V0_SCRGB: case Dataspace::V0_SCRGB_LINEAR: + case Dataspace::BT2020: + case Dataspace::BT2020_ITU: + case Dataspace::BT2020_LINEAR: + case Dataspace::DISPLAY_BT2020: + bestDataSpace = Dataspace::DISPLAY_BT2020; + break; case Dataspace::DISPLAY_P3: bestDataSpace = Dataspace::DISPLAY_P3; break; @@ -1998,9 +2125,8 @@ Dataspace SurfaceFlinger::getBestDataspace( } // 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; @@ -2009,11 +2135,11 @@ void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice, } Dataspace hdrDataSpace; - Dataspace bestDataSpace = getBestDataspace(displayDevice, &hdrDataSpace); + Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace); // respect hdrDataSpace only when there is no legacy HDR support const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN && - !displayDevice->hasLegacyHdrSupport(hdrDataSpace); + !display->hasLegacyHdrSupport(hdrDataSpace); if (isHdr) { bestDataSpace = hdrDataSpace; } @@ -2032,177 +2158,101 @@ void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& displayDevice, break; } - displayDevice->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent); + display->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent); } -void SurfaceFlinger::setUpHWComposer() { - ATRACE_CALL(); - ALOGV("setUpHWComposer"); - - 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; - - // 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 ? "+" : "-"); +void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& display) +{ + bool dirty = !display->getDirtyRegion(false).isEmpty(); + bool empty = display->getVisibleLayersSortedByZ().size() == 0; + bool wasEmpty = !display->lastCompositionHadVisibleLayers; - mDisplays[dpy]->beginFrame(mustRecompose); + // 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 (mustRecompose) { - mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty; - } - } + const char flagPrefix[] = {'-', '+'}; + static_cast<void>(flagPrefix); + ALOGV_IF(display->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", + __FUNCTION__, mustRecompose ? "doing" : "skipping", display->getDebugName().c_str(), + flagPrefix[dirty], flagPrefix[empty], flagPrefix[wasEmpty]); - // 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; - } - } + display->beginFrame(mustRecompose); - layer->setGeometry(displayDevice, i); - if (mDebugDisableHWC || mDebugRegion) { - layer->forceClientComposition(hwcId); - } - } - } - } + if (mustRecompose) { + display->lastCompositionHadVisibleLayers = !empty; } +} - // 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); - } +void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& display) +{ + if (!display->isPoweredOn()) { + return; } - 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)); - } + status_t result = display->prepareFrame(getHwComposer(), + getBE().mCompositionInfo[display->getDisplayToken()]); + ALOGE_IF(result != NO_ERROR, "prepareFrame failed for %s: %d (%s)", + display->getDebugName().c_str(), result, strerror(-result)); } -void SurfaceFlinger::doComposition() { +void SurfaceFlinger::doComposition(const sp<DisplayDevice>& display, 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)); + if (display->isPoweredOn()) { + // transform the dirty region into this screen's coordinate space + const Region dirtyRegion(display->getDirtyRegion(repaintEverything)); - // repaint the framebuffer (if needed) - doDisplayComposition(hw, dirtyRegion); + // repaint the framebuffer (if needed) + doDisplayComposition(display, dirtyRegion); - hw->dirtyRegion.clear(); - hw->flip(); + display->dirtyRegion.clear(); + display->flip(); + } + postFramebuffer(display); +} + +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(); } } - postFramebuffer(); } -void SurfaceFlinger::postFramebuffer() +void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& display) { ATRACE_CALL(); ALOGV("postFramebuffer"); - const nsecs_t now = systemTime(); - mDebugInSwapBuffers = now; + mPostFramebufferTime = systemTime(); - for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { - auto& displayDevice = mDisplays[displayId]; - if (!displayDevice->isDisplayOn()) { - continue; - } - const auto hwcId = displayDevice->getHwcDisplayId(); - if (hwcId >= 0) { - getBE().mHwc->presentAndGetReleaseFences(hwcId); + if (display->isPoweredOn()) { + const auto displayId = display->getId(); + if (displayId) { + getHwComposer().presentAndGetReleaseFences(*displayId); } - displayDevice->onSwapBuffersCompleted(); - displayDevice->makeCurrent(); - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + display->onPresentDisplayCompleted(); + for (auto& layer : display->getVisibleLayersSortedByZ()) { sp<Fence> releaseFence = Fence::NO_FENCE; // The layer buffer from the previous frame (if any) is released // by HWC only when the release fence from this frame (if any) is // signaled. Always get the release fence from HWC first. - auto hwcLayer = layer->getHwcLayer(hwcId); - if (hwcId >= 0) { - releaseFence = getBE().mHwc->getLayerReleaseFence(hwcId, hwcLayer); + if (displayId && layer->hasHwcLayer(*displayId)) { + releaseFence = getHwComposer().getLayerReleaseFence(*displayId, + layer->getHwcLayer(*displayId)); } // If the layer was client composited in the previous frame, we @@ -2210,37 +2260,27 @@ void SurfaceFlinger::postFramebuffer() // 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) { + if (layer->getCompositionType(displayId) == HWC2::Composition::Client) { releaseFence = Fence::merge("LayerRelease", releaseFence, - displayDevice->getClientTargetAcquireFence()); + display->getClientTargetAcquireFence()); } - layer->onLayerDisplayed(releaseFence); + layer->getBE().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); - for (auto& layer : displayDevice->getLayersNeedingFences()) { - layer->onLayerDisplayed(presentFence); + if (!display->getLayersNeedingFences().isEmpty()) { + sp<Fence> presentFence = + displayId ? getBE().mHwc->getPresentFence(*displayId) : Fence::NO_FENCE; + for (auto& layer : display->getLayersNeedingFences()) { + layer->getBE().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); } } } @@ -2275,68 +2315,36 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) // 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); - continue; - } + const std::optional<DisplayIdentificationInfo> info = + getHwComposer().onHotplug(event.hwcDisplayId, event.connection); - if (getBE().mHwc->isUsingVrComposer() && displayType == DisplayDevice::DISPLAY_EXTERNAL) { - ALOGE("External displays are not supported by the vr hardware composer."); + if (!info) { 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(); @@ -2346,70 +2354,57 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { } 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; - - if (hasWideColorDisplay && hwcId >= 0) { - std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId); + const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId, + const DisplayDeviceState& state, const sp<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 == getInternalDisplayId(); + 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); } + 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, - hasWideColorGamut, hdrCapabilities, - supportedPerFrameMetadata, hwcColorModes, initialPowerMode); + sp<DisplayDevice> display = getFactory().createDisplayDevice(std::move(creationArgs)); if (maxFrameBufferAcquiredBuffers >= 3) { nativeWindowSurface->preallocateBuffers(); @@ -2417,20 +2412,22 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( 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, + setActiveColorModeInternal(display, defaultColorMode, defaultDataSpace, RenderIntent::COLORIMETRIC); - if (state.type < DisplayDevice::DISPLAY_VIRTUAL) { - hw->setActiveConfig(getHwComposer().getActiveConfigIndex(state.type)); + 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() { @@ -2451,21 +2448,34 @@ void SurfaceFlinger::processDisplayChangesLocked() { for (size_t i = 0; i < dc;) { const ssize_t j = curr.indexOfKey(draw.keyAt(i)); if (j < 0) { + // Save display IDs before disconnecting. + const auto internalDisplayId = getInternalDisplayId(); + const auto externalDisplayId = getExternalDisplayId(); + // 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))) { + display->disconnect(getHwComposer()); + } + if (internalDisplayId && internalDisplayId == draw[i].displayId) { + if (mUseScheduler) { + mScheduler->hotplugReceived(mAppConnectionHandle, + EventThread::DisplayType::Primary, false); + } else { + mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, false); + } + } else if (externalDisplayId && externalDisplayId == draw[i].displayId) { + if (mUseScheduler) { + mScheduler->hotplugReceived(mAppConnectionHandle, + EventThread::DisplayType::External, false); + } else { + mEventThread->onHotplugReceived(EventThread::DisplayType::External, 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) { @@ -2473,26 +2483,26 @@ void SurfaceFlinger::processDisplayChangesLocked() { // 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(getHwComposer()); + } + 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); } } } @@ -2509,16 +2519,16 @@ void SurfaceFlinger::processDisplayChangesLocked() { 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); @@ -2530,13 +2540,14 @@ void SurfaceFlinger::processDisplayChangesLocked() { 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); @@ -2549,18 +2560,39 @@ void SurfaceFlinger::processDisplayChangesLocked() { "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); + + if (displayId == getInternalDisplayId()) { + if (mUseScheduler) { + mScheduler->hotplugReceived(mAppConnectionHandle, + EventThread::DisplayType::Primary, + true); + } else { + mEventThread->onHotplugReceived(EventThread::DisplayType::Primary, + true); + } + } else if (displayId == getExternalDisplayId()) { + if (mUseScheduler) { + mScheduler->hotplugReceived(mAppConnectionHandle, + EventThread::DisplayType::External, + true); + } else { + mEventThread->onHotplugReceived(EventThread::DisplayType::External, + true); + } + } } } } @@ -2582,6 +2614,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) * (perform the transaction for each of them if needed) */ + bool inputChanged = false; if (transactionFlags & eTraversalNeeded) { mCurrentState.traverseInZOrder([&](Layer* layer) { uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); @@ -2590,6 +2623,10 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) const uint32_t flags = layer->doTransaction(0); if (flags & Layer::eVisibleRegion) mVisibleRegionsDirty = true; + + if (flags & Layer::eInputInfoChanged) { + inputChanged = true; + } }); } @@ -2622,7 +2659,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // 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) { @@ -2635,34 +2672,33 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // 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 (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) { + 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; @@ -2700,21 +2736,55 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) commitTransaction(); + if (inputChanged || mVisibleRegionsDirty) { + updateInputWindows(); + } + updateCursorAsync(); } +void SurfaceFlinger::updateInputWindows() { + ATRACE_CALL(); + + if (mInputFlinger == nullptr) { + return; + } + + 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.add(layer->fillInputInfo( + layer->computeScreenBounds(false /* reduceTransparentRegion */))); + } + }); + mInputFlinger->setInputWindows(inputHandles); +} + 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()) { + const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime(); + if (layer->shouldPresentNow(expectedPresentTime)) { + bool ignored = false; + layer->latchBuffer(ignored, systemTime(), Fence::NO_FENCE); } } + layer->releasePendingBuffer(systemTime()); } void SurfaceFlinger::commitTransaction() @@ -2724,7 +2794,17 @@ void SurfaceFlinger::commitTransaction() for (const auto& l : mLayersPendingRemoval) { recordBufferingStats(l->getName().string(), l->getOccupancyHistory(true)); - l->onRemoved(); + + // We need to release the HWC layers when the Layer is removed + // from the current state otherwise the HWC layer just continues + // showing at its last configured state until we eventually + // abandon the buffer queue. + if (l->isRemovedFromCurrentState()) { + l->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* child) { + child->destroyHwcLayersForAllDisplays(); + latchAndReleaseBuffer(child); + }); + } } mLayersPendingRemoval.clear(); } @@ -2745,9 +2825,8 @@ void SurfaceFlinger::commitTransaction() mTransactionCV.broadcast(); } -void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice, - Region& outDirtyRegion, Region& outOpaqueRegion) -{ +void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& display, + Region& outDirtyRegion, Region& outOpaqueRegion) { ATRACE_CALL(); ALOGV("computeVisibleRegions"); @@ -2758,12 +2837,10 @@ void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displa outDirtyRegion.clear(); mDrawingState.traverseInReverseZOrder([&](Layer* layer) { - // start with the whole surface at its current location - const Layer::State& s(layer->getDrawingState()); - // only consider the layers on the given layer stack - if (!layer->belongsToDisplay(displayDevice->getLayerStack(), displayDevice->isPrimary())) + if (!layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) { return; + } /* * opaqueRegion: area of a surface that is fully opaque. @@ -2797,16 +2874,18 @@ void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displa // handle hidden surfaces by setting the visible region to empty if (CC_LIKELY(layer->isVisible())) { - const bool translucent = !layer->isOpaque(s); + const bool translucent = !layer->isDrawingOpaque(); Rect bounds(layer->computeScreenBounds()); + 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->getDrawingActiveTransparentRegion()); } else { // transformation too complex, can't do the // transparent region optimization. @@ -2817,7 +2896,8 @@ void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displa // 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; } @@ -2883,10 +2963,9 @@ void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displa } 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, display] : mDisplays) { + if (layer->belongsToDisplay(display->getLayerStack(), display->isPrimary())) { + display->dirtyRegion.orSelf(dirty); } } } @@ -2911,9 +2990,10 @@ bool SurfaceFlinger::handlePageFlip() // 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)) { + const nsecs_t expectedPresentTime = mPrimaryDispSync->expectedPresentTime(); + if (layer->shouldPresentNow(expectedPresentTime)) { mLayersWithQueuedFrames.push_back(layer); } else { layer->useEmptyDamage(); @@ -2924,7 +3004,7 @@ bool SurfaceFlinger::handlePageFlip() }); for (auto& layer : mLayersWithQueuedFrames) { - const Region dirty(layer->latchBuffer(visibleRegions, latchTime)); + const Region dirty(layer->latchBuffer(visibleRegions, latchTime, getBE().flushFence)); layer->useSurfaceDamage(); invalidateLayerStack(layer, dirty); if (layer->isBufferLatched()) { @@ -2932,8 +3012,18 @@ bool SurfaceFlinger::handlePageFlip() } } + // Clear the renderengine fence here... + // downstream code assumes that a cleared fence == NO_FENCE, so reassign to + // clear instead of sp::clear. + getBE().flushFence = Fence::NO_FENCE; + mVisibleRegionsDirty |= visibleRegions; + if (visibleRegions) { + // Update input window info if the layer receives its first buffer. + updateInputWindows(); + } + // If we will need to wake up at some time in the future to deal with a // queued frame that shouldn't be displayed during this vsync period, wake // up during the next vsync period to check again. @@ -2956,85 +3046,82 @@ void SurfaceFlinger::invalidateHwcGeometry() mGeometryInvalid = true; } - -void SurfaceFlinger::doDisplayComposition( - const sp<const DisplayDevice>& displayDevice, - const Region& inDirtyRegion) -{ +void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& display, + const Region& inDirtyRegion) { // 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 (!display->getId() && inDirtyRegion.isEmpty()) { ALOGV("Skipping display composition"); return; } ALOGV("doDisplayComposition"); - if (!doComposeSurfaces(displayDevice)) return; + if (!doComposeSurfaces(display)) return; // swap buffers (presentation) - displayDevice->swapBuffers(getHwComposer()); + display->queueBuffer(getHwComposer()); } -bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDevice) -{ +bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& display) { ALOGV("doComposeSurfaces"); - const Region bounds(displayDevice->bounds()); - const DisplayRenderArea renderArea(displayDevice); - const auto hwcId = displayDevice->getHwcDisplayId(); - const bool hasClientComposition = getBE().mHwc->hasClientComposition(hwcId); + const Region bounds(display->bounds()); + const DisplayRenderArea renderArea(display); + const auto displayId = display->getId(); + const bool hasClientComposition = getBE().mHwc->hasClientComposition(displayId); ATRACE_INT("hasClientComposition", hasClientComposition); + mat4 colorMatrix; bool applyColorMatrix = false; - bool needsEnhancedColorMatrix = false; + + // Framebuffer will live in this scope for GPU composition. + std::unique_ptr<renderengine::BindNativeBufferAsFramebuffer> fbo; if (hasClientComposition) { ALOGV("hasClientComposition"); + sp<GraphicBuffer> buf = display->dequeueBuffer(); + + if (buf == nullptr) { + ALOGW("Dequeuing buffer for display [%s] failed, bailing out of " + "client composition for this frame", + display->getDisplayName().c_str()); + return false; + } + + // Bind the framebuffer in this scope. + fbo = std::make_unique<renderengine::BindNativeBufferAsFramebuffer>(getRenderEngine(), + buf->getNativeBuffer()); + + if (fbo->getStatus() != NO_ERROR) { + ALOGW("Binding buffer for display [%s] failed with status: %d", + display->getDisplayName().c_str(), fbo->getStatus()); + return false; + } + Dataspace outputDataspace = Dataspace::UNKNOWN; - if (displayDevice->hasWideColorGamut()) { - outputDataspace = displayDevice->getCompositionDataSpace(); + if (display->hasWideColorGamut()) { + outputDataspace = display->getCompositionDataSpace(); } getBE().mRenderEngine->setOutputDataSpace(outputDataspace); getBE().mRenderEngine->setDisplayMaxLuminance( - displayDevice->getHdrCapabilities().getDesiredMaxLuminance()); + display->getHdrCapabilities().getDesiredMaxLuminance()); - const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(hwcId); - const bool skipClientColorTransform = getBE().mHwc->hasCapability( - HWC2::Capability::SkipClientColorTransform); + const bool hasDeviceComposition = getBE().mHwc->hasDeviceComposition(displayId); + const bool skipClientColorTransform = + getBE().mHwc + ->hasDisplayCapability(displayId, + HWC2::DisplayCapability::SkipClientColorTransform); - mat4 colorMatrix; + // Compute the global color transform matrix. 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."); - } - return false; - } + display->setViewportAndProjection(); // Never touch the framebuffer if we don't have any framebuffer layers if (hasDeviceComposition) { @@ -3048,29 +3135,27 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev // 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())); + const Region letterbox = bounds.subtract(display->getScissor()); // compute the area to clear - Region region(displayDevice->undefinedRegion.merge(letterbox)); + const Region region = display->undefinedRegion.merge(letterbox); // screen is already cleared here if (!region.isEmpty()) { // can happen with SurfaceView - drawWormhole(displayDevice, region); + drawWormhole(region); } } - const Rect& bounds(displayDevice->getBounds()); - const Rect& scissor(displayDevice->getScissor()); + const Rect& bounds = display->getBounds(); + const Rect& scissor = display->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 // enable scissor for this frame - const uint32_t height = displayDevice->getHeight(); - getBE().mRenderEngine->setScissor(scissor.left, height - scissor.bottom, - scissor.getWidth(), scissor.getHeight()); + getBE().mRenderEngine->setScissor(scissor); } } @@ -3079,24 +3164,23 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev */ ALOGV("Rendering client layers"); - const Transform& displayTransform = displayDevice->getTransform(); + const ui::Transform& displayTransform = display->getTransform(); bool firstLayer = true; - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + for (auto& layer : display->getVisibleLayersSortedByZ()) { const Region clip(bounds.intersect( displayTransform.transform(layer->visibleRegion))); ALOGV("Layer: %s", layer->getName().string()); - ALOGV(" Composition type: %s", - to_string(layer->getCompositionType(hwcId)).c_str()); + ALOGV(" Composition type: %s", to_string(layer->getCompositionType(displayId)).c_str()); if (!clip.isEmpty()) { - switch (layer->getCompositionType(hwcId)) { + switch (layer->getCompositionType(displayId)) { case HWC2::Composition::Cursor: case HWC2::Composition::Device: case HWC2::Composition::Sideband: case HWC2::Composition::SolidColor: { - const Layer::State& state(layer->getDrawingState()); - if (layer->getClearClientTarget(hwcId) && !firstLayer && - layer->isOpaque(state) && (state.color.a == 1.0f) - && hasClientComposition) { + LOG_ALWAYS_FATAL_IF(!displayId); + if (layer->getClearClientTarget(*displayId) && !firstLayer && + layer->isDrawingOpaque() && (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); @@ -3104,6 +3188,16 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev break; } case HWC2::Composition::Client: { + if (layer->hasColorTransform()) { + mat4 tmpMatrix; + if (applyColorMatrix) { + tmpMatrix = mDrawingState.colorMatrix; + } + tmpMatrix *= layer->getColorTransform(); + getRenderEngine().setColorTransform(tmpMatrix); + } else { + getRenderEngine().setColorTransform(colorMatrix); + } layer->draw(renderArea, clip); break; } @@ -3116,19 +3210,21 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& displayDev firstLayer = false; } - if (applyColorMatrix || needsEnhancedColorMatrix) { - getRenderEngine().setupColorTransform(mat4()); + // Perform some cleanup steps if we used client composition. + if (hasClientComposition) { + getRenderEngine().setColorTransform(mat4()); + getBE().mRenderEngine->disableScissor(); + display->finishBuffer(); + // Clear out error flags here so that we don't wait until next + // composition to log. + getRenderEngine().checkErrors(); } - - // disable scissor at the end of the frame - getBE().mRenderEngine->disableScissor(); 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, @@ -3148,9 +3244,9 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, if (parent == nullptr) { mCurrentState.layersSortedByZ.add(lbc); } else { - if (parent->isPendingRemoval()) { + if (parent->isRemovedFromCurrentState()) { ALOGE("addClientLayer called with a removed parent"); - return NAME_NOT_FOUND; + lbc->onRemovedFromCurrentState(); } parent->addChild(lbc); } @@ -3164,7 +3260,6 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, mMaxGraphicBufferProducerListSize, mNumLayers); } mLayersAdded = true; - mNumLayers++; } // attach this layer to the client @@ -3173,75 +3268,45 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, return NO_ERROR; } -status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer, bool topLevelOnly) { +status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) { Mutex::Autolock _l(mStateLock); - return removeLayerLocked(mStateLock, layer, topLevelOnly); + return removeLayerLocked(mStateLock, layer); } -status_t SurfaceFlinger::removeLayerLocked(const Mutex&, const sp<Layer>& layer, - bool topLevelOnly) { - if (layer->isPendingRemoval()) { +status_t SurfaceFlinger::removeLayerLocked(const Mutex& lock, const sp<Layer>& layer) { + if (layer->isLayerDetached()) { 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); + + markLayerPendingRemovalLocked(lock, layer); 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(); @@ -3304,9 +3369,15 @@ void SurfaceFlinger::setTransactionState( transactionFlags |= setDisplayStateLocked(display); } + uint32_t clientStateFlags = 0; for (const ComposerState& state : states) { - transactionFlags |= setClientStateLocked(state); + clientStateFlags |= setClientStateLocked(state); + } + // If the state doesn't require a traversal and there are callbacks, send them now + if (!(clientStateFlags & eTraversalNeeded)) { + mTransactionCompletedThread.sendCallbacks(); } + transactionFlags |= clientStateFlags; // Iterate through all layers again to determine if any need to be destroyed. Marking layers // as destroyed should only occur after setting all other states. This is to allow for a @@ -3331,9 +3402,8 @@ void SurfaceFlinger::setTransactionState( } // 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 @@ -3357,53 +3427,51 @@ void SurfaceFlinger::setTransactionState( } } -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; - } + 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 (disp.layerStack != s.layerStack) { - disp.layerStack = s.layerStack; - flags |= eDisplayTransactionNeeded; - } + } + if (what & DisplayState::eLayerStackChanged) { + if (state.layerStack != s.layerStack) { + state.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::eDisplayProjectionChanged) { + if (state.orientation != s.orientation) { + state.orientation = s.orientation; + 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; - } + 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; } @@ -3411,9 +3479,8 @@ bool 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; @@ -3428,20 +3495,15 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState 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(); } @@ -3499,6 +3561,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState 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::eMatrixChanged) { // TODO: b/109894387 // @@ -3526,12 +3593,12 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState 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) { @@ -3553,15 +3620,15 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState 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"); @@ -3592,6 +3659,54 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState // 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::eBufferChanged) { + if (layer->setBuffer(s.buffer)) 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) { + layer->setInputInfo(s.inputInfo); + flags |= eTraversalNeeded; + } + std::vector<sp<CallbackHandle>> callbackHandles; + if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) { + mTransactionCompletedThread.run(); + for (const auto& [listener, callbackIds] : s.listenerCallbacks) { + callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface)); + } + } + if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded; + // Do not put anything that updates layer state or modifies flags after + // setTransactionCompletedListener return flags; } @@ -3604,11 +3719,6 @@ void SurfaceFlinger::setDestroyStateLocked(const ComposerState& composerState) { return; } - if (layer->isPendingRemoval()) { - ALOGW("Attempting to destroy on removed layer: %s", layer->getName().string()); - return; - } - if (state.what & layer_state_t::eDestroySurface) { removeLayerLocked(mStateLock, layer); } @@ -3634,18 +3744,33 @@ status_t SurfaceFlinger::createLayer( String8 uniqueName = getUniqueLayerName(name); 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, format, handle, gbp, + &layer); break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + result = createBufferStateLayer(client, uniqueName, w, h, flags, handle, &layer); + break; case ISurfaceComposerClient::eFXSurfaceColor: + // 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, handle, &layer); break; case ISurfaceComposerClient::eFXSurfaceContainer: + // 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, handle, &layer); @@ -3700,15 +3825,17 @@ String8 SurfaceFlinger::getUniqueLayerName(const String8& name) }); } - 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, + PixelFormat& format, sp<IBinder>* handle, + sp<IGraphicBufferProducer>* gbp, + sp<Layer>* outLayer) { // initialize the surfaces switch (format) { case PIXEL_FORMAT_TRANSPARENT: @@ -3720,23 +3847,35 @@ status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client, 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)); + 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::createBufferStateLayer(const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags, + sp<IBinder>* handle, sp<Layer>* outLayer) { + sp<BufferStateLayer> layer = + getFactory().createBufferStateLayer(LayerCreationArgs(this, client, name, w, h, flags)); + *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, sp<IBinder>* handle, sp<Layer>* outLayer) { - *outLayer = new ColorLayer(this, client, name, w, h, flags); + *outLayer = getFactory().createColorLayer(LayerCreationArgs(this, client, name, w, h, flags)); *handle = (*outLayer)->getHandle(); return NO_ERROR; } @@ -3745,7 +3884,8 @@ 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); + *outLayer = + getFactory().createContainerLayer(LayerCreationArgs(this, client, name, w, h, flags)); *handle = (*outLayer)->getHandle(); return NO_ERROR; } @@ -3765,29 +3905,32 @@ status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBind return err; } -status_t SurfaceFlinger::onLayerDestroyed(const wp<Layer>& layer) +void SurfaceFlinger::markLayerPendingRemovalLocked(const Mutex&, const sp<Layer>& layer) { + mLayersPendingRemoval.add(layer); + mLayersRemoved = true; + setTransactionFlags(eTransactionNeeded); +} + +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; - } - // If we have a parent, then we can continue to live as long as it does. - return removeLayer(l, true); + Mutex::Autolock lock(mStateLock); + markLayerPendingRemovalLocked(mStateLock, layer); + layer.clear(); } // --------------------------------------------------------------------------- void SurfaceFlinger::onInitializeDisplays() { + const auto displayToken = getInternalDisplayToken(); + if (!displayToken) return; + // 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 = displayToken; d.layerStack = 0; d.orientation = DisplayState::eOrientationDefault; d.frame.makeInvalid(); @@ -3796,66 +3939,58 @@ void SurfaceFlinger::onInitializeDisplays() { d.height = 0; displays.add(d); setTransactionState(state, displays, 0); - setPowerModeInternal(getDisplayDevice(d.token), HWC_POWER_MODE_NORMAL, - /*stateLockHeld*/ false); - const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); + const auto display = getDisplayDevice(displayToken); + if (!display) return; + + setPowerModeInternal(display, HWC_POWER_MODE_NORMAL); + + const auto activeConfig = getHwComposer().getActiveConfig(*display->getId()); const nsecs_t period = activeConfig->getVsyncPeriod(); mAnimFrameTracker.setDisplayRefreshPeriod(period); // 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 */, period /* 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] { 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(); - - if (mode == currentMode) { +void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) { + if (display->isVirtual()) { + ALOGE("%s: Invalid operation on virtual display", __FUNCTION__); return; } - hw->setPowerMode(mode); - if (type >= DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) { - ALOGW("Trying to set power mode for virtual display"); + 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; } + 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(); + getHwComposer().setPowerMode(*displayId, mode); + if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) { + if (mUseScheduler) { + mScheduler->onScreenAcquired(mAppConnectionHandle); + } else { + mEventThread->onScreenAcquired(); + } resyncToHardwareVsync(true); } @@ -3875,75 +4010,80 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& hw, 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) { + if (mUseScheduler) { + mScheduler->disableHardwareVsync(true); + } else { + disableHardwareVsync(true); // also cancels any in-progress resync + } + if (mUseScheduler) { + mScheduler->onScreenReleased(mAppConnectionHandle); + } else { + mEventThread->onScreenReleased(); + } } - 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(); + getHwComposer().setPowerMode(*displayId, mode); + if (display->isPrimary() && currentMode == HWC_POWER_MODE_DOZE_SUSPEND) { + if (mUseScheduler) { + mScheduler->onScreenAcquired(mAppConnectionHandle); + } else { + mEventThread->onScreenAcquired(); + } resyncToHardwareVsync(true); } } 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()) { + if (mUseScheduler) { + mScheduler->disableHardwareVsync(true); + } else { + disableHardwareVsync(true); // also cancels any in-progress resync + } + if (mUseScheduler) { + mScheduler->onScreenReleased(mAppConnectionHandle); + } else { + mEventThread->onScreenReleased(); + } } - 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); + } + + if (display->isPrimary()) { + mTimeStats->setPowerMode(mode); } - ALOGD("Finished set power mode=%d, type=%d", mode, hw->getDisplayType()); + + 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([&] { + 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; + std::string result; IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); @@ -3951,8 +4091,8 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro 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 @@ -3960,9 +4100,10 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro 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; @@ -3994,7 +4135,7 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro if ((index < numArgs) && (args[index] == String16("--dispsync"))) { index++; - mPrimaryDispSync.dump(result); + mPrimaryDispSync->dump(result); dumpAll = false; } @@ -4046,9 +4187,23 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro dumpAll = false; } + if ((index < numArgs) && + (args[index] == String16("--frame-composition"))) { + index++; + dumpFrameCompositionInfo(result); + dumpAll = false; + } + + if ((index < numArgs) && + (args[index] == String16("--display-identification"))) { + index++; + dumpDisplayIdentificationData(result); + dumpAll = false; + } + if ((index < numArgs) && (args[index] == String16("--timestats"))) { index++; - mTimeStats.parseArgs(asProto, args, index, result); + mTimeStats->parseArgs(asProto, args, index, result); dumpAll = false; } } @@ -4066,30 +4221,30 @@ status_t SurfaceFlinger::doDump(int fd, const Vector<String16>& args, bool asPro 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::listLayersLocked(const Vector<String16>& /* args */, size_t& /* index */, + std::string& result) const { + mCurrentState.traverseInZOrder( + [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); }); } void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index, - String8& result) const -{ + std::string& result) const { String8 name; if (index < args.size()) { name = String8(args[index]); index++; } - const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); - const nsecs_t period = activeConfig->getVsyncPeriod(); - result.appendFormat("%" PRId64 "\n", period); + if (const auto displayId = getInternalDisplayId(); + displayId && getHwComposer().isConnected(*displayId)) { + const auto activeConfig = getHwComposer().getActiveConfig(*displayId); + const nsecs_t period = activeConfig->getVsyncPeriod(); + StringAppendF(&result, "%" PRId64 "\n", period); + } if (name.isEmpty()) { mAnimFrameTracker.dumpStats(result); @@ -4103,8 +4258,7 @@ void SurfaceFlinger::dumpStatsLocked(const Vector<String16>& args, size_t& index } void SurfaceFlinger::clearStatsLocked(const Vector<String16>& args, size_t& index, - String8& /* result */) -{ + std::string& /* result */) { String8 name; if (index < args.size()) { name = String8(args[index]); @@ -4130,37 +4284,34 @@ void SurfaceFlinger::logFrameStats() { 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::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, @@ -4181,8 +4332,8 @@ void SurfaceFlinger::recordBufferingStats(const char* layerName, } } -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(); @@ -4191,7 +4342,7 @@ void SurfaceFlinger::dumpFrameEventsLocked(String8& result) { } } -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"); @@ -4217,41 +4368,102 @@ void SurfaceFlinger::dumpBufferingStats(String8& result) const { 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()); +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; + } + + 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; + } + + if (!isEdid(data)) { + result.append("unknown identification data: "); + for (uint8_t byte : data) { + StringAppendF(&result, "%x ", byte); + } + result.append("\n"); + continue; + } + + const auto edid = parseEdid(data); + if (!edid) { + result.append("invalid EDID: "); + for (uint8_t byte : data) { + StringAppendF(&result, "%x ", byte); + } + result.append("\n"); + continue; + } + + StringAppendF(&result, "port=%u pnpId=%s displayName=\"", port, edid->pnpId.data()); + result.append(edid->displayName.data(), edid->displayName.length()); + result.append("\"\n"); + } +} + +void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { + StringAppendF(&result, "Device has wide color display: %d\n", hasWideColorDisplay); + StringAppendF(&result, "Device uses color management: %d\n", useColorManagement); + StringAppendF(&result, "DisplayColorSetting: %s\n", + decodeDisplayColorSetting(mDisplayColorSetting).c_str()); // TODO: print out if wide-color mode is active or not - for (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 color modes:\n", hwcId); - std::vector<ColorMode> modes = getHwComposer().getColorModes(hwcId); + StringAppendF(&result, "Display %s color modes:\n", to_string(*displayId).c_str()); + std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId); for (auto&& mode : modes) { - result.appendFormat(" %s (%d)\n", decodeColorMode(mode).c_str(), mode); + StringAppendF(&result, " %s (%d)\n", decodeColorMode(mode).c_str(), mode); } - ColorMode currentMode = displayDevice->getActiveColorMode(); - result.appendFormat(" Current color mode: %s (%d)\n", - decodeColorMode(currentMode).c_str(), currentMode); + ColorMode currentMode = display->getActiveColorMode(); + StringAppendF(&result, " Current color mode: %s (%d)\n", + decodeColorMode(currentMode).c_str(), currentMode); } result.append("\n"); } +void SurfaceFlinger::dumpFrameCompositionInfo(std::string& result) const { + for (const auto& [token, display] : mDisplays) { + const auto it = getBE().mEndOfFrameCompositionInfo.find(token); + if (it == getBE().mEndOfFrameCompositionInfo.end()) { + continue; + } + + const auto& compositionInfoList = it->second; + StringAppendF(&result, "%s\n", display->getDebugName().c_str()); + StringAppendF(&result, "numComponents: %zu\n", compositionInfoList.size()); + for (const auto& compositionInfo : compositionInfoList) { + compositionInfo.dump(result, nullptr); + result.append("\n"); + } + } +} + LayersProto SurfaceFlinger::dumpProtoInfo(LayerVector::StateSet stateSet) const { LayersProto layersProto; const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; @@ -4264,23 +4476,23 @@ LayersProto SurfaceFlinger::dumpProtoInfo(LayerVector::StateSet stateSet) const return layersProto; } -LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(int32_t hwcId) const { +LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(const DisplayDevice& display) const { LayersProto layersProto; - const sp<DisplayDevice>& displayDevice(mDisplays[hwcId]); SizeProto* resolution = layersProto.mutable_resolution(); - resolution->set_w(displayDevice->getWidth()); - resolution->set_h(displayDevice->getHeight()); + resolution->set_w(display.getWidth()); + resolution->set_h(display.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())); + layersProto.set_color_mode(decodeColorMode(display.getActiveColorMode())); + layersProto.set_color_transform(decodeColorTransform(display.getColorTransform())); + layersProto.set_global_transform(static_cast<int32_t>(display.getOrientationTransform())); + const auto displayId = display.getId(); + LOG_ALWAYS_FATAL_IF(!displayId); mDrawingState.traverseInZOrder([&](Layer* layer) { - if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(hwcId)) { + if (!layer->visibleRegion.isEmpty() && layer->getBE().mHwcLayers.count(*displayId)) { LayerProto* layerProto = layersProto.add_layers(); - layer->writeToProto(layerProto, hwcId); + layer->writeToProto(layerProto, *displayId); } }); @@ -4288,8 +4500,7 @@ LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo(int32_t hwcId) const { } void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, - String8& result) const -{ + std::string& result) const { bool colorize = false; if (index < args.size() && (args[index] == String16("--color"))) { @@ -4301,9 +4512,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, // 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; /* @@ -4318,6 +4527,9 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, appendGuiConfigString(result); result.append("\n"); + result.append("\nDisplay identification data:\n"); + dumpDisplayIdentificationData(result); + result.append("\nWide-Color information:\n"); dumpWideColorInfo(result); @@ -4327,28 +4539,27 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.append(SyncFeatures::getInstance().toString()); result.append("\n"); - const auto& activeConfig = getBE().mHwc->getActiveConfig(HWC_DISPLAY_PRIMARY); - colorizer.bold(result); - result.append("DispSync configuration: "); + result.append("DispSync 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()); + if (const auto displayId = getInternalDisplayId(); + displayId && getHwComposer().isConnected(*displayId)) { + const auto activeConfig = getHwComposer().getActiveConfig(*displayId); + StringAppendF(&result, + "Display %s: 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)", + to_string(*displayId).c_str(), vsyncPhaseOffsetNs, sfVsyncPhaseOffsetNs, + appEarlyOffset, sfEarlyOffset, appEarlyGlOffset, sfEarlyGlOffset, + dispSyncPresentTimeOffset, activeConfig->getVsyncPeriod()); + } result.append("\n"); // Dump static screen stats @@ -4356,20 +4567,28 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, dumpStaticScreenStats(result); result.append("\n"); + StringAppendF(&result, "Missed frame count: %u\n\n", mFrameMissedCount.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()); + { + LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current); + auto layerTree = LayerProtoParser::generateLayerTree(layersProto); + result.append(LayerProtoParser::layerTreeToString(layerTree)); + result.append("\n"); + } + + result.append("\nFrame-Composition information:\n"); + dumpFrameCompositionInfo(result); result.append("\n"); /* @@ -4377,11 +4596,10 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, */ 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"); @@ -4393,43 +4611,40 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.append("SurfaceFlinger global state:\n"); colorizer.reset(result); - HWComposer& hwc(getHwComposer()); - sp<const DisplayDevice> hw(getDefaultDisplayDeviceLocked()); - getBE().mRenderEngine->dump(result); - if (hw) { - hw->undefinedRegion.dump(result, "undefinedRegion"); - result.appendFormat(" orientation=%d, isDisplayOn=%d\n", - hw->getOrientation(), hw->isDisplayOn()); - } - 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); - - result.appendFormat(" eglSwapBuffers time: %f us\n", - inSwapBuffersDuration/1000.0); - - result.appendFormat(" transaction time: %f us\n", - inTransactionDuration/1000.0); + if (const auto display = getDefaultDisplayDeviceLocked()) { + display->undefinedRegion.dump(result, "undefinedRegion"); + StringAppendF(&result, " orientation=%d, isPoweredOn=%d\n", display->getOrientation(), + display->isPoweredOn()); + } + StringAppendF(&result, + " transaction-flags : %08x\n" + " gpu_to_cpu_unsupported : %d\n", + mTransactionFlags.load(), !mGpuToCpuSupported); + + if (const auto displayId = getInternalDisplayId(); + 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()); + } + + StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0); + StringAppendF(&result, " use Scheduler: %s\n", mUseScheduler ? "true" : "false"); /* * VSYNC state */ - mEventThread->dump(result); + if (mUseScheduler) { + mScheduler->dump(mAppConnectionHandle, result); + } else { + mEventThread->dump(result); + } result.append("\n"); /* @@ -4441,18 +4656,15 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, /* * 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); - }); + mCurrentState.traverseInZOrder([&](Layer* layer) { layer->miniDump(result, *displayId); }); result.append("\n"); } @@ -4463,9 +4675,8 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, 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 @@ -4478,27 +4689,22 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, */ if (mVrFlingerRequestsDisplay && mVrFlinger) { result.append("VrFlinger state:\n"); - result.append(mVrFlinger->Dump().c_str()); + result.append(mVrFlinger->Dump()); 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; } bool SurfaceFlinger::startDdmConnection() @@ -4544,51 +4750,69 @@ void SurfaceFlinger::updateColorMatrixLocked() { } 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 CREATE_CONNECTION: + case CREATE_DISPLAY: + case DESTROY_DISPLAY: + case ENABLE_VSYNC_INJECTIONS: + case GET_ACTIVE_COLOR_MODE: case GET_ANIMATION_FRAME_STATS: - case SET_POWER_MODE: case GET_HDR_CAPABILITIES: - case ENABLE_VSYNC_INJECTIONS: + case SET_ACTIVE_CONFIG: + case SET_ACTIVE_COLOR_MODE: case INJECT_VSYNC: - { - // codes that require permission check + case SET_POWER_MODE: + case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: + case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: + case GET_DISPLAYED_CONTENT_SAMPLE: { if (!callingThreadHasUnscopedSurfaceFlingerAccess()) { IPCThreadState* ipc = IPCThreadState::self(); ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d", ipc->getCallingPid(), ipc->getCallingUid()); return PERMISSION_DENIED; } - break; - } - /* - * Calling setTransactionState is safe, because you need to have been - * granted a reference to Client* and Handle* to do anything with it. - * - * Creating a scoped connection is safe, as per discussion in ISurfaceComposer.h - */ - case SET_TRANSACTION_STATE: - case CREATE_SCOPED_CONNECTION: - { return OK; } - case CAPTURE_SCREEN: - { - // codes that require permission check + case GET_LAYER_DEBUG_INFO: { IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - if ((uid != AID_GRAPHICS) && - !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { - ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid); + if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { + ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; } - break; + return OK; + } + // 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_CONFIG: + case GET_BUILT_IN_DISPLAY: + case GET_DISPLAY_COLOR_MODES: + 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: + // Creating a scoped connection is safe, as per discussion in ISurfaceComposer.h + case CREATE_SCOPED_CONNECTION: + case GET_COLOR_MANAGEMENT: + case GET_COMPOSITION_PREFERENCE: { + return OK; } - case CAPTURE_LAYERS: { + case CAPTURE_LAYERS: + case CAPTURE_SCREEN: { + // codes that require permission check IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); @@ -4597,15 +4821,37 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { ALOGE("Permission Denial: can't read framebuffer pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; } - break; + return OK; + } + // The following codes are deprecated and should never be allowed to access SF. + case CONNECT_DISPLAY_UNUSED: + case CREATE_GRAPHIC_BUFFER_ALLOC_UNUSED: { + ALOGE("Attempting to access SurfaceFlinger with unused code: %u", code); + return PERMISSION_DENIED; } } - return OK; + + // These codes are used for the IBinder protocol to either interrogate the recipient + // side of the transaction for its canonical interface descriptor or to dump its state. + // We let them pass by default. + if (code == IBinder::INTERFACE_TRANSACTION || code == IBinder::DUMP_TRANSACTION || + code == IBinder::PING_TRANSACTION || code == IBinder::SHELL_COMMAND_TRANSACTION || + code == IBinder::SYSPROPS_TRANSACTION) { + return OK; + } + // Numbers from 1000 to 1031 are currently use for backdoors. The code + // in onTransact verifies that the user is root, and has access to use SF. + if (code >= 1000 && code <= 1031) { + 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; @@ -4670,8 +4916,12 @@ status_t SurfaceFlinger::onTransact( 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: { @@ -4730,7 +4980,8 @@ status_t SurfaceFlinger::onTransact( // 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. + mPrimaryDispSync->setRefreshSkipCount(n); return NO_ERROR; } case 1017: { @@ -4740,12 +4991,20 @@ status_t SurfaceFlinger::onTransact( } case 1018: { // Modify Choreographer's phase offset n = data.readInt32(); - mEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); + if (mUseScheduler) { + mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n)); + } else { + mEventThread->setPhaseOffset(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)); + if (mUseScheduler) { + mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n)); + } else { + mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); + } return NO_ERROR; } case 1020: { // Layer updates interceptor @@ -4778,9 +5037,9 @@ status_t SurfaceFlinger::onTransact( 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(); @@ -4802,35 +5061,79 @@ status_t SurfaceFlinger::onTransact( } // 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(getBE().mHwc->isUsingVrComposer()); + 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; + } } } return err; } void SurfaceFlinger::repaintEverything() { - android_atomic_or(1, &mRepaintEverything); + mRepaintEverything = true; signalTransaction(); } @@ -4847,58 +5150,69 @@ private: const int mApi; }; -status_t SurfaceFlinger::captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, +status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken, + sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation) { ATRACE_CALL(); - if (CC_UNLIKELY(display == 0)) return BAD_VALUE; + if (!displayToken) return BAD_VALUE; + + auto renderAreaRotation = fromSurfaceComposerRotation(rotation); + + sp<DisplayDevice> display; + { + Mutex::Autolock _l(mStateLock); - const sp<const DisplayDevice> device(getDisplayDeviceLocked(display)); - if (CC_UNLIKELY(device == 0)) return BAD_VALUE; + display = getDisplayDeviceLocked(displayToken); + if (!display) return BAD_VALUE; - const Rect& dispScissor = device->getScissor(); - if (!dispScissor.isEmpty()) { - sourceCrop.set(dispScissor); - // adb shell screencap will default reqWidth and reqHeight to zeros. + // 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, reqHeight, reqWidth, rotation); + DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace, + renderAreaRotation); auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this, - device, minLayerZ, maxLayerZ, std::placeholders::_1); - return captureScreenCommon(renderArea, traverseLayers, outBuffer, useIdentityTransform); + display, std::placeholders::_1); + return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, + useIdentityTransform); } status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, - sp<GraphicBuffer>* outBuffer, const Rect& sourceCrop, + sp<GraphicBuffer>* outBuffer, const Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale, bool childrenOnly) { ATRACE_CALL(); class LayerRenderArea : public RenderArea { public: LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop, - int32_t reqWidth, int32_t reqHeight, bool childrenOnly) - : RenderArea(reqHeight, reqWidth, 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; } - Rect getBounds() const override { - const Layer::State& layerState(mLayer->getDrawingState()); - return Rect(layerState.active.w, layerState.active.h); + const ui::Transform& getTransform() const override { return mTransform; } + Rect getBounds() const override { return mLayer->getBufferSize(StateSet::Drawing); } + int getHeight() const override { + return mLayer->getBufferSize(StateSet::Drawing).getHeight(); + } + int getWidth() const override { + return mLayer->getBufferSize(StateSet::Drawing).getWidth(); } - int getHeight() const override { return mLayer->getDrawingState().active.h; } - int getWidth() const override { return mLayer->getDrawingState().active.w; } bool isSecure() const override { return false; } - bool needsFiltering() const override { return false; } + bool needsFiltering() const override { return mNeedsFiltering; } Rect getSourceCrop() const override { if (mCrop.isEmpty()) { return getBounds(); @@ -4919,14 +5233,19 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, }; void render(std::function<void()> drawLayers) override { + const Rect sourceCrop = getSourceCrop(); + // no need to check rotation because there is none + mNeedsFiltering = sourceCrop.width() != getReqWidth() || + sourceCrop.height() != getReqHeight(); + if (!mChildrenOnly) { mTransform = mLayer->getTransform().inverse(); drawLayers(); } else { 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)); ReparentForDrawing reparent(mLayer, screenshotParentLayer); drawLayers(); @@ -4940,7 +5259,8 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, // 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; @@ -4949,14 +5269,14 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, auto layerHandle = reinterpret_cast<Layer::Handle*>(layerHandleBinder.get()); auto parent = layerHandle->owner.promote(); - if (parent == nullptr || parent->isPendingRemoval()) { + if (parent == nullptr || parent->isRemovedFromCurrentState()) { ALOGE("captureLayers called with a removed parent"); return NAME_NOT_FOUND; } const int uid = IPCThreadState::self()->getCallingUid(); const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM; - if (!forSystem && parent->getCurrentState().flags & layer_state_t::eLayerSecure) { + if (!forSystem && parent->getCurrentFlags() & layer_state_t::eLayerSecure) { ALOGW("Attempting to capture secure layer: PERMISSION_DENIED"); return PERMISSION_DENIED; } @@ -4964,18 +5284,26 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, Rect crop(sourceCrop); if (sourceCrop.width() <= 0) { crop.left = 0; - crop.right = parent->getCurrentState().active.w; + crop.right = parent->getBufferSize(StateSet::Current).getWidth(); } if (sourceCrop.height() <= 0) { crop.top = 0; - crop.bottom = parent->getCurrentState().active.h; + crop.bottom = parent->getBufferSize(StateSet::Current).getHeight(); } int32_t reqWidth = crop.width() * frameScale; int32_t reqHeight = crop.height() * frameScale; - LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, childrenOnly); + // really small crop or frameScale + if (reqWidth <= 0) { + reqWidth = 1; + } + if (reqHeight <= 0) { + reqHeight = 1; + } + + LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly); auto traverseLayers = [parent, childrenOnly](const LayerVector::Visitor& visitor) { parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { @@ -4987,21 +5315,23 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, visitor(layer); }); }; - return captureScreenCommon(renderArea, traverseLayers, outBuffer, false); + return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false); } status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, sp<GraphicBuffer>* outBuffer, + const ui::PixelFormat reqPixelFormat, bool useIdentityTransform) { ATRACE_CALL(); - renderArea.updateDimensions(mPrimaryDisplayOrientation); - + // 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"); // This mutex protects syncFd and captureResult for communication of the return values from the // main thread back to this Binder thread @@ -5014,7 +5344,7 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, 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) { @@ -5029,7 +5359,7 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, int fd = -1; { Mutex::Autolock _l(mStateLock); - renderArea.render([&]() { + renderArea.render([&] { result = captureScreenImplLocked(renderArea, traverseLayers, (*outBuffer).get(), useIdentityTransform, forSystem, &fd); }); @@ -5045,14 +5375,14 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, 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; } @@ -5066,106 +5396,25 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, } void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, - TraverseLayersFunction traverseLayers, bool yswap, + TraverseLayersFunction traverseLayers, bool useIdentityTransform) { ATRACE_CALL(); auto& engine(getRenderEngine()); - // get screen geometry - const auto raWidth = renderArea.getWidth(); - const auto raHeight = renderArea.getHeight(); - const auto reqWidth = renderArea.getReqWidth(); const auto reqHeight = renderArea.getReqHeight(); - Rect sourceCrop = renderArea.getSourceCrop(); - - bool filtering = false; - if (mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) { - filtering = static_cast<int32_t>(reqWidth) != raHeight || - static_cast<int32_t>(reqHeight) != raWidth; - } else { - filtering = static_cast<int32_t>(reqWidth) != raWidth || - static_cast<int32_t>(reqHeight) != raHeight; - } - - // if a default or invalid sourceCrop is passed in, set reasonable values - if (sourceCrop.width() == 0 || sourceCrop.height() == 0 || !sourceCrop.isValid()) { - sourceCrop.setLeftTop(Point(0, 0)); - sourceCrop.setRightBottom(Point(raWidth, raHeight)); - } else if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) { - Transform tr; - uint32_t flags = 0x00; - switch (mPrimaryDisplayOrientation) { - case DisplayState::eOrientation90: - flags = Transform::ROT_90; - break; - case DisplayState::eOrientation180: - flags = Transform::ROT_180; - break; - case DisplayState::eOrientation270: - flags = Transform::ROT_270; - break; - } - tr.set(flags, raWidth, raHeight); - sourceCrop = tr.transform(sourceCrop); - } - - // ensure that sourceCrop is inside screen - if (sourceCrop.left < 0) { - ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left); - } - if (sourceCrop.right > raWidth) { - ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, raWidth); - } - if (sourceCrop.top < 0) { - ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top); - } - if (sourceCrop.bottom > raHeight) { - ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, raHeight); - } + const auto sourceCrop = renderArea.getSourceCrop(); + const auto rotation = renderArea.getRotationFlags(); - // assume ColorMode::SRGB / RenderIntent::COLORIMETRIC - engine.setOutputDataSpace(Dataspace::SRGB); + engine.setOutputDataSpace(renderArea.getReqDataSpace()); engine.setDisplayMaxLuminance(DisplayDevice::sDefaultMaxLumiance); // make sure to clear all GL error flags engine.checkErrors(); - Transform::orientation_flags rotation = renderArea.getRotationFlags(); - if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) { - // convert hw orientation into flag presentation - // here inverse transform needed - uint8_t hw_rot_90 = 0x00; - uint8_t hw_flip_hv = 0x00; - switch (mPrimaryDisplayOrientation) { - case DisplayState::eOrientation90: - hw_rot_90 = Transform::ROT_90; - hw_flip_hv = Transform::ROT_180; - break; - case DisplayState::eOrientation180: - hw_flip_hv = Transform::ROT_180; - break; - case DisplayState::eOrientation270: - hw_rot_90 = 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; - if (rotation_rot_90 & hw_rot_90) { - rotation_flip_hv = (~rotation_flip_hv) & Transform::ROT_180; - } - rotation = static_cast<Transform::orientation_flags> - ((rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv)); - } - // set-up our viewport - engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, raHeight, yswap, - rotation); + engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, rotation); engine.disableTexturing(); const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill()); @@ -5173,9 +5422,9 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, engine.clearWithColor(0, 0, 0, alpha); traverseLayers([&](Layer* layer) { - if (filtering) layer->setFiltering(true); + engine.setColorTransform(layer->getColorTransform()); layer->draw(renderArea, useIdentityTransform); - if (filtering) layer->setFiltering(false); + engine.setColorTransform(mat4()); }); } @@ -5200,10 +5449,11 @@ status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea, ALOGW("FB is protected: PERMISSION_DENIED"); return PERMISSION_DENIED; } + auto& engine(getRenderEngine()); // this binds the given EGLImage as a framebuffer for the // duration of this scope. - RE::BindNativeBufferAsFramebuffer bufferBond(getRenderEngine(), buffer); + renderengine::BindNativeBufferAsFramebuffer bufferBond(engine, buffer); if (bufferBond.getStatus() != NO_ERROR) { ALOGE("got ANWB binding error while taking screenshot"); return INVALID_OPERATION; @@ -5213,53 +5463,17 @@ status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea, // 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; + renderScreenImplLocked(renderArea, traverseLayers, useIdentityTransform); - 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(); + base::unique_fd syncFd = engine.flush(); + if (syncFd < 0) { + engine.finish(); } + *outSyncFd = syncFd.release(); 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 ***"); - - 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++; - }); - } -} - // --------------------------------------------------------------------------- void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const { @@ -5270,22 +5484,17 @@ void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& 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()) { diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 463a551bfe..b1bfb3a08d 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -45,6 +45,7 @@ #include <gui/LayerState.h> #include <gui/OccupancyTracker.h> +#include <gui/BufferQueue.h> #include <hardware/hwcomposer_defs.h> @@ -54,30 +55,32 @@ #include "Barrier.h" #include "DisplayDevice.h" -#include "DispSync.h" -#include "EventThread.h" #include "FrameTracker.h" +#include "LayerBE.h" #include "LayerStats.h" #include "LayerVector.h" -#include "MessageQueue.h" +#include "SurfaceFlingerFactory.h" #include "SurfaceInterceptor.h" #include "SurfaceTracing.h" -#include "StartPropertySetThread.h" -#include "TimeStats/TimeStats.h" -#include "VSyncModulator.h" +#include "TransactionCompletedThread.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/HWComposer.h" - #include "Effects/Daltonizer.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/MessageQueue.h" +#include "Scheduler/Scheduler.h" +#include "Scheduler/VSyncModulator.h" #include <map> #include <mutex> #include <queue> +#include <set> #include <string> #include <thread> +#include <unordered_map> #include <utility> -#include "RenderArea.h" #include <layerproto/LayerProtoHeader.h> @@ -94,17 +97,20 @@ class EventControlThread; class EventThread; class IGraphicBufferConsumer; class IGraphicBufferProducer; +class IInputFlinger; class InjectVSyncSource; class Layer; class Surface; class SurfaceFlingerBE; +class TimeStats; class VSyncSource; +struct CompositionInfo; namespace impl { class EventThread; } // namespace impl -namespace RE { +namespace renderengine { class RenderEngine; } @@ -114,6 +120,10 @@ namespace dvr { class VrFlinger; } // namespace dvr +namespace surfaceflinger { +class NativeWindowSurface; +} // namespace surfaceflinger + // --------------------------------------------------------------------------- enum { @@ -130,18 +140,6 @@ enum class DisplayColorSetting : int32_t { 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 { @@ -173,7 +171,7 @@ public: 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; + std::unique_ptr<renderengine::RenderEngine> mRenderEngine; EGLContext mEGLContext; EGLDisplay mEGLDisplay; @@ -196,6 +194,9 @@ public: nsecs_t mTotalTime; std::atomic<nsecs_t> mLastSwapTime; + // Synchronization fence from a GL composition. + sp<Fence> flushFence = Fence::NO_FENCE; + // Double- vs. triple-buffering stats struct BufferingStats { BufferingStats() @@ -223,6 +224,9 @@ public: // use to differentiate callbacks from different hardware composer // instances. Each hardware composer instance gets a different sequence id. int32_t mComposerSequenceId; + + std::map<wp<IBinder>, std::vector<CompositionInfo>> mCompositionInfo; + std::map<wp<IBinder>, std::vector<CompositionInfo>> mEndOfFrameCompositionInfo; }; @@ -279,21 +283,37 @@ public: // 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; @@ -314,6 +334,8 @@ public: // force full composition on all displays void repaintEverything(); + surfaceflinger::Factory& getFactory() { return mFactory; } + // returns the default Display sp<const DisplayDevice> getDefaultDisplayDevice() const { Mutex::Autolock _l(mStateLock); @@ -329,7 +351,7 @@ public: // enable/disable h/w composer event // TODO: this should be made accessible only to EventThread - void setVsyncEnabled(int disp, int enabled); + void setVsyncEnabled(EventThread::DisplayType displayType, bool enabled); // called on the main thread by MessageQueue when an internal message // is received @@ -338,14 +360,19 @@ public: // 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 { return *getBE().mRenderEngine; } bool authenticateSurfaceTextureLocked( const sp<IGraphicBufferProducer>& bufferProducer) const; - int getPrimaryDisplayOrientation() const { return mPrimaryDisplayOrientation; } + inline void onLayerCreated() { mNumLayers++; } + inline void onLayerDestroyed() { mNumLayers--; } + + TransactionCompletedThread& getTransactionCompletedThread() { + return mTransactionCompletedThread; + } private: friend class Client; @@ -353,6 +380,8 @@ private: friend class impl::EventThread; friend class Layer; friend class BufferLayer; + friend class BufferQueueLayer; + friend class BufferStateLayer; friend class MonitoredProducer; // For unit tests @@ -410,7 +439,7 @@ private: 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 void destroyDisplay(const sp<IBinder>& displayToken); virtual sp<IBinder> getBuiltInDisplay(int32_t id); virtual void setTransactionState(const Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags); @@ -421,32 +450,45 @@ private: std::vector<FrameEvent>* outSupported) const; virtual sp<IDisplayEventConnection> createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp); - virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - int32_t minLayerZ, int32_t maxLayerZ, bool useIdentityTransform, + virtual status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer, + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, ISurfaceComposer::Rotation rotation); 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); + const ui::Dataspace reqDataspace, + const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, + float frameScale, bool childrenOnly); + virtual status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats); + virtual status_t getDisplayConfigs(const sp<IBinder>& displayToken, + Vector<DisplayInfo>* configs); + virtual int getActiveConfig(const sp<IBinder>& displayToken); + virtual status_t getDisplayColorModes(const sp<IBinder>& displayToken, + Vector<ui::ColorMode>* configs); + virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken); + virtual status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode); + virtual void setPowerMode(const sp<IBinder>& displayToken, int mode); + virtual status_t setActiveConfig(const sp<IBinder>& displayToken, 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 getHdrCapabilities(const sp<IBinder>& displayToken, + 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; - + virtual status_t getColorManagement(bool* outGetColorManagement) const; + status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat, + ui::Dataspace* outWideColorGamutDataspace, + ui::PixelFormat* outWideColorGamutPixelFormat) const override; + virtual status_t getDisplayedContentSamplingAttributes( + const sp<IBinder>& display, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, + uint8_t* outComponentMask) const override; + virtual status_t setDisplayContentSamplingEnabled(const sp<IBinder>& display, bool enable, + uint8_t componentMask, + uint64_t maxFrames) const override; + virtual status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames, + uint64_t timestamp, + DisplayedFrameStats* outStats) const override; /* ------------------------------------------------------------------------ * DeathRecipient interface @@ -461,11 +503,11 @@ private: /* ------------------------------------------------------------------------ * 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 @@ -480,16 +522,13 @@ private: // called on the main thread in response to initializeDisplays() void onInitializeDisplays(); // called on the main thread in response to setActiveConfig() - void setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode); + void setActiveConfigInternal(const sp<DisplayDevice>& display, int mode); // called on the main thread in response to setPowerMode() - void setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, - bool stateLockHeld); + void setPowerModeInternal(const sp<DisplayDevice>& display, int mode); // Called on the main thread in response to setActiveColorMode() - void setActiveColorModeInternal(const sp<DisplayDevice>& hw, - ui::ColorMode colorMode, - ui::Dataspace dataSpace, - ui::RenderIntent renderIntent); + void setActiveColorModeInternal(const sp<DisplayDevice>& display, ui::ColorMode colorMode, + ui::Dataspace dataSpace, ui::RenderIntent renderIntent); // Returns whether the transaction actually modified any state bool handleMessageTransaction(); @@ -502,6 +541,7 @@ private: void handleTransaction(uint32_t transactionFlags); void handleTransactionLocked(uint32_t transactionFlags); + void updateInputWindows(); void updateCursorAsync(); /* handlePageFlip - latch a new buffer if available and compute the dirty @@ -517,7 +557,8 @@ private: 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); + uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart); + void latchAndReleaseBuffer(const sp<Layer>& layer); void commitTransaction(); bool containsAnyInvalidClientState(const Vector<ComposerState>& states); uint32_t setClientStateLocked(const ComposerState& composerState); @@ -532,10 +573,14 @@ private: int32_t windowType, int32_t ownerUid, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent); - status_t createBufferLayer(const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format, - sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, - sp<Layer>* outLayer); + status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, PixelFormat& format, + sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, + sp<Layer>* outLayer); + + status_t createBufferStateLayer(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, sp<IBinder>* outHandle, @@ -551,14 +596,16 @@ private: // ISurfaceComposerClient::destroySurface() status_t onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle); + void markLayerPendingRemovalLocked(const Mutex& /* mStateLock */, const sp<Layer>& layer); + // 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); + void onHandleDestroyed(sp<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); + status_t removeLayer(const sp<Layer>& layer); + status_t removeLayerLocked(const Mutex&, const sp<Layer>& layer); // add a layer to SurfaceFlinger status_t addClientLayer(const sp<Client>& client, @@ -574,18 +621,18 @@ private: void startBootAnim(); void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, - bool yswap, bool useIdentityTransform); + bool useIdentityTransform); status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, - sp<GraphicBuffer>* outBuffer, + sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat, bool useIdentityTransform); status_t captureScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, ANativeWindowBuffer* buffer, bool useIdentityTransform, bool forSystem, int* outSyncFd); - void traverseLayersInDisplay(const sp<const DisplayDevice>& display, int32_t minLayerZ, - int32_t maxLayerZ, const LayerVector::Visitor& visitor); + void traverseLayersInDisplay(const sp<const DisplayDevice>& display, + const LayerVector::Visitor& visitor); - sp<StartPropertySetThread> mStartPropertySetThread = nullptr; + sp<StartPropertySetThread> mStartPropertySetThread; /* ------------------------------------------------------------------------ * Properties @@ -604,38 +651,36 @@ private: // 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 = getInternalDisplayToken()) { + return getDisplayDeviceLocked(token); } - return NAME_NOT_FOUND; + return nullptr; } // mark a region of a layer stack dirty. this updates the dirty @@ -652,50 +697,56 @@ private: * Compositing */ void invalidateHwcGeometry(); - void computeVisibleRegions(const sp<const DisplayDevice>& displayDevice, - 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 computeVisibleRegions(const sp<const DisplayDevice>& display, Region& dirtyRegion, + Region& opaqueRegion); + + void preComposition(); + void postComposition(); + void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, + std::shared_ptr<FenceTime>& presentFenceTime); + void setCompositorTimingSnapped(const DisplayStatInfo& stats, + nsecs_t compositeToPresentLatency); void rebuildLayerStacks(); - ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& displayDevice, + ui::Dataspace getBestDataspace(const sp<const DisplayDevice>& display, ui::Dataspace* outHdrDataSpace) const; // Returns the appropriate ColorMode, Dataspace and RenderIntent for the // DisplayDevice. The function only returns the supported ColorMode, // Dataspace and RenderIntent. - void pickColorMode(const sp<DisplayDevice>& displayDevice, - ui::ColorMode* outMode, - ui::Dataspace* outDataSpace, - ui::RenderIntent* outRenderIntent) const; - - void setUpHWComposer(); - void doComposition(); - void doDebugFlashRegions(); + void pickColorMode(const sp<DisplayDevice>& display, ui::ColorMode* outMode, + ui::Dataspace* outDataSpace, ui::RenderIntent* outRenderIntent) const; + + 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 doTracing(const char* where); 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. + bool doComposeSurfaces(const sp<DisplayDevice>& display); - 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, + sp<DisplayDevice> setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken, + const std::optional<DisplayId>& displayId, const DisplayDeviceState& state, const sp<DisplaySurface>& dispSurface, const sp<IGraphicBufferProducer>& producer); @@ -727,27 +778,58 @@ public: } 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; + sp<IBinder> getPhysicalDisplayToken(DisplayId displayId) const { + const auto it = mPhysicalDisplayTokens.find(displayId); + return it != mPhysicalDisplayTokens.end() ? it->second : nullptr; + } + + std::optional<DisplayId> getPhysicalDisplayId(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> getInternalDisplayToken() const { + const auto displayId = getInternalDisplayId(); + return displayId ? getPhysicalDisplayToken(*displayId) : nullptr; + } + + std::optional<DisplayId> getInternalDisplayId() const { + const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId(); + return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt; + } + + // TODO(b/74619554): Remove special cases for external display. + std::optional<DisplayId> getExternalDisplayId() const { + const auto hwcDisplayId = getHwComposer().getExternalHwcDisplayId(); + return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt; + } + + void listLayersLocked(const Vector<String16>& args, size_t& index, std::string& result) const; + void dumpStatsLocked(const Vector<String16>& args, size_t& index, std::string& result) const; + void clearStatsLocked(const Vector<String16>& args, size_t& index, std::string& result); + void dumpAllLocked(const Vector<String16>& args, size_t& index, std::string& result) const; bool startDdmConnection(); - void appendSfConfigString(String8& result) const; - void checkScreenshot(size_t w, size_t s, size_t h, void const* vaddr, - TraverseLayersFunction traverseLayers); + void appendSfConfigString(std::string& result) const; void logFrameStats(); - void dumpStaticScreenStats(String8& result) const; + void dumpStaticScreenStats(std::string& result) const; // Not const because each Layer needs to query Fences and cache timestamps. - void dumpFrameEventsLocked(String8& result); + void dumpFrameEventsLocked(std::string& result); void recordBufferingStats(const char* layerName, std::vector<OccupancyTracker::Segment>&& history); - void dumpBufferingStats(String8& result) const; - void dumpWideColorInfo(String8& result) const; + void dumpBufferingStats(std::string& result) const; + void dumpDisplayIdentificationData(std::string& result) const; + void dumpWideColorInfo(std::string& result) const; + void dumpFrameCompositionInfo(std::string& result) const; LayersProto dumpProtoInfo(LayerVector::StateSet stateSet) const; - LayersProto dumpVisibleLayersProtoInfo(int32_t hwcId) const; + LayersProto dumpVisibleLayersProtoInfo(const DisplayDevice& display) const; bool isLayerTripleBufferingDisabled() const { return this->mLayerTripleBufferingDisabled; @@ -768,10 +850,12 @@ private: * 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; @@ -790,8 +874,7 @@ private: bool mLayersRemoved; bool mLayersAdded; - // access must be protected by mInvalidateLock - volatile int32_t mRepaintEverything; + std::atomic<bool> mRepaintEverything{false}; // constant members (no synchronization needed for access) nsecs_t mBootTime; @@ -803,7 +886,7 @@ private: std::unique_ptr<VSyncSource> mSfEventThreadSource; std::unique_ptr<InjectVSyncSource> mVSyncInjector; std::unique_ptr<EventControlThread> mEventControlThread; - sp<IBinder> mBuiltinDisplays[DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES]; + std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens; VSyncModulator mVsyncModulator; @@ -825,7 +908,7 @@ private: BootStage mBootStage; struct HotplugEvent { - hwc2_display_t display; + hwc2_display_t hwcDisplayId; HWC2::Connection connection = HWC2::Connection::Invalid; }; // protected by mStateLock @@ -833,7 +916,7 @@ private: // 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; // don't use a lock for these, we don't care int mDebugRegion; @@ -841,26 +924,27 @@ private: int mDebugDisableHWC; int mDebugDisableTransformHint; volatile nsecs_t mDebugInSwapBuffers; - nsecs_t mLastSwapBufferTime; volatile nsecs_t mDebugInTransaction; nsecs_t mLastTransactionTime; + nsecs_t mPostFramebufferTime; bool mForceFullDamage; bool mPropagateBackpressure = true; - std::unique_ptr<SurfaceInterceptor> mInterceptor = - std::make_unique<impl::SurfaceInterceptor>(this); + std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)}; SurfaceTracing mTracing; LayerStats mLayerStats; - TimeStats& mTimeStats = TimeStats::getInstance(); + std::unique_ptr<TimeStats> mTimeStats; bool mUseHwcVirtualDisplays = false; + std::atomic<uint32_t> mFrameMissedCount{0}; + + TransactionCompletedThread mTransactionCompletedThread; // Restrict layers to use two buffers in their bufferqueues. bool mLayerTripleBufferingDisabled = false; // these are thread safe - mutable std::unique_ptr<MessageQueue> mEventQueue{std::make_unique<impl::MessageQueue>()}; + mutable std::unique_ptr<MessageQueue> mEventQueue{mFactory.createMessageQueue()}; FrameTracker mAnimFrameTracker; - DispSync mPrimaryDispSync; - int mPrimaryDisplayOrientation = DisplayState::eOrientationDefault; + std::unique_ptr<DispSync> mPrimaryDispSync; // protected by mDestroyedLayerLock; mutable Mutex mDestroyedLayerLock; @@ -870,6 +954,7 @@ private: Mutex mHWVsyncLock; bool mPrimaryHWVsyncEnabled; bool mHWVsyncAvailable; + nsecs_t mRefreshStartTime; std::atomic<bool> mRefreshPending{false}; @@ -901,20 +986,18 @@ private: std::thread::id mMainThreadId; DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::ENHANCED; - // Applied on Display P3 layers when the render intent is non-colorimetric. - mat4 mEnhancedSaturationMatrix; - - using CreateBufferQueueFunction = - std::function<void(sp<IGraphicBufferProducer>* /* outProducer */, - sp<IGraphicBufferConsumer>* /* outConsumer */, - bool /* consumerIsSurfaceFlinger */)>; - CreateBufferQueueFunction mCreateBufferQueue; - using CreateNativeWindowSurfaceFunction = - std::function<std::unique_ptr<NativeWindowSurface>(const sp<IGraphicBufferProducer>&)>; - CreateNativeWindowSurfaceFunction mCreateNativeWindowSurface; + ui::Dataspace mDefaultCompositionDataspace; + ui::Dataspace mWideColorGamutCompositionDataspace; SurfaceFlingerBE mBE; + + bool mUseScheduler = false; + std::unique_ptr<Scheduler> mScheduler; + sp<Scheduler::ConnectionHandle> mAppConnectionHandle; + sp<Scheduler::ConnectionHandle> mSfConnectionHandle; + + sp<IInputFlinger> mInputFlinger; }; }; // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp new file mode 100644 index 0000000000..88649e3a1d --- /dev/null +++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp @@ -0,0 +1,130 @@ +/* + * 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 <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/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<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> createScheduler(std::function<void(bool)> callback) override { + return std::make_unique<Scheduler>(callback); + } + + std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor( + SurfaceFlinger* flinger) override { + return std::make_unique<android::impl::SurfaceInterceptor>(flinger); + } + + sp<StartPropertySetThread> createStartPropertySetThread( + bool timestampPropertyValue) override { + return new StartPropertySetThread(timestampPropertyValue); + } + + sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override { + return new DisplayDevice(std::move(creationArgs)); + } + + sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + std::string requestorName) override { + return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); + } + + void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer, + bool consumerIsSurfaceFlinger) override { + BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); + } + + std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer>& producer) override { + return surfaceflinger::impl::createNativeWindowSurface(producer); + } + + 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::unique_ptr<TimeStats> createTimeStats() override { + return std::make_unique<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 0000000000..1c27cc7e23 --- /dev/null +++ b/services/surfaceflinger/SurfaceFlingerFactory.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 <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 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> createScheduler(std::function<void(bool)> callback) = 0; + virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0; + + virtual sp<StartPropertySetThread> createStartPropertySetThread( + bool timestampPropertyValue) = 0; + virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) = 0; + virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, std::string requestorName) = 0; + virtual void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer, + bool consumerIsSurfaceFlinger) = 0; + virtual std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer>&) = 0; + + virtual 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::unique_ptr<TimeStats> createTimeStats() = 0; + +protected: + ~Factory() = default; +}; + +ANDROID_API sp<SurfaceFlinger> createSurfaceFlinger(); + +} // namespace surfaceflinger +} // namespace android diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 4596a21f12..a6dcb7e327 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,24 +96,28 @@ void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment, 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()); - addDepthLocked(transaction, layerId, layer->mCurrentState.z); - addAlphaLocked(transaction, layerId, layer->mCurrentState.color.a); - addTransparentRegionLocked(transaction, layerId, layer->mCurrentState.activeTransparentRegion); - addLayerStackLocked(transaction, layerId, layer->mCurrentState.layerStack); - addCropLocked(transaction, layerId, layer->mCurrentState.crop); - if (layer->mCurrentState.barrierLayer != nullptr) { - addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer.promote(), - layer->mCurrentState.frameNumber); - } - addFinalCropLocked(transaction, layerId, layer->mCurrentState.finalCrop); + Mutex::Autolock lock(layer->mStateMutex); + addPositionLocked(transaction, layerId, layer->mState.current.active_legacy.transform.tx(), + layer->mState.current.active_legacy.transform.ty()); + addDepthLocked(transaction, layerId, layer->mState.current.z); + addAlphaLocked(transaction, layerId, layer->mState.current.color.a); + addTransparentRegionLocked(transaction, layerId, + layer->mState.current.activeTransparentRegion_legacy); + addLayerStackLocked(transaction, layerId, layer->mState.current.layerStack); + addCropLocked(transaction, layerId, layer->mState.current.crop_legacy); + addCornerRadiusLocked(transaction, layerId, layer->mState.current.cornerRadius); + if (layer->mState.current.barrierLayer_legacy != nullptr) { + addDeferTransactionLocked(transaction, layerId, + layer->mState.current.barrierLayer_legacy.promote(), + layer->mState.current.frameNumber_legacy); + } addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode()); - addFlagsLocked(transaction, layerId, layer->mCurrentState.flags); + addFlagsLocked(transaction, layerId, layer->mState.current.flags); } void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment, @@ -122,10 +127,10 @@ void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment, 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 +182,10 @@ SurfaceChange* SurfaceInterceptor::createSurfaceChangeLocked(Transaction* transa } 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 +291,12 @@ void SurfaceInterceptor::addCropLocked(Transaction* transaction, int32_t layerId 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 +357,26 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, 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 +384,19 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, } 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 +416,7 @@ void SurfaceInterceptor::addTransactionLocked(Increment* increment, 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 +427,9 @@ void SurfaceInterceptor::addSurfaceCreationLocked(Increment* increment, 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); + Mutex::Autolock lock(layer->mStateMutex); + creation->set_w(layer->mState.current.active_legacy.w); + creation->set_h(layer->mState.current.active_legacy.h); } void SurfaceInterceptor::addSurfaceDeletionLocked(Increment* increment, @@ -448,7 +454,7 @@ void SurfaceInterceptor::addVSyncUpdateLocked(Increment* increment, nsecs_t time 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 +463,7 @@ void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32 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 +475,26 @@ void SurfaceInterceptor::addDisplaySurfaceLocked(Transaction* transaction, int32 } 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 +507,24 @@ void SurfaceInterceptor::addDisplayCreationLocked(Increment* increment, 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 +587,22 @@ void SurfaceInterceptor::saveDisplayCreation(const DisplayDeviceState& info) { 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 96defcc265..563a44c088 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -66,8 +66,8 @@ public: // 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 @@ public: // 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 @@ private: 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 @@ private: 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 @@ private: 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 67dcd06187..b7e9a915fa 100644 --- a/services/surfaceflinger/SurfaceTracing.cpp +++ b/services/surfaceflinger/SurfaceTracing.cpp @@ -20,28 +20,55 @@ #include "SurfaceTracing.h" #include <android-base/file.h> +#include <android-base/stringprintf.h> #include <log/log.h> #include <utils/SystemClock.h> #include <utils/Trace.h> namespace android { -void SurfaceTracing::enable() { - ATRACE_CALL(); +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(size_t bufferSizeInByte) { std::lock_guard<std::mutex> protoGuard(mTraceMutex); if (mEnabled) { return; } mEnabled = true; - - mTrace = std::make_unique<LayersTraceFileProto>(); - mTrace->set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 | - LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L); + mBuffer.reset(bufferSizeInByte); } status_t SurfaceTracing::disable() { - ATRACE_CALL(); std::lock_guard<std::mutex> protoGuard(mTraceMutex); if (!mEnabled) { @@ -51,7 +78,7 @@ status_t SurfaceTracing::disable() { 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(); + mBuffer.reset(0); return err; } @@ -65,43 +92,42 @@ void SurfaceTracing::traceLayers(const char* where, LayersProto layers) { 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(); - } + + LayersTraceProto entry; + entry.set_elapsed_realtime_nanos(elapsedRealtimeNano()); + entry.set_where(where); + entry.mutable_layers()->Swap(&layers); + + mBuffer.emplace(std::move(entry)); } status_t SurfaceTracing::writeProtoFileLocked() { ATRACE_CALL(); - if (!mTrace->IsInitialized()) { - return NOT_ENOUGH_DATA; - } + LayersTraceFileProto fileProto; std::string output; - if (!mTrace->SerializeToString(&output)) { + + fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 | + LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L); + mBuffer.flush(&fileProto); + + if (!fileProto.SerializeToString(&output)) { return PERMISSION_DENIED; } - if (!android::base::WriteStringToFile(output, mOutputFileName, true)) { + if (!android::base::WriteStringToFile(output, kDefaultFileName, true)) { return PERMISSION_DENIED; } return NO_ERROR; } -void SurfaceTracing::dump(String8& result) const { +void SurfaceTracing::dump(std::string& result) const { std::lock_guard<std::mutex> protoGuard(mTraceMutex); - result.appendFormat("Tracing state: %s\n", mEnabled ? "enabled" : "disabled"); - result.appendFormat(" number of entries: %d\n", mTrace ? mTrace->entry_size() : 0); + 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 fd8cb82a9b..fd919af999 100644 --- a/services/surfaceflinger/SurfaceTracing.h +++ b/services/surfaceflinger/SurfaceTracing.h @@ -18,36 +18,57 @@ #include <layerproto/LayerProtoHeader.h> #include <utils/Errors.h> -#include <utils/String8.h> #include <memory> #include <mutex> +#include <queue> using namespace android::surfaceflinger; namespace android { +constexpr auto operator""_MB(unsigned long long const num) { + return num * 1024 * 1024; +} + /* * SurfaceTracing records layer states during surface flinging. */ class SurfaceTracing { public: - void enable(); + void enable() { enable(kDefaultBufferCapInByte); } + void enable(size_t bufferSizeInByte); status_t disable(); - bool isEnabled() const; - void traceLayers(const char* where, LayersProto); - void dump(String8& result) const; + + bool isEnabled() const; + void dump(std::string& result) const; private: - static constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/layers_trace.pb"; + static constexpr auto kDefaultBufferCapInByte = 100_MB; + static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb"; + + class LayersTraceBuffer { // ring buffer + public: + size_t size() const { return mSizeInBytes; } + size_t used() const { return mUsedInBytes; } + size_t frameCount() const { return mStorage.size(); } + + 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; + }; status_t writeProtoFileLocked(); bool mEnabled = false; - std::string mOutputFileName = DEFAULT_FILENAME; mutable std::mutex mTraceMutex; - std::unique_ptr<LayersTraceFileProto> mTrace; + LayersTraceBuffer mBuffer; }; } // namespace android diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index d4f1e29042..6a5488aa2c 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,16 +32,8 @@ namespace android { -TimeStats& TimeStats::getInstance() { - static std::unique_ptr<TimeStats> sInstance; - static std::once_flag sOnceFlag; - - 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) { + std::string& result) { ATRACE_CALL(); if (args.size() > index + 10) { @@ -85,7 +78,7 @@ void TimeStats::incrementTotalFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - timeStats.totalFrames++; + mTimeStats.totalFrames++; } void TimeStats::incrementMissedFrames() { @@ -94,7 +87,7 @@ void TimeStats::incrementMissedFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - timeStats.missedFrames++; + mTimeStats.missedFrames++; } void TimeStats::incrementClientCompositionFrames() { @@ -103,13 +96,13 @@ void TimeStats::incrementClientCompositionFrames() { 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 +111,11 @@ bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* time 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 +124,11 @@ bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* time 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 +141,15 @@ static int32_t msBetween(nsecs_t start, nsecs_t end) { 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 +157,129 @@ static std::string getPackageName(const std::string& layerName) { 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++; - - const int32_t postToPresentMs = - msBetween(timeRecords[0].postTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-post2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, postToPresentMs); + timeStatsLayer.droppedFrames += layerRecord.droppedFrames; + layerRecord.droppedFrames = 0; + + const int32_t postToAcquireMs = msBetween(timeRecords[0].frameTime.postTime, + timeRecords[0].frameTime.acquireTime); + ALOGV("[%d]-[%" PRIu64 "]-post2acquire[%d]", layerID, + timeRecords[0].frameTime.frameNumber, postToAcquireMs); + timeStatsLayer.deltas["post2acquire"].insert(postToAcquireMs); + + const int32_t postToPresentMs = msBetween(timeRecords[0].frameTime.postTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-post2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, postToPresentMs); timeStatsLayer.deltas["post2present"].insert(postToPresentMs); - const int32_t acquireToPresentMs = - msBetween(timeRecords[0].acquireTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-acquire2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, acquireToPresentMs); + const int32_t acquireToPresentMs = msBetween(timeRecords[0].frameTime.acquireTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-acquire2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, acquireToPresentMs); timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs); - const int32_t latchToPresentMs = - msBetween(timeRecords[0].latchTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-latch2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, latchToPresentMs); + const int32_t latchToPresentMs = msBetween(timeRecords[0].frameTime.latchTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-latch2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, latchToPresentMs); timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs); - const int32_t desiredToPresentMs = - msBetween(timeRecords[0].desiredTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-desired2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, desiredToPresentMs); + const int32_t desiredToPresentMs = msBetween(timeRecords[0].frameTime.desiredTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-desired2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, desiredToPresentMs); timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs); - const int32_t presentToPresentMs = - msBetween(prevTimeRecord.presentTime, timeRecords[0].presentTime); - ALOGV("[%s]-[%" PRIu64 "]-present2present[%d]", layerName.c_str(), - timeRecords[0].frameNumber, presentToPresentMs); + const int32_t presentToPresentMs = msBetween(prevTimeRecord.frameTime.presentTime, + timeRecords[0].frameTime.presentTime); + ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerID, + timeRecords[0].frameTime.frameNumber, presentToPresentMs); timeStatsLayer.deltas["present2present"].insert(presentToPresentMs); - - timeStats.stats[layerName].statsEnd = static_cast<int64_t>(std::time(0)); } + + // Output additional trace points to track frame time. + ATRACE_INT64(("TimeStats-Post - " + layerName).c_str(), timeRecords[0].frameTime.postTime); + ATRACE_INT64(("TimeStats-Acquire - " + layerName).c_str(), + timeRecords[0].frameTime.acquireTime); + ATRACE_INT64(("TimeStats-Latch - " + layerName).c_str(), + timeRecords[0].frameTime.latchTime); + ATRACE_INT64(("TimeStats-Desired - " + layerName).c_str(), + timeRecords[0].frameTime.desiredTime); + ATRACE_INT64(("TimeStats-Present - " + layerName).c_str(), + timeRecords[0].frameTime.presentTime); + prevTimeRecord = timeRecords[0]; timeRecords.pop_front(); layerRecord.waitData--; } } +// This regular expression captures the following layer names for instance: +// 1) StatusBat#0 +// 2) NavigationBar#1 +// 3) co(m).*#0 +// 4) SurfaceView - co(m).*#0 +// Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).* +// is a bit more robust in case there's a slight change. +// The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases. +static const std::regex layerNameRegex( + "(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+"); + static bool layerNameIsValid(const std::string& layerName) { - // This regular expression captures the following layer names for instance: - // 1) StatusBat#0 - // 2) NavigationBar#1 - // 3) com.*#0 - // 4) SurfaceView - com.*#0 - // Using [-\\s\t]+ for the conjunction part between SurfaceView and com.* is - // a bit more robust in case there's a slight change. - // The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases. - std::regex re("(((SurfaceView[-\\s\\t]+)?com\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+"); - return std::regex_match(layerName.begin(), layerName.end(), re); + return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex); } -void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime) { +void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName, + nsecs_t postTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerName.c_str(), frameNumber, postTime); + ALOGV("[%d]-[%" PRIu64 "]-[%s]-PostTime[%" PRId64 "]", layerID, frameNumber, layerName.c_str(), + postTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName) && !layerNameIsValid(layerName)) { - return; + if (!mTimeStatsTracker.count(layerID) && layerNameIsValid(layerName)) { + mTimeStatsTracker[layerID].layerName = layerName; } - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) { - ALOGV("[%s]-timeRecords is already at its maximum size[%zu]", layerName.c_str(), - MAX_NUM_TIME_RECORDS); - // TODO(zzyiwei): if this happens, there must be a present fence missing - // or waitData is not in the correct position. Need to think out a - // reasonable way to recover from this state. + ALOGE("[%d]-[%s]-timeRecords is at its maximum size[%zu]. Ignore this when unittesting.", + layerID, layerRecord.layerName.c_str(), MAX_NUM_TIME_RECORDS); + mTimeStatsTracker.erase(layerID); return; } // For most media content, the acquireFence is invalid because the buffer is // ready at the queueBuffer stage. In this case, acquireTime should be given // a default value as postTime. TimeRecord timeRecord = { - .frameNumber = frameNumber, - .postTime = postTime, - .acquireTime = postTime, + .frameTime = + { + .frameNumber = frameNumber, + .postTime = postTime, + .latchTime = postTime, + .acquireTime = postTime, + .desiredTime = postTime, + }, }; layerRecord.timeRecords.push_back(timeRecord); if (layerRecord.waitData < 0 || @@ -267,160 +287,234 @@ void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, layerRecord.waitData = layerRecord.timeRecords.size() - 1; } -void TimeStats::setLatchTime(const std::string& layerName, uint64_t frameNumber, - nsecs_t latchTime) { +void TimeStats::setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerName.c_str(), frameNumber, latchTime); + ALOGV("[%d]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerID, frameNumber, latchTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { - timeRecord.latchTime = latchTime; + if (timeRecord.frameTime.frameNumber == frameNumber) { + timeRecord.frameTime.latchTime = latchTime; } } -void TimeStats::setDesiredTime(const std::string& layerName, uint64_t frameNumber, - nsecs_t desiredTime) { +void TimeStats::setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerName.c_str(), frameNumber, - desiredTime); + ALOGV("[%d]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerID, frameNumber, desiredTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { - timeRecord.desiredTime = desiredTime; + if (timeRecord.frameTime.frameNumber == frameNumber) { + timeRecord.frameTime.desiredTime = desiredTime; } } -void TimeStats::setAcquireTime(const std::string& layerName, uint64_t frameNumber, - nsecs_t acquireTime) { +void TimeStats::setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerName.c_str(), frameNumber, - acquireTime); + ALOGV("[%d]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerID, frameNumber, acquireTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { - timeRecord.acquireTime = acquireTime; + if (timeRecord.frameTime.frameNumber == frameNumber) { + timeRecord.frameTime.acquireTime = acquireTime; } } -void TimeStats::setAcquireFence(const std::string& layerName, uint64_t frameNumber, +void TimeStats::setAcquireFence(int32_t layerID, uint64_t frameNumber, const std::shared_ptr<FenceTime>& acquireFence) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber, + ALOGV("[%d]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerID, frameNumber, acquireFence->getSignalTime()); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { + if (timeRecord.frameTime.frameNumber == frameNumber) { timeRecord.acquireFence = acquireFence; } } -void TimeStats::setPresentTime(const std::string& layerName, uint64_t frameNumber, - nsecs_t presentTime) { +void TimeStats::setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerName.c_str(), frameNumber, - presentTime); + ALOGV("[%d]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerID, frameNumber, presentTime); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { - timeRecord.presentTime = presentTime; + if (timeRecord.frameTime.frameNumber == frameNumber) { + timeRecord.frameTime.presentTime = presentTime; timeRecord.ready = true; layerRecord.waitData++; } - flushAvailableRecordsToStatsLocked(layerName); + flushAvailableRecordsToStatsLocked(layerID); } -void TimeStats::setPresentFence(const std::string& layerName, uint64_t frameNumber, +void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber, + ALOGV("[%d]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerID, frameNumber, presentFence->getSignalTime()); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData]; - if (timeRecord.frameNumber == frameNumber) { + if (timeRecord.frameTime.frameNumber == frameNumber) { timeRecord.presentFence = presentFence; timeRecord.ready = true; layerRecord.waitData++; } - flushAvailableRecordsToStatsLocked(layerName); + flushAvailableRecordsToStatsLocked(layerID); } -void TimeStats::onDisconnect(const std::string& layerName) { +void TimeStats::onDestroy(int32_t layerID) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-onDisconnect", layerName.c_str()); + ALOGV("[%d]-onDestroy", layerID); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - flushAvailableRecordsToStatsLocked(layerName); - timeStatsTracker.erase(layerName); + if (!mTimeStatsTracker.count(layerID)) return; + mTimeStatsTracker.erase(layerID); } -void TimeStats::clearLayerRecord(const std::string& layerName) { +void TimeStats::removeTimeRecord(int32_t layerID, uint64_t frameNumber) { if (!mEnabled.load()) return; ATRACE_CALL(); - ALOGV("[%s]-clearLayerRecord", layerName.c_str()); + ALOGV("[%d]-[%" PRIu64 "]-removeTimeRecord", layerID, frameNumber); std::lock_guard<std::mutex> lock(mMutex); - if (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; - layerRecord.timeRecords.clear(); - layerRecord.prevTimeRecord.ready = false; - layerRecord.waitData = -1; + if (!mTimeStatsTracker.count(layerID)) return; + LayerRecord& layerRecord = mTimeStatsTracker[layerID]; + size_t removeAt = 0; + for (const TimeRecord& record : layerRecord.timeRecords) { + 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.droppedFrames++; } -void TimeStats::removeTimeRecord(const std::string& layerName, uint64_t frameNumber) { +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::flushAvailableGlobalRecordsToStatsLocked() { ATRACE_CALL(); - ALOGV("[%s]-[%" PRIu64 "]-removeTimeRecord", layerName.c_str(), frameNumber); + 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 (!timeStatsTracker.count(layerName)) return; - LayerRecord& layerRecord = timeStatsTracker[layerName]; - size_t removeAt = 0; - for (const TimeRecord& record : layerRecord.timeRecords) { - if (record.frameNumber == frameNumber) break; - removeAt++; + if (presentFence == nullptr || !presentFence->isValid()) { + mGlobalRecord.prevPresentTime = 0; + return; } - if (removeAt == layerRecord.timeRecords.size()) return; - layerRecord.timeRecords.erase(layerRecord.timeRecords.begin() + removeAt); - if (layerRecord.waitData > static_cast<int32_t>(removeAt)) { - --layerRecord.waitData; + + if (mPowerTime.powerMode != HWC_POWER_MODE_NORMAL) { + // Try flushing the last present fence on HWC_POWER_MODE_NORMAL. + flushAvailableGlobalRecordsToStatsLocked(); + mGlobalRecord.presentFences.clear(); + mGlobalRecord.prevPresentTime = 0; + return; } + + if (mGlobalRecord.presentFences.size() == MAX_NUM_TIME_RECORDS) { + // The front presentFence must be trapped in pending status in this + // case. Try dequeuing the front one to recover. + ALOGE("GlobalPresentFences is already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS); + mGlobalRecord.prevPresentTime = 0; + mGlobalRecord.presentFences.pop_front(); + } + + mGlobalRecord.presentFences.emplace_back(presentFence); + flushAvailableGlobalRecordsToStatsLocked(); } void TimeStats::enable() { @@ -429,9 +523,10 @@ void TimeStats::enable() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - ALOGD("Enabled"); mEnabled.store(true); - timeStats.statsStart = static_cast<int64_t>(std::time(0)); + mTimeStats.statsStart = static_cast<int64_t>(std::time(0)); + mPowerTime.prevTime = systemTime(); + ALOGD("Enabled"); } void TimeStats::disable() { @@ -440,45 +535,54 @@ void TimeStats::disable() { 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(); + 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"); } } diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index 831821000f..71c3ed7e8e 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,96 @@ 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; - - struct TimeRecord { - bool ready = false; + 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); + TimeStats() = default; + ~TimeStats() = default; + + void parseArgs(bool asProto, const Vector<String16>& args, size_t& index, std::string& result); + bool isEnabled(); + 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, + void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName, + nsecs_t postTime); + void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime); + void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime); + void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime); + void setAcquireFence(int32_t layerID, 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, + void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime); + void setPresentFence(int32_t layerID, 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); + // Clean up the layer record + void onDestroy(int32_t layerID); + // If SF skips or rejects a buffer, remove the corresponding TimeRecord. + void removeTimeRecord(int32_t layerID, uint64_t frameNumber); -private: - TimeStats() = default; + void setPowerMode(int32_t powerMode); + void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence); - bool recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord); - void flushAvailableRecordsToStatsLocked(const std::string& layerName); + // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU + // static const size_t MAX_NUM_LAYER_RECORDS = 200; + static const size_t MAX_NUM_TIME_RECORDS = 64; + +private: + bool recordReadyLocked(int32_t layerID, TimeRecord* timeRecord); + void flushAvailableRecordsToStatsLocked(int32_t layerID); + void flushPowerTimeLocked(); + void flushAvailableGlobalRecordsToStatsLocked(); void enable(); void disable(); void clear(); - bool isEnabled(); - void dump(bool asProto, std::optional<uint32_t> maxLayers, String8& result); + void dump(bool asProto, std::optional<uint32_t> maxLayers, std::string& result); std::atomic<bool> mEnabled = false; std::mutex mMutex; - TimeStatsHelper::TimeStatsGlobal timeStats; - std::unordered_map<std::string, LayerRecord> timeStatsTracker; + TimeStatsHelper::TimeStatsGlobal mTimeStats; + // Hashmap for LayerRecord with layerID as the hash key + std::unordered_map<int32_t, LayerRecord> mTimeStatsTracker; + PowerTime mPowerTime; + GlobalRecord mGlobalRecord; }; } // namespace android diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index 21f3ef3d7e..75ce4be8f2 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 @@ void TimeStatsHelper::Histogram::insert(int32_t delta) { 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::Histogram::toString() const { } 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,18 @@ std::string TimeStatsHelper::TimeStatsLayer::toString() const { 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, "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 +118,14 @@ SFTimeStatsLayerProto TimeStatsHelper::TimeStatsLayer::toProto() const { 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 +140,14 @@ SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto( 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& 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 +157,7 @@ SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto( 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 1798555be5..5f40a1a4e2 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -34,6 +34,7 @@ public: 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 +43,8 @@ public: 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,6 +58,8 @@ public: 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::string toString(std::optional<uint32_t> maxLayers) const; diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto index f29fbd187a..377612a9ae 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto +++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto @@ -20,46 +20,63 @@ package android.surfaceflinger; 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: 9 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; + // 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; } diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp new file mode 100644 index 0000000000..a1a86923f3 --- /dev/null +++ b/services/surfaceflinger/TransactionCompletedThread.cpp @@ -0,0 +1,214 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "TransactionCompletedThread" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "TransactionCompletedThread.h" + +#include <cinttypes> + +#include <binder/IInterface.h> +#include <gui/ITransactionCompletedListener.h> +#include <utils/RefBase.h> + +namespace android { + +TransactionCompletedThread::~TransactionCompletedThread() { + std::lock_guard lockThread(mThreadMutex); + + { + std::lock_guard lock(mMutex); + mKeepRunning = false; + mConditionVariable.notify_all(); + } + + if (mThread.joinable()) { + mThread.join(); + } + + { + std::lock_guard lock(mMutex); + for (const auto& [listener, listenerStats] : mListenerStats) { + listener->unlinkToDeath(mDeathRecipient); + } + } +} + +void TransactionCompletedThread::run() { + std::lock_guard lock(mMutex); + if (mRunning || !mKeepRunning) { + return; + } + mDeathRecipient = new ThreadDeathRecipient(); + mRunning = true; + + std::lock_guard lockThread(mThreadMutex); + mThread = std::thread(&TransactionCompletedThread::threadMain, this); +} + +void TransactionCompletedThread::registerPendingLatchedCallbackHandle( + const sp<CallbackHandle>& handle) { + std::lock_guard lock(mMutex); + + sp<IBinder> listener = IInterface::asBinder(handle->listener); + const auto& callbackIds = handle->callbackIds; + + mPendingTransactions[listener][callbackIds]++; +} + +void TransactionCompletedThread::addLatchedCallbackHandles( + const std::deque<sp<CallbackHandle>>& handles, nsecs_t latchTime, + const sp<Fence>& previousReleaseFence) { + std::lock_guard lock(mMutex); + + // If the previous release fences have not signaled, something as probably gone wrong. + // Store the fences and check them again before sending a callback. + if (previousReleaseFence && + previousReleaseFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) { + ALOGD("release fence from the previous frame has not signaled"); + mPreviousReleaseFences.push_back(previousReleaseFence); + } + + for (const auto& handle : handles) { + auto listener = mPendingTransactions.find(IInterface::asBinder(handle->listener)); + auto& pendingCallbacks = listener->second; + auto pendingCallback = pendingCallbacks.find(handle->callbackIds); + + if (pendingCallback != pendingCallbacks.end()) { + auto& pendingCount = pendingCallback->second; + + // Decrease the pending count for this listener + if (--pendingCount == 0) { + pendingCallbacks.erase(pendingCallback); + } + } else { + ALOGE("there are more latched callbacks than there were registered callbacks"); + } + + addCallbackHandle(handle, latchTime); + } +} + +void TransactionCompletedThread::addUnlatchedCallbackHandle(const sp<CallbackHandle>& handle) { + std::lock_guard lock(mMutex); + addCallbackHandle(handle); +} + +void TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle, + nsecs_t latchTime) { + const sp<IBinder> listener = IInterface::asBinder(handle->listener); + + // If we don't already have a reference to this listener, linkToDeath so we get a notification + // if it dies. + if (mListenerStats.count(listener) == 0) { + status_t error = listener->linkToDeath(mDeathRecipient); + if (error != NO_ERROR) { + ALOGE("cannot add callback handle because linkToDeath failed, err: %d", error); + return; + } + } + + auto& listenerStats = mListenerStats[listener]; + listenerStats.listener = handle->listener; + + auto& transactionStats = listenerStats.transactionStats[handle->callbackIds]; + transactionStats.latchTime = latchTime; + transactionStats.surfaceStats.emplace_back(handle->surfaceControl, handle->acquireTime, + handle->releasePreviousBuffer); +} + +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); + + // We should never hit this case. The release fences from the previous frame should have + // signaled long before the current frame is presented. + for (const auto& fence : mPreviousReleaseFences) { + status_t status = fence->wait(100); + if (status != NO_ERROR) { + ALOGE("previous release fence has not signaled, err %d", status); + } + } + + // For each listener + auto it = mListenerStats.begin(); + while (it != mListenerStats.end()) { + auto& [listener, listenerStats] = *it; + + // For each transaction + bool sendCallback = true; + for (auto& [callbackIds, transactionStats] : listenerStats.transactionStats) { + // If we are still waiting on the callback handles for this transaction, skip it + if (mPendingTransactions[listener].count(callbackIds) != 0) { + sendCallback = false; + break; + } + + // If the transaction has been latched + if (transactionStats.latchTime >= 0) { + if (!mPresentFence) { + sendCallback = false; + break; + } + transactionStats.presentFence = mPresentFence; + } + } + // If the listener has no pending transactions and all latched transactions have been + // presented + if (sendCallback) { + // If the listener is still alive + if (listener->isBinderAlive()) { + // Send callback + listenerStats.listener->onTransactionCompleted(listenerStats); + listener->unlinkToDeath(mDeathRecipient); + } + it = mListenerStats.erase(it); + } else { + it++; + } + } + + if (mPresentFence) { + mPresentFence.clear(); + mPreviousReleaseFences.clear(); + } + } +} + +// ----------------------------------------------------------------------- + +CallbackHandle::CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener, + const std::vector<CallbackId>& ids, const sp<IBinder>& sc) + : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {} + +} // namespace android diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h new file mode 100644 index 0000000000..1612f69d00 --- /dev/null +++ b/services/surfaceflinger/TransactionCompletedThread.h @@ -0,0 +1,116 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <condition_variable> +#include <deque> +#include <mutex> +#include <thread> +#include <unordered_map> + +#include <android-base/thread_annotations.h> + +#include <binder/IBinder.h> +#include <gui/ITransactionCompletedListener.h> +#include <ui/Fence.h> + +namespace android { + +class CallbackHandle : public RefBase { +public: + CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener, + const std::vector<CallbackId>& ids, const sp<IBinder>& sc); + + sp<ITransactionCompletedListener> listener; + std::vector<CallbackId> callbackIds; + sp<IBinder> surfaceControl; + + bool releasePreviousBuffer = false; + nsecs_t acquireTime = -1; +}; + +class TransactionCompletedThread { +public: + ~TransactionCompletedThread(); + + void run(); + + // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle + // that needs to be latched and presented this frame. This function should be called once the + // layer has received the CallbackHandle so the TransactionCompletedThread knows not to send + // a callback for that Listener/Transaction pair until that CallbackHandle has been latched and + // presented. + void registerPendingLatchedCallbackHandle(const sp<CallbackHandle>& handle); + // Notifies the TransactionCompletedThread that a pending CallbackHandle has been latched. + void addLatchedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles, nsecs_t latchTime, + const sp<Fence>& previousReleaseFence); + + // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and + // presented this frame. + void addUnlatchedCallbackHandle(const sp<CallbackHandle>& handle); + + void addPresentFence(const sp<Fence>& presentFence); + + void sendCallbacks(); + +private: + void threadMain(); + + void addCallbackHandle(const sp<CallbackHandle>& handle, nsecs_t latchTime = -1) + REQUIRES(mMutex); + + class ThreadDeathRecipient : public IBinder::DeathRecipient { + public: + // This function is a no-op. isBinderAlive needs a linked DeathRecipient to work. + // Death recipients needs a binderDied function. + // + // (isBinderAlive checks if BpBinder's mAlive is 0. mAlive is only set to 0 in sendObituary. + // sendObituary is only called if linkToDeath was called with a DeathRecipient.) + void binderDied(const wp<IBinder>& /*who*/) override {} + }; + sp<ThreadDeathRecipient> mDeathRecipient; + + struct IBinderHash { + std::size_t operator()(const sp<IBinder>& strongPointer) const { + return std::hash<IBinder*>{}(strongPointer.get()); + } + }; + + // Protects the creation and destruction of mThread + std::mutex mThreadMutex; + + std::thread mThread GUARDED_BY(mThreadMutex); + + std::mutex mMutex; + std::condition_variable_any mConditionVariable; + + std::unordered_map< + sp<IBinder /*listener*/>, + std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>, + IBinderHash> + mPendingTransactions GUARDED_BY(mMutex); + std::unordered_map<sp<IBinder /*listener*/>, ListenerStats, IBinderHash> mListenerStats + GUARDED_BY(mMutex); + + bool mRunning GUARDED_BY(mMutex) = false; + bool mKeepRunning GUARDED_BY(mMutex) = true; + + sp<Fence> mPresentFence GUARDED_BY(mMutex); + std::vector<sp<Fence>> mPreviousReleaseFences GUARDED_BY(mMutex); +}; + +} // namespace android diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h deleted file mode 100644 index b11d0576c4..0000000000 --- a/services/surfaceflinger/Transform.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_TRANSFORM_H -#define ANDROID_TRANSFORM_H - -#include <stdint.h> -#include <sys/types.h> - -#include <ui/Point.h> -#include <ui/Rect.h> -#include <math/vec2.h> -#include <math/vec3.h> - -#include <gui/ISurfaceComposer.h> - -#include <hardware/hardware.h> - -namespace android { - -class Region; - -// --------------------------------------------------------------------------- - -class Transform -{ -public: - Transform(); - Transform(const Transform& other); - explicit Transform(uint32_t orientation); - ~Transform(); - - enum orientation_flags { - ROT_0 = 0x00000000, - FLIP_H = HAL_TRANSFORM_FLIP_H, - FLIP_V = HAL_TRANSFORM_FLIP_V, - ROT_90 = HAL_TRANSFORM_ROT_90, - ROT_180 = FLIP_H|FLIP_V, - ROT_270 = ROT_180|ROT_90, - ROT_INVALID = 0x80 - }; - - static orientation_flags fromRotation(ISurfaceComposer::Rotation rotation); - - enum type_mask { - IDENTITY = 0, - TRANSLATE = 0x1, - ROTATE = 0x2, - SCALE = 0x4, - UNKNOWN = 0x8 - }; - - // query the transform - bool preserveRects() const; - uint32_t getType() const; - uint32_t getOrientation() const; - - const vec3& operator [] (size_t i) const; // returns column i - float tx() const; - float ty() const; - - // modify the transform - void reset(); - void set(float tx, float ty); - void set(float a, float b, float c, float d); - status_t set(uint32_t flags, float w, float h); - - // transform data - Rect makeBounds(int w, int h) const; - vec2 transform(int x, int y) const; - Region transform(const Region& reg) const; - Rect transform(const Rect& bounds, - bool roundOutwards = false) const; - FloatRect transform(const FloatRect& bounds) const; - Transform operator * (const Transform& rhs) const; - // assumes the last row is < 0 , 0 , 1 > - vec2 transform(const vec2& v) const; - vec3 transform(const vec3& v) const; - - Transform inverse() const; - - // for debugging - void dump(const char* name) const; - -private: - struct mat33 { - vec3 v[3]; - inline const vec3& operator [] (int i) const { return v[i]; } - inline vec3& operator [] (int i) { return v[i]; } - }; - - enum { UNKNOWN_TYPE = 0x80000000 }; - - uint32_t type() const; - static bool absIsOne(float f); - static bool isZero(float f); - - mat33 mMatrix; - mutable uint32_t mType; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif /* ANDROID_TRANSFORM_H */ diff --git a/services/surfaceflinger/clz.h b/services/surfaceflinger/clz.h deleted file mode 100644 index a4c5262ddf..0000000000 --- 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/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp index fcf42f00a9..d020a394ce 100644 --- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp +++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp @@ -31,17 +31,12 @@ bool sortLayers(LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs 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,77 +47,81 @@ const LayerProtoParser::LayerGlobal LayerProtoParser::generateLayerGlobalInfo( 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; - - 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)); +LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(const LayersProto& layersProto) { + LayerTree layerTree; + layerTree.allLayers = generateLayerList(layersProto); + + // 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.windowType = layerProto.window_type(); + layer.appId = layerProto.app_id(); + layer.hwcCompositionType = layerProto.hwc_composition_type(); + layer.isProtected = layerProto.is_protected(); + layer.cornerRadius = layerProto.corner_radius(); return layer; } @@ -186,9 +185,7 @@ void LayerProtoParser::updateChildrenAndRelative(const LayerProto& layerProto, 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)]); } } @@ -211,29 +208,28 @@ void LayerProtoParser::updateChildrenAndRelative(const LayerProto& layerProto, } } -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 +240,13 @@ std::string LayerProtoParser::layerToString(LayerProtoParser::Layer* layer) { 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 +294,8 @@ std::string LayerProtoParser::Layer::to_string() const { z, static_cast<double>(position.x), static_cast<double>(position.y), size.x, size.y); - StringAppendF(&result, "crop=%s, finalCrop=%s, ", crop.to_string().c_str(), - finalCrop.to_string().c_str()); + StringAppendF(&result, "crop=%s, ", crop.to_string().c_str()); + StringAppendF(&result, "cornerRadius=%f, ", cornerRadius); StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate); StringAppendF(&result, "dataspace=%s, ", dataspace.c_str()); StringAppendF(&result, "defaultPixelFormat=%s, ", pixelFormat.c_str()); @@ -312,6 +308,7 @@ std::string LayerProtoParser::Layer::to_string() const { 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); diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h index 74a6f28f2b..a794ca57a3 100644 --- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h +++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h @@ -80,7 +80,7 @@ public: 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 +92,6 @@ public: float2 requestedPosition; int2 size; LayerProtoParser::Rect crop; - LayerProtoParser::Rect finalCrop; bool isOpaque; bool invalidate; std::string dataspace; @@ -105,6 +104,7 @@ public: Layer* parent = 0; Layer* zOrderRelativeOf = 0; LayerProtoParser::ActiveBuffer activeBuffer; + Transform bufferTransform; int32_t queuedFrames; bool refreshPending; LayerProtoParser::Rect hwcFrame; @@ -114,6 +114,7 @@ public: int32_t appId; int32_t hwcCompositionType; bool isProtected; + float cornerRadius; std::string to_string() const; }; @@ -126,13 +127,22 @@ public: 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 @@ private: 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 edf56abc4a..b10043877c 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -41,7 +41,7 @@ message LayerProto { // The layer's crop in it's own bounds. optional RectProto crop = 14; // The layer's crop in it's parent's bounds. - optional RectProto final_crop = 15; + optional RectProto final_crop = 15 [deprecated=true]; optional bool is_opaque = 16; optional bool invalidate = 17; optional string dataspace = 18; @@ -84,6 +84,11 @@ message LayerProto { optional 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. + optional TransformProto buffer_transform = 39; + optional int32 effective_scaling_mode = 40; + // Layer's corner radius. + optional float corner_radius = 41; } message PositionProto { diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index d0900e9a5a..92ae87b5a8 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -21,15 +21,15 @@ #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 <cutils/sched_policy.h> -#include <binder/IServiceManager.h> #include <binder/IPCThreadState.h> -#include <binder/ProcessState.h> #include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <configstore/Utils.h> +#include <cutils/sched_policy.h> #include <displayservice/DisplayService.h> #include <hidl/LegacySupport.h> -#include <configstore/Utils.h> #include "SurfaceFlinger.h" +#include "SurfaceFlingerFactory.h" using namespace android; @@ -84,7 +84,7 @@ int main(int, char**) { 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/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index c511c5e753..604aa7df56 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -17,6 +17,7 @@ cc_test { defaults: ["surfaceflinger_defaults"], test_suites: ["device-tests"], srcs: [ + "Credentials_test.cpp", "Stress_test.cpp", "SurfaceInterceptor_test.cpp", "Transaction_test.cpp", diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp new file mode 100644 index 0000000000..a73ec6c7ef --- /dev/null +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -0,0 +1,331 @@ +#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; + +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::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); + DisplayInfo info; + 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()); + + // No one else can init the client. + setBinUID(); + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_INIT, mComposerClient->initCheck()); +} + +TEST_F(CredentialsTest, GetBuiltInDisplayAccessTest) { + std::function<bool()> condition = [=]() { + sp<IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + return (display != 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(); + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + 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) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + 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, SetActiveConfigTest) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + 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) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + 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, CreateSurfaceTest) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + const ssize_t displayWidth = info.w; + const ssize_t displayHeight = info.h; + + std::function<bool()> condition = [=]() { + mBGSurfaceControl = + mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight, + PIXEL_FORMAT_RGBA_8888, 0); + return mBGSurfaceControl != nullptr && mBGSurfaceControl->isValid(); + }; + ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false)); +} + +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) { + sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + 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)); +} +} // namespace android diff --git a/services/surfaceflinger/tests/Stress_test.cpp b/services/surfaceflinger/tests/Stress_test.cpp index 4577153df2..3e1be8e288 100644 --- a/services/surfaceflinger/tests/Stress_test.cpp +++ b/services/surfaceflinger/tests/Stress_test.cpp @@ -101,10 +101,7 @@ TEST(LayerProtoStress, mem_info) { 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 cca84e552f..91999ae7f0 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.*" } } diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp index de78c3f355..e506757867 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -42,13 +42,17 @@ constexpr uint32_t BUFFER_UPDATES = 18; 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 @@ static void disableInterceptor() { 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 @@ int32_t getDisplayId(const std::string& displayName) { 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 @@ protected: 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 @@ public: 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 @@ public: 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 @@ public: 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,93 @@ public: 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); +void SurfaceInterceptorTest::captureInTransaction(TestTransactionAction action, Trace* outTrace) { + enableInterceptor(); + setupBackgroundSurface(); + runInTransaction(action); + disableInterceptor(); + ASSERT_EQ(NO_ERROR, readProtoFile(outTrace)); + preProcessTrace(*outTrace); +} + +void SurfaceInterceptorTest::capture(TestAction action, Trace* outTrace) { + enableInterceptor(); + setupBackgroundSurface(); + (this->*action)(); + disableInterceptor(); + ASSERT_EQ(NO_ERROR, readProtoFile(outTrace)); + preProcessTrace(*outTrace); +} + +void SurfaceInterceptorTest::setupBackgroundSurface() { + 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(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::preProcessTrace(const Trace& trace) { + mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_SURFACE_NAME); +} + +void SurfaceInterceptorTest::captureTest(TestTransactionAction action, + TestBooleanVerification verification) { Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE((this->*verification)(&capturedTrace)); + captureInTransaction(action, &capturedTrace); + ASSERT_TRUE((this->*verification)(capturedTrace)); } -void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&), - Increment::IncrementCase incrementCase) -{ - runInTransaction(action, true); +void SurfaceInterceptorTest::captureTest(TestTransactionAction action, + Increment::IncrementCase incrementCase) { Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE(singleIncrementFound(&capturedTrace, incrementCase)); + captureInTransaction(action, &capturedTrace); + ASSERT_TRUE(singleIncrementFound(capturedTrace, incrementCase)); } -void SurfaceInterceptorTest::captureTest(void (SurfaceInterceptorTest::* action)(Transaction&), - SurfaceChange::SurfaceChangeCase changeCase) -{ - runInTransaction(action, true); +void SurfaceInterceptorTest::captureTest(TestTransactionAction action, + SurfaceChange::SurfaceChangeCase changeCase) { Trace capturedTrace; - ASSERT_EQ(NO_ERROR, readProtoFile(&capturedTrace)); - ASSERT_TRUE(surfaceUpdateFound(&capturedTrace, changeCase)); + captureInTransaction(action, &capturedTrace); + ASSERT_TRUE(surfaceUpdateFound(capturedTrace, changeCase)); } -void SurfaceInterceptorTest::runInTransaction(void (SurfaceInterceptorTest::* action)(Transaction&), - bool intercepted) -{ - if (intercepted) { - enableInterceptor(); - } +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 +316,16 @@ void SurfaceInterceptorTest::alphaUpdate(Transaction& t) { 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 +358,8 @@ void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) { } 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 +369,6 @@ void SurfaceInterceptorTest::displayCreation(Transaction&) { void SurfaceInterceptorTest::displayDeletion(Transaction&) { sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); - mTargetId = getDisplayId(DISPLAY_NAME.string()); SurfaceComposerClient::destroyDisplay(testDisplay); } @@ -346,9 +376,9 @@ void SurfaceInterceptorTest::runAllUpdates() { 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 +410,8 @@ bool SurfaceInterceptorTest::positionUpdateFound(const SurfaceChange& change, bo 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 +422,7 @@ bool SurfaceInterceptorTest::sizeUpdateFound(const SurfaceChange& change, bool f 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 +432,28 @@ bool SurfaceInterceptorTest::alphaUpdateFound(const SurfaceChange& change, bool 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 +466,38 @@ bool SurfaceInterceptorTest::cropUpdateFound(const SurfaceChange& change, bool f 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 +505,72 @@ bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChang 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 +591,8 @@ bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace, 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 +628,12 @@ bool SurfaceInterceptorTest::surfaceUpdateFound(Trace* trace, 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 +645,23 @@ void SurfaceInterceptorTest::assertAllUpdatesFound(Trace* trace) { } 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 +672,45 @@ bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bo 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 +721,9 @@ bool SurfaceInterceptorTest::singleIncrementFound(Trace* trace, 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 +752,9 @@ TEST_F(SurfaceInterceptorTest, InterceptCropUpdateWorks) { 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 +797,8 @@ TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) { } 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) { @@ -808,16 +807,15 @@ TEST_F(SurfaceInterceptorTest, InterceptSurfaceCreationWorks) { } TEST_F(SurfaceInterceptorTest, InterceptSurfaceDeletionWorks) { + enableInterceptor(); 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)); + ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceDeletion)); } TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) { @@ -826,21 +824,24 @@ TEST_F(SurfaceInterceptorTest, InterceptDisplayCreationWorks) { } 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 +855,7 @@ TEST_F(SurfaceInterceptorTest, InterceptWhileBufferUpdatesWorks) { TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) { enableInterceptor(); + setupBackgroundSurface(); std::thread bufferUpdates(&SurfaceInterceptorTest::nBufferUpdates, this); std::thread surfaceUpdates(&SurfaceInterceptorTest::runAllUpdates, this); runInTransaction(&SurfaceInterceptorTest::surfaceCreation); @@ -863,10 +865,11 @@ TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) { 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 5108279043..037d32faff 100644 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -15,9 +15,12 @@ */ #include <algorithm> +#include <chrono> +#include <cinttypes> #include <functional> #include <limits> #include <ostream> +#include <thread> #include <gtest/gtest.h> @@ -30,6 +33,7 @@ #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> +#include <ui/ColorSpace.h> #include <ui/DisplayInfo.h> #include <ui/Rect.h> #include <utils/String8.h> @@ -62,38 +66,55 @@ const Color Color::WHITE{255, 255, 255, 255}; const Color Color::BLACK{0, 0, 0, 255}; const Color Color::TRANSPARENT{0, 0, 0, 0}; +using android::hardware::graphics::common::V1_1::BufferUsage; +using namespace std::chrono_literals; + std::ostream& operator<<(std::ostream& os, const Color& color) { os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a); return os; } // Fill a region with the specified color. -void fillBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, const Color& color) { - int32_t x = rect.left; - int32_t y = rect.top; - int32_t width = rect.right - rect.left; - int32_t height = rect.bottom - rect.top; - - if (x < 0) { - width += x; - x = 0; +void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, + const Color& color) { + Rect r(0, 0, buffer.width, buffer.height); + if (!r.intersect(rect, &r)) { + return; } - if (y < 0) { - height += y; - y = 0; - } - if (x + width > buffer.width) { - x = std::min(x, buffer.width); - width = buffer.width - x; + + int32_t width = r.right - r.left; + int32_t height = r.bottom - r.top; + + for (int32_t row = 0; row < height; row++) { + uint8_t* dst = + static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4; + for (int32_t column = 0; column < width; column++) { + dst[0] = color.r; + dst[1] = color.g; + dst[2] = color.b; + dst[3] = color.a; + dst += 4; + } } - if (y + height > buffer.height) { - y = std::min(y, buffer.height); - height = buffer.height - y; +} + +// Fill a region with the specified color. +void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, const Color& color) { + Rect r(0, 0, buffer->width, buffer->height); + if (!r.intersect(rect, &r)) { + return; } - 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; @@ -101,6 +122,7 @@ void fillBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, const dst += 4; } } + buffer->unlock(); } // Check if a region has the specified color. @@ -169,17 +191,15 @@ static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, // 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()) { + static void captureScreen(std::unique_ptr<ScreenCapture>* sc) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); 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); + sf->captureScreen(display, &outBuffer, Rect(), 0, 0, false)); + *sc = std::make_unique<ScreenCapture>(outBuffer); } static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, @@ -299,18 +319,28 @@ protected: ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient"; ASSERT_NO_FATAL_FAILURE(SetUpDisplay()); + + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + sp<IBinder> binder = sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); + ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed)); } - sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, - uint32_t flags = 0) { + virtual void TearDown() { + mBlackBgSurface = 0; + mClient->dispose(); + mClient = 0; + } + + 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 = - mClient->createSurface(String8(name), width, height, PIXEL_FORMAT_RGBA_8888, flags); - EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl"; + createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent); - status_t error = Transaction() - .setLayerStack(layer, mDisplayLayerStack) - .setLayer(layer, mLayerZBase) - .apply(); + Transaction t; + t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase); + + status_t error = t.apply(); if (error != NO_ERROR) { ADD_FAILURE() << "failed to initialize SurfaceControl"; layer.clear(); @@ -319,7 +349,21 @@ protected: return layer; } - ANativeWindow_Buffer getLayerBuffer(const sp<SurfaceControl>& layer) { + virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client, + const char* name, uint32_t width, uint32_t height, + PixelFormat format, uint32_t flags, + SurfaceControl* parent = nullptr) { + auto layer = client->createSurface(String8(name), width, height, format, flags, parent); + EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl"; + return layer; + } + + virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, + uint32_t flags = 0, SurfaceControl* parent = nullptr) { + return createLayer(mClient, name, width, height, flags, parent); + } + + ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) { // wait for previous transactions (such as setSize) to complete Transaction().apply(true); @@ -329,40 +373,108 @@ protected: return buffer; } - void postLayerBuffer(const sp<SurfaceControl>& layer) { + void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) { ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost()); // wait for the newly posted buffer to be latched waitForLayerBuffers(); } - void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color) { + virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { ANativeWindow_Buffer buffer; - ASSERT_NO_FATAL_FAILURE(buffer = getLayerBuffer(layer)); - fillBufferColor(buffer, Rect(0, 0, buffer.width, buffer.height), color); - postLayerBuffer(layer); + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + fillANativeWindowBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color); + postBufferQueueLayerBuffer(layer); + } + + virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { + sp<GraphicBuffer> buffer = + new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color); + Transaction().setBuffer(layer, buffer).apply(); + } + + void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { + switch (mLayerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight); + break; + default: + ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType; + } } - void fillLayerQuadrant(const sp<SurfaceControl>& layer, const Color& topLeft, + 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) { + 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; + } + } + + 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 = getLayerBuffer(layer)); - ASSERT_TRUE(buffer.width % 2 == 0 && buffer.height % 2 == 0); + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 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); + 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); - postLayerBuffer(layer); + postBufferQueueLayerBuffer(layer); } - sp<ScreenCapture> screenshot() { - sp<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot, mLayerZBase); + 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; } @@ -372,10 +484,18 @@ protected: 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; + void setRelativeZBasicHelper(uint32_t layerType); + void setRelativeZGroupHelper(uint32_t layerType); + void setAlphaBasicHelper(uint32_t layerType); + + sp<SurfaceControl> mBlackBgSurface; + bool mColorManagementUsed; + private: void SetUpDisplay() { mDisplay = mClient->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain); @@ -386,6 +506,8 @@ private: SurfaceComposerClient::getDisplayInfo(mDisplay, &info); mDisplayWidth = info.w; mDisplayHeight = info.h; + mDisplayRect = + Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight)); // After a new buffer is queued, SurfaceFlinger is notified and will // latch the new buffer on next vsync. Let's heuristically wait for 3 @@ -393,42 +515,96 @@ private: 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; }; -TEST_F(LayerTransactionTest, SetPositionBasic) { +class LayerTypeTransactionTest : public LayerTransactionTest, + public ::testing::WithParamInterface<uint32_t> { +public: + LayerTypeTransactionTest() { mLayerType = GetParam(); } + + sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, + uint32_t flags = 0, SurfaceControl* parent = nullptr) override { + // 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; +}; + +INSTANTIATE_TEST_CASE_P( + LayerTypeTransactionTests, LayerTypeTransactionTest, + ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue), + static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState))); + +TEST_F(LayerTransactionTest, 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"); + const Rect rect(0, 0, 32, 32); auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); } Transaction().setPosition(layer, 5, 10).apply(); { SCOPED_TRACE("new position"); + const Rect rect(5, 10, 37, 42); auto shot = screenshot(); - shot->expectColor(Rect(5, 10, 37, 42), Color::RED); - shot->expectBorder(Rect(5, 10, 37, 42), Color::BLACK); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); } } -TEST_F(LayerTransactionTest, SetPositionRounding) { +TEST_F(LayerTransactionTest, 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 @@ -447,28 +623,28 @@ TEST_F(LayerTransactionTest, SetPositionRounding) { } } -TEST_F(LayerTransactionTest, SetPositionOutOfBounds) { +TEST_F(LayerTransactionTest, 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); + screenshot()->expectColor(mDisplayRect, Color::BLACK); } Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply(); { SCOPED_TRACE("positive coordinates"); - screenshot()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); + screenshot()->expectColor(mDisplayRect, Color::BLACK); } } -TEST_F(LayerTransactionTest, SetPositionPartiallyOutOfBounds) { +TEST_F(LayerTransactionTest, 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(); @@ -486,10 +662,10 @@ TEST_F(LayerTransactionTest, SetPositionPartiallyOutOfBounds) { } } -TEST_F(LayerTransactionTest, SetPositionWithResize) { +TEST_F(LayerTransactionTest, 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 @@ -497,21 +673,22 @@ TEST_F(LayerTransactionTest, SetPositionWithResize) { { 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); + 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); } } -TEST_F(LayerTransactionTest, SetPositionWithNextResize) { +TEST_F(LayerTransactionTest, 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(); @@ -533,17 +710,17 @@ TEST_F(LayerTransactionTest, SetPositionWithNextResize) { } // 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); } } -TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow) { +TEST_F(LayerTransactionTest, 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() @@ -557,43 +734,45 @@ TEST_F(LayerTransactionTest, SetPositionWithNextResizeScaleToWindow) { screenshot()->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); } } -TEST_F(LayerTransactionTest, SetSizeBasic) { +TEST_F(LayerTransactionTest, 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); + 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); + const Rect rect(0, 0, 64, 64); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); } } -TEST_F(LayerTransactionTest, SetSizeInvalid) { +TEST_P(LayerTypeTransactionTest, SetSizeInvalid) { // cannot test robustness against invalid sizes (zero or really huge) } -TEST_F(LayerTransactionTest, SetSizeWithScaleToWindow) { +TEST_F(LayerTransactionTest, 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() @@ -603,13 +782,13 @@ TEST_F(LayerTransactionTest, SetSizeWithScaleToWindow) { screenshot()->expectColor(Rect(0, 0, 64, 64), Color::RED); } -TEST_F(LayerTransactionTest, SetZBasic) { +TEST_P(LayerTypeTransactionTest, 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(); { @@ -624,43 +803,62 @@ TEST_F(LayerTransactionTest, SetZBasic) { } } -TEST_F(LayerTransactionTest, SetZNegative) { +TEST_P(LayerTypeTransactionTest, 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 = screenshot(); + 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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN); } } -TEST_F(LayerTransactionTest, SetRelativeZBasic) { +void LayerTransactionTest::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)); - - Transaction() - .setPosition(layerG, 16, 16) - .setRelativeLayer(layerG, layerR->getHandle(), 1) - .apply(); + 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)); + + 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(); @@ -677,44 +875,77 @@ TEST_F(LayerTransactionTest, SetRelativeZBasic) { } } -TEST_F(LayerTransactionTest, SetRelativeZNegative) { +TEST_F(LayerTransactionTest, SetRelativeZBasic_BufferQueue) { + ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); +} + +TEST_F(LayerTransactionTest, SetRelativeZBasic_BufferState) { + ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); +} + +TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) { + sp<SurfaceControl> parent = + LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceContainer); + Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply(); sp<SurfaceControl> layerR; sp<SurfaceControl> layerG; sp<SurfaceControl> layerB; ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32)); + + Transaction() + .reparent(layerB, parent->getHandle()) + .apply(); // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2 Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply(); - sp<ScreenCapture> screenshot; + std::unique_ptr<ScreenCapture> screenshot; // only layerB is in this range - ScreenCapture::captureScreen(&screenshot, -2, -1); + sp<IBinder> parentHandle = parent->getHandle(); + ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32)); screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); } -TEST_F(LayerTransactionTest, SetRelativeZGroup) { +void LayerTransactionTest::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(); @@ -764,14 +995,22 @@ TEST_F(LayerTransactionTest, SetRelativeZGroup) { } } -TEST_F(LayerTransactionTest, SetRelativeZBug64572777) { +TEST_F(LayerTransactionTest, SetRelativeZGroup_BufferQueue) { + ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); +} + +TEST_F(LayerTransactionTest, SetRelativeZGroup_BufferState) { + ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); +} + +TEST_P(LayerTypeTransactionTest, 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) @@ -783,15 +1022,15 @@ TEST_F(LayerTransactionTest, SetRelativeZBug64572777) { screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); } -TEST_F(LayerTransactionTest, SetFlagsHidden) { +TEST_P(LayerTypeTransactionTest, 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); + screenshot()->expectColor(mDisplayRect, Color::BLACK); } Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply(); @@ -801,14 +1040,14 @@ TEST_F(LayerTransactionTest, SetFlagsHidden) { } } -TEST_F(LayerTransactionTest, SetFlagsOpaque) { +TEST_P(LayerTypeTransactionTest, 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) @@ -827,10 +1066,10 @@ TEST_F(LayerTransactionTest, SetFlagsOpaque) { } } -TEST_F(LayerTransactionTest, SetFlagsSecure) { +TEST_P(LayerTypeTransactionTest, SetFlagsSecure) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); sp<ISurfaceComposer> composer = ComposerService::getComposerService(); sp<GraphicBuffer> outBuffer; @@ -838,28 +1077,26 @@ TEST_F(LayerTransactionTest, SetFlagsSecure) { .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) .apply(true); ASSERT_EQ(PERMISSION_DENIED, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, mLayerZBase, mLayerZBase, - false)); + composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true); ASSERT_EQ(NO_ERROR, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, mLayerZBase, mLayerZBase, - false)); + composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); } -TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) { +TEST_F(LayerTransactionTest, 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(); @@ -875,10 +1112,61 @@ TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) { 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(); + shot->expectColor(top, Color::RED); + shot->expectColor(bottom, Color::BLACK); + } +} + +TEST_F(LayerTransactionTest, 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 = screenshot(); + shot->expectColor(top, Color::BLACK); + shot->expectColor(bottom, Color::RED); + } + + Transaction().setTransparentRegionHint(layer, Region(bottom)).apply(); + { + SCOPED_TRACE("transparent region hint intermediate"); + auto shot = screenshot(); + 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 = screenshot(); @@ -887,7 +1175,7 @@ TEST_F(LayerTransactionTest, SetTransparentRegionHintBasic) { } } -TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds) { +TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) { sp<SurfaceControl> layerTransparent; sp<SurfaceControl> layerR; ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32)); @@ -895,30 +1183,64 @@ TEST_F(LayerTransactionTest, SetTransparentRegionHintOutOfBounds) { // 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)); + ASSERT_NO_FATAL_FAILURE( + fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32)); screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED); } -TEST_F(LayerTransactionTest, SetAlphaBasic) { - 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})); +TEST_F(LayerTransactionTest, 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() - .setAlpha(layer1, 0.25f) - .setAlpha(layer2, 0.75f) - .setPosition(layer2, 16, 0) - .setLayer(layer2, mLayerZBase + 1) + .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)); + screenshot()->expectColor(Rect(16, 16, 48, 48), Color::RED); +} + +void LayerTransactionTest::setAlphaBasicHelper(uint32_t layerType) { + sp<SurfaceControl> layer1; + sp<SurfaceControl> layer2; + 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)); + + 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(); uint8_t r = 16; // 64 * 0.25f @@ -931,11 +1253,19 @@ TEST_F(LayerTransactionTest, SetAlphaBasic) { } } -TEST_F(LayerTransactionTest, SetAlphaClamped) { +TEST_F(LayerTransactionTest, SetAlphaBasic_BufferQueue) { + ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); +} + +TEST_F(LayerTransactionTest, SetAlphaBasic_BufferState) { + ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); +} + +TEST_P(LayerTypeTransactionTest, 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(); { @@ -950,15 +1280,72 @@ TEST_F(LayerTransactionTest, SetAlphaClamped) { } } +TEST_P(LayerTypeTransactionTest, 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 = screenshot(); + // 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(LayerTypeTransactionTest, 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 = screenshot(); + // 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_F(LayerTransactionTest, 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() + .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) + .setLayer(colorLayer, mLayerZBase + 1) + .apply(); - Transaction().setLayer(colorLayer, mLayerZBase + 1).apply(); { SCOPED_TRACE("default color"); screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); @@ -978,10 +1365,14 @@ TEST_F(LayerTransactionTest, SetColorBasic) { TEST_F(LayerTransactionTest, SetColorClamped) { sp<SurfaceControl> colorLayer; - ASSERT_NO_FATAL_FAILURE( - colorLayer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceColor)); + 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(); - Transaction().setColor(colorLayer, half3(2.0f, -1.0f, 0.0f)).apply(); screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); } @@ -989,9 +1380,11 @@ TEST_F(LayerTransactionTest, 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; @@ -1014,10 +1407,11 @@ TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) { 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); @@ -1034,25 +1428,25 @@ TEST_F(LayerTransactionTest, SetColorWithParentAlpha_Bug74220420) { tolerance); } -TEST_F(LayerTransactionTest, SetColorWithBuffer) { +TEST_P(LayerTypeTransactionTest, 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); } -TEST_F(LayerTransactionTest, SetLayerStackBasic) { +TEST_P(LayerTypeTransactionTest, 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); + screenshot()->expectColor(mDisplayRect, Color::BLACK); } Transaction().setLayerStack(layer, mDisplayLayerStack).apply(); @@ -1062,11 +1456,11 @@ TEST_F(LayerTransactionTest, SetLayerStackBasic) { } } -TEST_F(LayerTransactionTest, SetMatrixBasic) { +TEST_F(LayerTransactionTest, 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(); { @@ -1104,11 +1498,57 @@ TEST_F(LayerTransactionTest, SetMatrixBasic) { } } -TEST_F(LayerTransactionTest, SetMatrixRot45) { +TEST_F(LayerTransactionTest, SetMatrixBasic_BufferState) { 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)); + 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"); + screenshot()->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"); + screenshot()->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"); + screenshot()->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"); + screenshot()->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"); + screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, Color::BLUE, + Color::WHITE); + } +} + +TEST_F(LayerTransactionTest, SetMatrixRot45_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + 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; @@ -1127,31 +1567,33 @@ TEST_F(LayerTransactionTest, SetMatrixRot45) { shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE); } -TEST_F(LayerTransactionTest, SetMatrixWithResize) { +TEST_F(LayerTransactionTest, 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); + 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); + screenshot()->expectColor(rect, Color::RED); } } -TEST_F(LayerTransactionTest, SetMatrixWithScaleToWindow) { +TEST_F(LayerTransactionTest, 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() @@ -1162,11 +1604,11 @@ TEST_F(LayerTransactionTest, SetMatrixWithScaleToWindow) { screenshot()->expectColor(Rect(0, 0, 128, 128), Color::RED); } -TEST_F(LayerTransactionTest, SetOverrideScalingModeBasic) { +TEST_F(LayerTransactionTest, 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 @@ -1182,22 +1624,67 @@ TEST_F(LayerTransactionTest, SetOverrideScalingModeBasic) { } } -TEST_F(LayerTransactionTest, SetCropBasic) { +TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + + 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_F(LayerTransactionTest, SetCropBasic_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 Rect crop(8, 8, 24, 24); - Transaction().setCrop(layer, crop).apply(); + Transaction().setCrop_legacy(layer, crop).apply(); auto shot = screenshot(); shot->expectColor(crop, Color::RED); shot->expectBorder(crop, Color::BLACK); } -TEST_F(LayerTransactionTest, SetCropEmpty) { +TEST_F(LayerTransactionTest, 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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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(); + screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } + + { + SCOPED_TRACE("negative rect"); + Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply(); + screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } +} + +TEST_F(LayerTransactionTest, 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"); @@ -1212,52 +1699,112 @@ TEST_F(LayerTransactionTest, SetCropEmpty) { } } -TEST_F(LayerTransactionTest, SetCropOutOfBounds) { +TEST_F(LayerTransactionTest, 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(); + Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply(); auto shot = screenshot(); shot->expectColor(Rect(0, 0, 32, 32), Color::RED); shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetCropWithTranslation) { +TEST_F(LayerTransactionTest, SetCropOutOfBounds_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", mDisplayWidth, mDisplayHeight / 2, + ISurfaceComposerClient::eFXSurfaceBufferState)); + sp<GraphicBuffer> buffer = + new GraphicBuffer(mDisplayWidth, mDisplayHeight / 2, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE); + fillGraphicBufferColor(buffer, Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2), + Color::RED); + + Transaction().setBuffer(layer, buffer).apply(); + + // Partially out of bounds in the negative (upper left) direction + Transaction().setCrop(layer, Rect(-128, -128, mDisplayWidth, mDisplayHeight / 4)).apply(); + { + SCOPED_TRACE("out of bounds, negative (upper left) direction"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLUE); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK); + } + + // Partially out of bounds in the positive (lower right) direction + Transaction() + .setCrop(layer, Rect(0, mDisplayHeight / 4, mDisplayWidth + 1, mDisplayHeight)) + .apply(); + { + SCOPED_TRACE("out of bounds, positive (lower right) direction"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), 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 = screenshot(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE); + shot->expectColor(Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2), + Color::RED); + } +} + +TEST_F(LayerTransactionTest, 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(); + Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply(); auto shot = screenshot(); shot->expectColor(crop + position, Color::RED); shot->expectBorder(crop + position, Color::BLACK); } -TEST_F(LayerTransactionTest, SetCropWithScale) { +TEST_F(LayerTransactionTest, 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 = screenshot(); + shot->expectColor(frame, Color::RED); + shot->expectBorder(frame, Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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(); shot->expectColor(Rect(16, 16, 48, 48), Color::RED); shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK); } -TEST_F(LayerTransactionTest, SetCropWithResize) { +TEST_F(LayerTransactionTest, 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(); @@ -1265,7 +1812,7 @@ TEST_F(LayerTransactionTest, SetCropWithResize) { 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(); @@ -1274,19 +1821,22 @@ TEST_F(LayerTransactionTest, SetCropWithResize) { } } -TEST_F(LayerTransactionTest, SetCropWithNextResize) { +TEST_F(LayerTransactionTest, 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); } - 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); @@ -1299,7 +1849,7 @@ TEST_F(LayerTransactionTest, SetCropWithNextResize) { } // 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(); @@ -1308,14 +1858,14 @@ TEST_F(LayerTransactionTest, SetCropWithNextResize) { } } -TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow) { +TEST_F(LayerTransactionTest, 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) @@ -1329,7 +1879,7 @@ TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow) { // 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"); @@ -1339,173 +1889,1235 @@ TEST_F(LayerTransactionTest, SetCropWithNextResizeScaleToWindow) { } } -TEST_F(LayerTransactionTest, SetFinalCropBasic) { +TEST_F(LayerTransactionTest, 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(); + Transaction().setFrame(layer, frame).apply(); auto shot = screenshot(); - shot->expectColor(crop, Color::RED); - shot->expectBorder(crop, Color::BLACK); + shot->expectColor(frame, Color::RED); + shot->expectBorder(frame, Color::BLACK); } -TEST_F(LayerTransactionTest, SetFinalCropEmpty) { +TEST_F(LayerTransactionTest, 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(); + screenshot()->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(); + screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); } } -TEST_F(LayerTransactionTest, SetFinalCropOutOfBounds) { +TEST_F(LayerTransactionTest, 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(); + // A parentless layer will default to a frame with the same size as the buffer auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectColor(Rect(0, 0, 10, 10), Color::RED); + shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetFinalCropWithTranslation) { +TEST_F(LayerTransactionTest, 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)); + + 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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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(); + + std::this_thread::sleep_for(500ms); + + Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply(); + + auto shot = screenshot(); + shot->expectColor(Rect(16, 16, 48, 48), Color::RED); + shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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(); - // 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); + 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_F(LayerTransactionTest, SetFinalCropWithScale) { +TEST_F(LayerTransactionTest, SetBufferBasic_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)); - // final crop is not affected by matrix - Transaction() - .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f) - .setFinalCrop(layer, Rect(8, 8, 24, 24)) - .apply(); auto shot = screenshot(); - shot->expectColor(Rect(8, 8, 24, 24), Color::RED); - shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } -TEST_F(LayerTransactionTest, SetFinalCropWithResize) { +TEST_F(LayerTransactionTest, SetBufferMultipleBuffers_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 SetCropWithResize - Transaction().setFinalCrop(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply(); { - SCOPED_TRACE("resize pending"); + SCOPED_TRACE("set buffer 1"); auto shot = screenshot(); - shot->expectColor(Rect(8, 8, 24, 24), Color::RED); - shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); } - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); + { - SCOPED_TRACE("resize applied"); + SCOPED_TRACE("set buffer 2"); auto shot = screenshot(); - shot->expectColor(Rect(8, 8, 16, 16), Color::RED); - shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("set buffer 3"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + } +} + +TEST_F(LayerTransactionTest, 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 = screenshot(); + 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 = screenshot(); + 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 = screenshot(); + 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 = screenshot(); + 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_F(LayerTransactionTest, SetFinalCropWithNextResize) { +TEST_F(LayerTransactionTest, SetTransformRotate90_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(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); - // 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_ROT_90) + .apply(); + + screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE, + Color::GREEN, true /* filtered */); +} + +TEST_F(LayerTransactionTest, SetTransformFlipH_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_H) + .apply(); + + screenshot()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE, + Color::BLUE, true /* filtered */); +} + +TEST_F(LayerTransactionTest, 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(); + + screenshot()->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_F(LayerTransactionTest, SetFenceBasic_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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_F(LayerTransactionTest, 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 = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), 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(); +} + +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_F(LayerTransactionTest, 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"); + screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); } - Transaction().setFinalCrop(layer, Rect(4, 4, 12, 12)).apply(); + 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(colorLayer, matrix, vec3()).apply(); { - SCOPED_TRACE("pending final crop modified"); - screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + SCOPED_TRACE("new color"); + screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); } +} - Transaction().setSize(layer, 16, 16).apply(); +TEST_F(LayerTransactionTest, 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())); + + Transaction() + .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100)) + .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) + .setLayer(parentLayer, mLayerZBase + 1) + .apply(); { - SCOPED_TRACE("resize pending"); - screenshot()->expectColor(Rect(0, 0, 32, 32), Color::RED); + SCOPED_TRACE("default color"); + screenshot()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); } - // finally resize - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, 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); + } + + 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 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"); + screenshot()->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_F(LayerTransactionTest, 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())); - // 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"); + screenshot()->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(); + 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 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"); + screenshot()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); + } +} + +class ExpectedResult { +public: + enum Transaction { + NOT_PRESENTED = 0, + PRESENTED, + }; + + enum Buffer { + NOT_ACQUIRED = 0, + ACQUIRED, + }; + + enum PreviousBuffer { + NOT_RELEASED = 0, + RELEASED, + }; + + void reset() { + mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; + mExpectedSurfaceResults.clear(); + } + + void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer, + ExpectedResult::Buffer bufferResult = NOT_ACQUIRED, + ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { + mTransactionResult = transactionResult; + mExpectedSurfaceResults.emplace(std::piecewise_construct, + std::forward_as_tuple(layer->getHandle()), + std::forward_as_tuple(bufferResult, previousBufferResult)); + } + + void addSurfaces(ExpectedResult::Transaction transactionResult, + const std::vector<sp<SurfaceControl>>& layers, + ExpectedResult::Buffer bufferResult = NOT_ACQUIRED, + ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { + for (const auto& layer : layers) { + addSurface(transactionResult, layer, bufferResult, previousBufferResult); + } + } + + void verifyTransactionStats(const TransactionStats& transactionStats) const { + const auto& [latchTime, presentFence, surfaceStats] = transactionStats; + if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) { + ASSERT_GE(latchTime, 0) << "bad latch time"; + ASSERT_NE(presentFence, nullptr); + } else { + ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented"; + ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched"; + } + + ASSERT_EQ(surfaceStats.size(), mExpectedSurfaceResults.size()) + << "wrong number of surfaces"; + + for (const auto& stats : surfaceStats) { + const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl); + ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end()) + << "unexpected surface control"; + expectedSurfaceResult->second.verifySurfaceStats(stats, latchTime); + } + } + +private: + class ExpectedSurfaceResult { + public: + ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult, + ExpectedResult::PreviousBuffer previousBufferResult) + : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {} + + void verifySurfaceStats(const SurfaceStats& surfaceStats, nsecs_t latchTime) const { + const auto& [surfaceControl, acquireTime, releasePreviousBuffer] = surfaceStats; + + ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) + << "bad acquire time"; + ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time"; + ASSERT_EQ(releasePreviousBuffer, + mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) + << "bad previous buffer released"; + } + + private: + ExpectedResult::Buffer mBufferResult; + ExpectedResult::PreviousBuffer mPreviousBufferResult; + }; + + struct IBinderHash { + std::size_t operator()(const sp<IBinder>& strongPointer) const { + return std::hash<IBinder*>{}(strongPointer.get()); + } + }; + ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; + std::unordered_map<sp<IBinder>, ExpectedSurfaceResult, IBinderHash> mExpectedSurfaceResults; +}; + +class CallbackHelper { +public: + static void function(void* callbackContext, const TransactionStats& transactionStats) { + if (!callbackContext) { + ALOGE("failed to get callback context"); + } + CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext); + std::lock_guard lock(helper->mMutex); + helper->mTransactionStatsQueue.push(transactionStats); + helper->mConditionVariable.notify_all(); + } + + void getTransactionStats(TransactionStats* outStats) { + std::unique_lock lock(mMutex); + + if (mTransactionStatsQueue.empty()) { + ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)), + std::cv_status::timeout) + << "did not receive callback"; + } + + *outStats = std::move(mTransactionStatsQueue.front()); + mTransactionStatsQueue.pop(); + } + + void verifyFinalState() { + // Wait to see if there are extra callbacks + std::this_thread::sleep_for(500ms); + + std::lock_guard lock(mMutex); + EXPECT_EQ(mTransactionStatsQueue.size(), 0) << "extra callbacks received"; + mTransactionStatsQueue = {}; + } + + void* getContext() { return static_cast<void*>(this); } + + std::mutex mMutex; + std::condition_variable mConditionVariable; + std::queue<TransactionStats> mTransactionStatsQueue; +}; + +class LayerCallbackTest : public LayerTransactionTest { +public: + virtual sp<SurfaceControl> createBufferStateLayer() { + return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState); + } + + static void fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper, + const sp<SurfaceControl>& layer = nullptr) { + if (layer) { + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY | + BufferUsage::GPU_TEXTURE, + "test"); + fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + sp<Fence> fence = new Fence(-1); + + transaction.setBuffer(layer, buffer).setAcquireFence(layer, fence); + } + + transaction.addTransactionCompletedCallback(callbackHelper->function, + callbackHelper->getContext()); + } + + static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult, + bool finalState = false) { + TransactionStats transactionStats; + ASSERT_NO_FATAL_FAILURE(helper.getTransactionStats(&transactionStats)); + EXPECT_NO_FATAL_FAILURE(expectedResult.verifyTransactionStats(transactionStats)); + + 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, Basic) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + fillTransaction(transaction, &callback, layer); + + transaction.apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, NoBuffer) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + fillTransaction(transaction, &callback); + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, NoStateChange) { + Transaction transaction; + CallbackHelper callback; + fillTransaction(transaction, &callback); + + 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; + fillTransaction(transaction, &callback, layer); + + 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, Merge) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + fillTransaction(transaction1, &callback1, layer1); + fillTransaction(transaction2, &callback2, layer2); + + 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, Merge_SameCallback) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback; + fillTransaction(transaction1, &callback, layer1); + fillTransaction(transaction2, &callback, layer2); + + 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; + fillTransaction(transaction1, &callback1, layer); + fillTransaction(transaction2, &callback2, layer); + + 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_SingleBuffer) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + fillTransaction(transaction1, &callback1, layer1); + fillTransaction(transaction2, &callback2); + + 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, 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; + fillTransaction(transaction1, &callback1, layer1); + fillTransaction(transaction2, &callback2, layer2); + + 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++) { + fillTransaction(transaction, &callback, layer); + + transaction.apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::NOT_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) { + fillTransaction(transaction, &callback, layer); + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + } else { + fillTransaction(transaction, &callback); + } + + 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) { + fillTransaction(transaction, &callback, layer); + } else { + fillTransaction(transaction, &callback); + } + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + ExpectedResult expected; + expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED + : ExpectedResult::Transaction::NOT_PRESENTED, + layer); + 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++) { + fillTransaction(transaction1, &callback1, layer1); + fillTransaction(transaction2, &callback2, layer2); + + 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, + (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++) { + fillTransaction(transaction1, &callback1, layer1); + fillTransaction(transaction2, &callback2, layer2); + + 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, + (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 + fillTransaction(transaction1, &callback1, layer1); + fillTransaction(transaction2, &callback2, layer2); + + 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 + fillTransaction(transaction1, &callback1); + fillTransaction(transaction2, &callback2); + + 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 + fillTransaction(transaction1, &callback1, layer1); + fillTransaction(transaction2, &callback2, layer2); + + 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 + fillTransaction(transaction1, &callback1); + fillTransaction(transaction2, &callback2); + + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2); + 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); + ExpectedResult::PreviousBuffer previousBufferResult = + ExpectedResult::PreviousBuffer::NOT_RELEASED; + for (auto& expected : expectedResults) { + expected.reset(); + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::NOT_ACQUIRED, previousBufferResult); + previousBufferResult = ExpectedResult::PreviousBuffer::RELEASED; + + fillTransaction(transaction, &callback, layer); + + transaction.apply(); + std::this_thread::sleep_for(200ms); + } + EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true)); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + std::vector<ExpectedResult> expectedResults(50); + bool first = true; + for (auto& expected : expectedResults) { + expected.reset(); + + if (first) { + fillTransaction(transaction, &callback, layer); + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + first = false; + } else { + fillTransaction(transaction, &callback); + } + + transaction.apply(); + std::this_thread::sleep_for(200ms); + } + 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; + fillTransaction(transaction, &callback, layer); + + 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); + + fillTransaction(transaction, &callback); + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + std::this_thread::sleep_for(200ms); + } + EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, 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)); @@ -1516,24 +3128,22 @@ protected: ssize_t displayHeight = info.h; // Background surface - mBGSurfaceControl = - mComposerClient->createSurface(String8("BG Test Surface"), displayWidth, - displayHeight, PIXEL_FORMAT_RGBA_8888, 0); + mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth, + displayHeight, 0); ASSERT_TRUE(mBGSurfaceControl != nullptr); ASSERT_TRUE(mBGSurfaceControl->isValid()); fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195); // Foreground surface - mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64, - PIXEL_FORMAT_RGBA_8888, 0); + mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0); + ASSERT_TRUE(mFGSurfaceControl != nullptr); ASSERT_TRUE(mFGSurfaceControl->isValid()); fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); // Synchronization surface - mSyncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1, - PIXEL_FORMAT_RGBA_8888, 0); + mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0); ASSERT_TRUE(mSyncSurfaceControl != nullptr); ASSERT_TRUE(mSyncSurfaceControl->isValid()); @@ -1555,11 +3165,10 @@ protected: } virtual void TearDown() { - mComposerClient->dispose(); + LayerTransactionTest::TearDown(); mBGSurfaceControl = 0; mFGSurfaceControl = 0; mSyncSurfaceControl = 0; - mComposerClient = 0; } void waitForPostedBuffers() { @@ -1578,7 +3187,6 @@ protected: t.apply(true); } - sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mBGSurfaceControl; sp<SurfaceControl> mFGSurfaceControl; @@ -1588,10 +3196,10 @@ protected: }; TEST_F(LayerUpdateTest, RelativesAreNotDetached) { - sp<ScreenCapture> sc; - sp<SurfaceControl> relative = mComposerClient->createSurface(String8("relativeTestSurface"), 10, - 10, PIXEL_FORMAT_RGBA_8888, 0); + std::unique_ptr<ScreenCapture> sc; + + sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0); fillSurfaceRGBA8(relative, 10, 10, 10); waitForPostedBuffers(); @@ -1649,13 +3257,12 @@ protected: asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 64, 64); t.setPosition(mFGSurfaceControl, 64, 64); - t.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64)); - t.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1)); + t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64)); }); EXPECT_INITIAL_STATE("After restoring initial state"); } - sp<ScreenCapture> sc; + std::unique_ptr<ScreenCapture> sc; }; class CropLatchingTest : public GeometryLatchingTest { @@ -1679,45 +3286,8 @@ protected: } }; -// In this test we ensure that setGeometryAppliesWithResize actually demands -// a buffer of the new size, and not just any size. -TEST_F(CropLatchingTest, FinalCropLatchingBufferOldSize) { - EXPECT_INITIAL_STATE("before anything"); - // Normally the crop applies immediately even while a resize is pending. - asTransaction([&](Transaction& t) { - t.setSize(mFGSurfaceControl, 128, 128); - t.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - }); - - EXPECT_CROPPED_STATE("after setting crop (without geometryAppliesWithResize)"); - - restoreInitialState(); - - // In order to prepare to submit a buffer at the wrong size, we acquire it prior to - // initiating the resize. - lockAndFillFGBuffer(); - - asTransaction([&](Transaction& t) { - t.setSize(mFGSurfaceControl, 128, 128); - t.setGeometryAppliesWithResize(mFGSurfaceControl); - t.setFinalCrop(mFGSurfaceControl, Rect(64, 64, 127, 127)); - }); - - EXPECT_INITIAL_STATE("after setting crop (with geometryAppliesWithResize)"); - - // We now submit our old buffer, at the old size, and ensure it doesn't - // trigger geometry latching. - unlockFGBuffer(); - - EXPECT_INITIAL_STATE("after unlocking FG buffer (with geometryAppliesWithResize)"); - - completeFGResize(); - - EXPECT_CROPPED_STATE("after the resize finishes"); -} - TEST_F(LayerUpdateTest, DeferredTransactionTest) { - sp<ScreenCapture> sc; + std::unique_ptr<ScreenCapture> sc; { SCOPED_TRACE("before anything"); ScreenCapture::captureScreen(&sc); @@ -1729,14 +3299,14 @@ TEST_F(LayerUpdateTest, DeferredTransactionTest) { // set up two deferred transactions on different frames asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.75); - t.deferTransactionUntil(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), - mSyncSurfaceControl->getSurface()->getNextFrameNumber()); + t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), + mSyncSurfaceControl->getSurface()->getNextFrameNumber()); }); asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 128, 128); - t.deferTransactionUntil(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), - mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1); + t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), + mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1); }); { @@ -1772,26 +3342,27 @@ TEST_F(LayerUpdateTest, DeferredTransactionTest) { } TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) { - sp<ScreenCapture> sc; + std::unique_ptr<ScreenCapture> sc; sp<SurfaceControl> childNoBuffer = - mComposerClient->createSurface(String8("Bufferless child"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - sp<SurfaceControl> childBuffer = - mComposerClient->createSurface(String8("Buffered child"), 20, 20, - PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get()); + createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20, + PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get()); fillSurfaceRGBA8(childBuffer, 200, 200, 200); - - SurfaceComposerClient::Transaction{}.show(childNoBuffer).show(childBuffer).apply(true); - + SurfaceComposerClient::Transaction{} + .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10)) + .show(childNoBuffer) + .show(childBuffer) + .apply(true); { ScreenCapture::captureScreen(&sc); sc->expectChildColor(73, 73); sc->expectFGColor(74, 74); } - - SurfaceComposerClient::Transaction{}.setSize(childNoBuffer, 20, 20).apply(true); - + SurfaceComposerClient::Transaction{} + .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20)) + .apply(true); { ScreenCapture::captureScreen(&sc); sc->expectChildColor(73, 73); @@ -1800,7 +3371,7 @@ TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) { } TEST_F(LayerUpdateTest, MergingTransactions) { - sp<ScreenCapture> sc; + std::unique_ptr<ScreenCapture> sc; { SCOPED_TRACE("before move"); ScreenCapture::captureScreen(&sc); @@ -1827,13 +3398,13 @@ class ChildLayerTest : public LayerUpdateTest { 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, 10, PIXEL_FORMAT_RGBA_8888, 0, + mFGSurfaceControl.get()); fillSurfaceRGBA8(mChild, 200, 200, 200); { SCOPED_TRACE("before anything"); - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(64, 64); } } @@ -1843,7 +3414,7 @@ protected: } sp<SurfaceControl> mChild; - sp<ScreenCapture> mCapture; + std::unique_ptr<ScreenCapture> mCapture; }; TEST_F(ChildLayerTest, ChildLayerPositioning) { @@ -1854,7 +3425,7 @@ TEST_F(ChildLayerTest, ChildLayerPositioning) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -1866,7 +3437,7 @@ TEST_F(ChildLayerTest, ChildLayerPositioning) { asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground should now be at 0, 0 mCapture->expectFGColor(0, 0); // But 10 pixels in we should see the child surface @@ -1881,27 +3452,11 @@ TEST_F(ChildLayerTest, ChildLayerCropping) { t.show(mChild); t.setPosition(mChild, 0, 0); t.setPosition(mFGSurfaceControl, 0, 0); - t.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5)); + t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5)); }); { - ScreenCapture::captureScreen(&mCapture); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(4, 4); - mCapture->expectBGColor(5, 5); - } -} - -TEST_F(ChildLayerTest, ChildLayerFinalCropping) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - t.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5)); - }); - - { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(0, 0); mCapture->expectChildColor(4, 4); mCapture->expectBGColor(5, 5); @@ -1916,7 +3471,7 @@ TEST_F(ChildLayerTest, ChildLayerConstraints) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(0, 0); // Last pixel in foreground should now be the child. mCapture->expectChildColor(63, 63); @@ -1931,7 +3486,7 @@ TEST_F(ChildLayerTest, ChildLayerScaling) { // Find the boundary between the parent and child { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectChildColor(9, 9); mCapture->expectFGColor(10, 10); } @@ -1941,7 +3496,7 @@ TEST_F(ChildLayerTest, ChildLayerScaling) { // 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); @@ -1962,7 +3517,7 @@ TEST_F(ChildLayerTest, ChildLayerAlpha) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Unblended child color mCapture->checkPixel(0, 0, 0, 254, 0); } @@ -1970,7 +3525,7 @@ TEST_F(ChildLayerTest, ChildLayerAlpha) { asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Child and BG blended. mCapture->checkPixel(0, 0, 127, 127, 0); } @@ -1978,7 +3533,7 @@ TEST_F(ChildLayerTest, ChildLayerAlpha) { asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Child and BG blended. mCapture->checkPixel(0, 0, 95, 64, 95); } @@ -1992,7 +3547,7 @@ TEST_F(ChildLayerTest, ReparentChildren) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2006,7 +3561,7 @@ TEST_F(ChildLayerTest, ReparentChildren) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(64, 64); // In reparenting we should have exposed the entire foreground surface. mCapture->expectFGColor(74, 74); @@ -2017,6 +3572,36 @@ TEST_F(ChildLayerTest, ReparentChildren) { } } +TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) { + sp<SurfaceControl> mGrandChild = + createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); + fillSurfaceRGBA8(mGrandChild, 111, 111, 111); + + { + SCOPED_TRACE("Grandchild visible"); + ScreenCapture::captureScreen(&mCapture); + mCapture->checkPixel(64, 64, 111, 111, 111); + } + + mChild->clear(); + + { + SCOPED_TRACE("After destroying child"); + ScreenCapture::captureScreen(&mCapture); + mCapture->expectFGColor(64, 64); + } + + asTransaction([&](Transaction& t) { + t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); + }); + + { + SCOPED_TRACE("After reparenting grandchild"); + ScreenCapture::captureScreen(&mCapture); + mCapture->checkPixel(64, 64, 111, 111, 111); + } +} + TEST_F(ChildLayerTest, DetachChildrenSameClient) { asTransaction([&](Transaction& t) { t.show(mChild); @@ -2025,7 +3610,7 @@ TEST_F(ChildLayerTest, DetachChildrenSameClient) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2034,6 +3619,7 @@ TEST_F(ChildLayerTest, DetachChildrenSameClient) { mCapture->expectFGColor(84, 84); } + asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); }); asTransaction([&](Transaction& t) { t.hide(mChild); }); @@ -2041,7 +3627,7 @@ TEST_F(ChildLayerTest, DetachChildrenSameClient) { // Since the child has the same client as the parent, it will not get // detached and will be hidden. { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(64, 64); mCapture->expectFGColor(74, 74); mCapture->expectFGColor(84, 84); @@ -2051,10 +3637,9 @@ TEST_F(ChildLayerTest, DetachChildrenSameClient) { TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient; sp<SurfaceControl> mChildNewClient = - mNewComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + createSurface(mNewComposerClient, "New Child Test Surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - ASSERT_TRUE(mChildNewClient != nullptr); ASSERT_TRUE(mChildNewClient->isValid()); fillSurfaceRGBA8(mChildNewClient, 200, 200, 200); @@ -2067,7 +3652,7 @@ TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2082,13 +3667,68 @@ TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { // Nothing should have changed. { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(64, 64); mCapture->expectChildColor(74, 74); mCapture->expectFGColor(84, 84); } } +TEST_F(ChildLayerTest, DetachChildrenThenAttach) { + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + + ASSERT_TRUE(childNewClient != nullptr); + ASSERT_TRUE(childNewClient->isValid()); + + fillSurfaceRGBA8(childNewClient, 200, 200, 200); + + Transaction() + .hide(mChild) + .show(childNewClient) + .setPosition(childNewClient, 10, 10) + .setPosition(mFGSurfaceControl, 64, 64) + .apply(); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + + Transaction().detachChildren(mFGSurfaceControl).apply(); + Transaction().hide(childNewClient).apply(); + + // Nothing should have changed. + { + mCapture = screenshot(); + mCapture->expectFGColor(64, 64); + mCapture->expectChildColor(74, 74); + mCapture->expectFGColor(84, 84); + } + + sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0); + fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32, + 32); + Transaction() + .setLayer(newParentSurface, INT32_MAX - 1) + .show(newParentSurface) + .setPosition(newParentSurface, 20, 20) + .reparent(childNewClient, newParentSurface->getHandle()) + .apply(); + { + mCapture = screenshot(); + // Child is now hidden. + mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED); + } +} + TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { asTransaction([&](Transaction& t) { t.show(mChild); @@ -2097,7 +3737,7 @@ TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); // But it's only 10x10. @@ -2111,7 +3751,7 @@ TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); mCapture->expectChildColor(10, 10); @@ -2130,7 +3770,7 @@ TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); // But it's only 10x10. @@ -2149,7 +3789,7 @@ TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { { // 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); } @@ -2160,14 +3800,13 @@ TEST_F(ChildLayerTest, Bug36858924) { mChild.clear(); // Now recreate it as hidden - mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden, - mFGSurfaceControl.get()); + mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eHidden, mFGSurfaceControl.get()); // Show the child layer in a deferred transaction asTransaction([&](Transaction& t) { - t.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(), - mFGSurfaceControl->getSurface()->getNextFrameNumber()); + t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()); t.show(mChild); }); @@ -2194,7 +3833,7 @@ TEST_F(ChildLayerTest, Reparent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2206,7 +3845,7 @@ TEST_F(ChildLayerTest, Reparent) { asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); mCapture->expectFGColor(64, 64); // In reparenting we should have exposed the entire foreground surface. mCapture->expectFGColor(74, 74); @@ -2225,7 +3864,7 @@ TEST_F(ChildLayerTest, ReparentToNoParent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // But 10 pixels in we should see the child surface @@ -2235,7 +3874,7 @@ TEST_F(ChildLayerTest, ReparentToNoParent) { } asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Nothing should have changed. mCapture->expectFGColor(64, 64); mCapture->expectChildColor(74, 74); @@ -2244,8 +3883,7 @@ TEST_F(ChildLayerTest, ReparentToNoParent) { } TEST_F(ChildLayerTest, ReparentFromNoParent) { - sp<SurfaceControl> newSurface = mComposerClient->createSurface(String8("New Surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0); ASSERT_TRUE(newSurface != nullptr); ASSERT_TRUE(newSurface->isValid()); @@ -2259,7 +3897,7 @@ TEST_F(ChildLayerTest, ReparentFromNoParent) { }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Top left of foreground must now be visible mCapture->expectFGColor(64, 64); // At 10, 10 we should see the new surface @@ -2269,7 +3907,7 @@ TEST_F(ChildLayerTest, ReparentFromNoParent) { asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); }); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from // mFGSurface, putting it at 74, 74. mCapture->expectFGColor(64, 64); @@ -2279,13 +3917,12 @@ TEST_F(ChildLayerTest, ReparentFromNoParent) { } TEST_F(ChildLayerTest, NestedChildren) { - sp<SurfaceControl> grandchild = - mComposerClient->createSurface(String8("Grandchild surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); + sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); fillSurfaceRGBA8(grandchild, 50, 50, 50); { - ScreenCapture::captureScreen(&mCapture); + mCapture = screenshot(); // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer // which begins at 64, 64 mCapture->checkPixel(64, 64, 50, 50, 50); @@ -2293,8 +3930,7 @@ TEST_F(ChildLayerTest, NestedChildren) { } TEST_F(ChildLayerTest, ChildLayerRelativeLayer) { - sp<SurfaceControl> relative = mComposerClient->createSurface(String8("Relative surface"), 128, - 128, PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0); fillSurfaceRGBA8(relative, 255, 255, 255); Transaction t; @@ -2306,12 +3942,212 @@ TEST_F(ChildLayerTest, ChildLayerRelativeLayer) { // 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: @@ -2329,9 +4165,8 @@ TEST_F(ScreenCaptureTest, CaptureSingleLayer) { TEST_F(ScreenCaptureTest, CaptureLayerWithChild) { auto fgHandle = mFGSurfaceControl->getHandle(); - sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, - 0, mFGSurfaceControl.get()); + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); SurfaceComposerClient::Transaction().show(child).apply(true); @@ -2345,9 +4180,8 @@ TEST_F(ScreenCaptureTest, CaptureLayerWithChild) { TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) { auto fgHandle = mFGSurfaceControl->getHandle(); - sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, - 0, mFGSurfaceControl.get()); + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); SurfaceComposerClient::Transaction().show(child).apply(true); @@ -2359,9 +4193,8 @@ TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) { } TEST_F(ScreenCaptureTest, CaptureTransparent) { - sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, - 0, mFGSurfaceControl.get()); + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); @@ -2379,11 +4212,10 @@ TEST_F(ScreenCaptureTest, CaptureTransparent) { TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) { auto fgHandle = mFGSurfaceControl->getHandle(); - sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, - 0, mFGSurfaceControl.get()); - sp<SurfaceControl> relative = mComposerClient->createSurface(String8("Relative surface"), 10, - 10, PIXEL_FORMAT_RGBA_8888, 0); + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + ASSERT_NE(nullptr, child.get()) << "failed to create surface"; + sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0); fillSurfaceRGBA8(child, 200, 200, 200); fillSurfaceRGBA8(relative, 100, 100, 100); @@ -2403,12 +4235,10 @@ TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) { TEST_F(ScreenCaptureTest, CaptureRelativeInTree) { auto fgHandle = mFGSurfaceControl->getHandle(); - sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, - 0, mFGSurfaceControl.get()); - sp<SurfaceControl> relative = - mComposerClient->createSurface(String8("Relative surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); fillSurfaceRGBA8(relative, 100, 100, 100); @@ -2437,9 +4267,8 @@ public: 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); @@ -2465,8 +4294,9 @@ TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) { } 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(); @@ -2494,14 +4324,12 @@ TEST_F(ScreenCaptureChildOnlyTest, RegressionTest76099859) { TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) { auto fgHandle = mFGSurfaceControl->getHandle(); - sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, - 0, mFGSurfaceControl.get()); + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); - sp<SurfaceControl> grandchild = - mComposerClient->createSurface(String8("Grandchild surface"), 5, 5, - PIXEL_FORMAT_RGBA_8888, 0, child.get()); + sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, + PIXEL_FORMAT_RGBA_8888, 0, child.get()); fillSurfaceRGBA8(grandchild, 50, 50, 50); SurfaceComposerClient::Transaction() @@ -2518,9 +4346,8 @@ TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) { } TEST_F(ScreenCaptureTest, CaptureChildOnly) { - sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, - 0, mFGSurfaceControl.get()); + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); auto childHandle = child->getHandle(); @@ -2533,15 +4360,13 @@ TEST_F(ScreenCaptureTest, CaptureChildOnly) { } TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) { - sp<SurfaceControl> child = - mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, - 0, mFGSurfaceControl.get()); + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(child, 200, 200, 200); auto childHandle = child->getHandle(); - sp<SurfaceControl> grandchild = - mComposerClient->createSurface(String8("Grandchild surface"), 5, 5, - PIXEL_FORMAT_RGBA_8888, 0, child.get()); + sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, + PIXEL_FORMAT_RGBA_8888, 0, child.get()); fillSurfaceRGBA8(grandchild, 50, 50, 50); SurfaceComposerClient::Transaction() @@ -2559,14 +4384,12 @@ TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) { } TEST_F(ScreenCaptureTest, CaptureCrop) { - sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60, - PIXEL_FORMAT_RGBA_8888, 0); - sp<SurfaceControl> blueLayer = - mComposerClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888, - 0, redLayer.get()); + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, + PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(blueLayer, Color::BLUE)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); SurfaceComposerClient::Transaction() .setLayer(redLayer, INT32_MAX - 1) @@ -2584,7 +4407,7 @@ TEST_F(ScreenCaptureTest, CaptureCrop) { // red area to the right of the blue area mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); - Rect crop = Rect(0, 0, 30, 30); + const Rect crop = Rect(0, 0, 30, 30); ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop); // Capturing the cropped screen, cropping out the shown red area, should leave only the blue // area visible. @@ -2593,14 +4416,12 @@ TEST_F(ScreenCaptureTest, CaptureCrop) { } TEST_F(ScreenCaptureTest, CaptureSize) { - sp<SurfaceControl> redLayer = mComposerClient->createSurface(String8("Red surface"), 60, 60, - PIXEL_FORMAT_RGBA_8888, 0); - sp<SurfaceControl> blueLayer = - mComposerClient->createSurface(String8("Blue surface"), 30, 30, PIXEL_FORMAT_RGBA_8888, - 0, redLayer.get()); + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, + PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(redLayer, Color::RED)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(blueLayer, Color::BLUE)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); SurfaceComposerClient::Transaction() .setLayer(redLayer, INT32_MAX - 1) @@ -2629,13 +4450,12 @@ TEST_F(ScreenCaptureTest, CaptureSize) { } 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); + mClient->destroySurface(redLayerHandle); SurfaceComposerClient::Transaction().apply(true); sp<GraphicBuffer> outBuffer; @@ -2651,9 +4471,9 @@ protected: 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"); diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp index 6ad1b87464..a5b522e827 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -30,8 +30,9 @@ cc_test { "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 9b319854e6..16e08918a0 100644 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -624,7 +624,7 @@ TEST_F(TransactionTest, LayerCrop) { { TransactionScope ts(*sFakeComposer); Rect cropRect(16, 16, 32, 32); - ts.setCrop(mFGSurfaceControl, cropRect); + ts.setCrop_legacy(mFGSurfaceControl, cropRect); } ASSERT_EQ(2, sFakeComposer->getFrameCount()); @@ -634,40 +634,6 @@ TEST_F(TransactionTest, LayerCrop) { EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); } -TEST_F(TransactionTest, LayerFinalCrop) { - // TODO: Add scaling to confirm that crop happens in display space? - { - TransactionScope ts(*sFakeComposer); - Rect cropRect(32, 32, 32 + 64, 32 + 64); - ts.setFinalCrop(mFGSurfaceControl, cropRect); - } - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - - // In display space we are cropping with [32, 32, 96, 96] against display rect - // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96] - auto referenceFrame = mBaseFrame; - referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f}; - referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32}; - - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); -} - -TEST_F(TransactionTest, LayerFinalCropEmpty) { - // TODO: Add scaling to confirm that crop happens in display space? - { - TransactionScope ts(*sFakeComposer); - Rect cropRect(16, 16, 32, 32); - ts.setFinalCrop(mFGSurfaceControl, cropRect); - } - ASSERT_EQ(2, sFakeComposer->getFrameCount()); - - // In display space we are cropping with [16, 16, 32, 32] against display rect - // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited. - std::vector<RenderState> referenceFrame(1); - referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); -} - TEST_F(TransactionTest, LayerSetLayer) { { TransactionScope ts(*sFakeComposer); @@ -847,18 +813,16 @@ TEST_F(TransactionTest, DeferredTransaction) { { TransactionScope ts(*sFakeComposer); ts.setAlpha(mFGSurfaceControl, 0.75); - ts.deferTransactionUntil(mFGSurfaceControl, - syncSurfaceControl->getHandle(), - syncSurfaceControl->getSurface()->getNextFrameNumber()); + ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber()); } EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); { TransactionScope ts(*sFakeComposer); ts.setPosition(mFGSurfaceControl, 128, 128); - ts.deferTransactionUntil(mFGSurfaceControl, - syncSurfaceControl->getHandle(), - syncSurfaceControl->getSurface()->getNextFrameNumber() + 1); + ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber() + 1); } EXPECT_EQ(4, sFakeComposer->getFrameCount()); EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); @@ -981,7 +945,7 @@ TEST_F(ChildLayerTest, Cropping) { ts.show(mChild); ts.setPosition(mChild, 0, 0); ts.setPosition(mFGSurfaceControl, 0, 0); - ts.setCrop(mFGSurfaceControl, Rect(0, 0, 5, 5)); + ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5)); } // NOTE: The foreground surface would be occluded by the child // now, but is included in the stack because the child is @@ -994,22 +958,6 @@ TEST_F(ChildLayerTest, Cropping) { EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); } -TEST_F(ChildLayerTest, FinalCropping) { - { - TransactionScope ts(*sFakeComposer); - ts.show(mChild); - ts.setPosition(mChild, 0, 0); - ts.setPosition(mFGSurfaceControl, 0, 0); - ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, 5, 5)); - } - auto referenceFrame = mBaseFrame; - referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; - referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; - referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; - referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; - EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); -} - TEST_F(ChildLayerTest, Constraints) { { TransactionScope ts(*sFakeComposer); @@ -1095,7 +1043,7 @@ TEST_F(ChildLayerTest, ReparentChildren) { EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); } -TEST_F(ChildLayerTest, DetachChildren) { +TEST_F(ChildLayerTest, DetachChildrenSameClient) { { TransactionScope ts(*sFakeComposer); ts.show(mChild); @@ -1111,14 +1059,59 @@ TEST_F(ChildLayerTest, DetachChildren) { { TransactionScope ts(*sFakeComposer); + ts.setPosition(mFGSurfaceControl, 0, 0); ts.detachChildren(mFGSurfaceControl); } { TransactionScope ts(*sFakeComposer); + ts.setPosition(mFGSurfaceControl, 64, 64); ts.hide(mChild); } + std::vector<RenderState> refFrame(2); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + refFrame[FG_LAYER] = mBaseFrame[FG_LAYER]; + + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + ASSERT_TRUE(childNewClient != nullptr); + ASSERT_TRUE(childNewClient->isValid()); + fillSurfaceRGBA8(childNewClient, LIGHT_GRAY); + + { + TransactionScope ts(*sFakeComposer); + ts.hide(mChild); + ts.show(childNewClient); + ts.setPosition(childNewClient, 10, 10); + ts.setPosition(mFGSurfaceControl, 64, 64); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + TransactionScope ts(*sFakeComposer); + ts.detachChildren(mFGSurfaceControl); + ts.setPosition(mFGSurfaceControl, 0, 0); + } + + { + TransactionScope ts(*sFakeComposer); + ts.setPosition(mFGSurfaceControl, 64, 64); + ts.setPosition(childNewClient, 0, 0); + ts.hide(childNewClient); + } + // Nothing should have changed. The child control becomes a no-op // zombie on detach. See comments for detachChildren in the // SurfaceControl.h file. @@ -1193,8 +1186,8 @@ TEST_F(ChildLayerTest, Bug36858924) { // Show the child layer in a deferred transaction { TransactionScope ts(*sFakeComposer); - ts.deferTransactionUntil(mChild, mFGSurfaceControl->getHandle(), - mFGSurfaceControl->getSurface()->getNextFrameNumber()); + ts.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()); ts.show(mChild); } @@ -1217,6 +1210,82 @@ TEST_F(ChildLayerTest, Bug36858924) { sFakeComposer->runVSyncAndWait(); } +class ChildColorLayerTest : public ChildLayerTest { +protected: + void SetUp() override { + TransactionTest::SetUp(); + mChild = mComposerClient->createSurface(String8("Child surface"), 0, 0, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, + mFGSurfaceControl.get()); + { + TransactionScope ts(*sFakeComposer); + ts.setColor(mChild, + {LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f}); + ts.setCrop_legacy(mChild, Rect(0, 0, 10, 10)); + } + + sFakeComposer->runVSyncAndWait(); + mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10)); + mBaseFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f}; + mBaseFrame[CHILD_LAYER].mSwapCount = 0; + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + } +}; + +TEST_F(ChildColorLayerTest, LayerAlpha) { + { + TransactionScope ts(*sFakeComposer); + ts.show(mChild); + ts.setPosition(mChild, 0, 0); + ts.setPosition(mFGSurfaceControl, 0, 0); + ts.setAlpha(mChild, 0.5); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + TransactionScope ts(*sFakeComposer); + ts.setAlpha(mFGSurfaceControl, 0.5); + } + + auto referenceFrame2 = referenceFrame; + referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f; + referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildColorLayerTest, LayerZeroAlpha) { + { + TransactionScope ts(*sFakeComposer); + ts.show(mChild); + ts.setPosition(mChild, 0, 0); + ts.setPosition(mFGSurfaceControl, 0, 0); + ts.setAlpha(mChild, 0.5); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + TransactionScope ts(*sFakeComposer); + ts.setAlpha(mFGSurfaceControl, 0.0f); + } + + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + class LatchingTest : public TransactionTest { protected: void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); } @@ -1235,8 +1304,7 @@ protected: TransactionScope ts(*sFakeComposer); ts.setSize(mFGSurfaceControl, 64, 64); ts.setPosition(mFGSurfaceControl, 64, 64); - ts.setCrop(mFGSurfaceControl, Rect(0, 0, 64, 64)); - ts.setFinalCrop(mFGSurfaceControl, Rect(0, 0, -1, -1)); + ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64)); } }; @@ -1280,7 +1348,7 @@ TEST_F(LatchingTest, CropLatching) { { TransactionScope ts(*sFakeComposer); ts.setSize(mFGSurfaceControl, 128, 128); - ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63)); + ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63)); } auto referenceFrame1 = mBaseFrame; @@ -1294,7 +1362,7 @@ TEST_F(LatchingTest, CropLatching) { TransactionScope ts(*sFakeComposer); ts.setSize(mFGSurfaceControl, 128, 128); ts.setGeometryAppliesWithResize(mFGSurfaceControl); - ts.setCrop(mFGSurfaceControl, Rect(0, 0, 63, 63)); + ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63)); } EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); @@ -1307,111 +1375,6 @@ TEST_F(LatchingTest, CropLatching) { 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 9949bfa8a2..2f35ae5d54 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,16 +16,31 @@ cc_test { 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. + address: true, + }, srcs: [ ":libsurfaceflinger_sources", + "libsurfaceflinger_unittest_main.cpp", + "CompositionTest.cpp", + "DisplayIdentificationTest.cpp", "DisplayTransactionTest.cpp", "EventControlThreadTest.cpp", "EventThreadTest.cpp", + "IdleTimerTest.cpp", + "LayerHistoryTest.cpp", + "SchedulerTest.cpp", + "SchedulerUtilsTest.cpp", + "TimeStatsTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplaySurface.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", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp new file mode 100644 index 0000000000..b7c09ed503 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -0,0 +1,1300 @@ +/* + * 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <gui/IProducerListener.h> +#include <log/log.h> +#include <system/window.h> +#include <utils/String8.h> + +#include "BufferQueueLayer.h" +#include "ColorLayer.h" +#include "Layer.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/RenderEngine/MockRenderEngine.h" +#include "mock/system/window/MockNativeWindow.h" + +namespace android { +namespace { + +using testing::_; +using testing::AtLeast; +using testing::ByMove; +using testing::DoAll; +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.mutableEventControlThread().reset(mEventControlThread); + mFlinger.mutableEventThread().reset(mEventThread); + mFlinger.mutableEventQueue().reset(mMessageQueue); + + mFlinger.mutablePrimaryDispSync().reset(mPrimaryDispSync); + EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0)); + EXPECT_CALL(*mPrimaryDispSync, getPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); + 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 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}; + + TestableSurfaceFlinger mFlinger; + sp<DisplayDevice> mDisplay; + sp<DisplayDevice> mExternalDisplay; + sp<mock::DisplaySurface> mDisplaySurface = new mock::DisplaySurface(); + mock::NativeWindow* mNativeWindow = new mock::NativeWindow(); + + sp<GraphicBuffer> mBuffer = new GraphicBuffer(); + ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer(); + + mock::EventThread* mEventThread = new mock::EventThread(); + mock::EventControlThread* mEventControlThread = new mock::EventControlThread(); + + Hwc2::mock::Composer* mComposer = nullptr; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); + mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); + mock::DispSync* mPrimaryDispSync = new mock::DispSync(); + renderengine::mock::Image* mReImage = new renderengine::mock::Image(); + renderengine::mock::Framebuffer* mReFrameBuffer = new renderengine::mock::Framebuffer(); + + sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE; + + sp<GraphicBuffer> mCaptureScreenBuffer; +}; + +template <typename LayerCase> +void CompositionTest::displayRefreshCompositionDirtyGeometry() { + setupForceGeometryDirty(); + LayerCase::setupForDirtyGeometry(this); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.onMessageReceived(MessageQueue::INVALIDATE); + mFlinger.onMessageReceived(MessageQueue::REFRESH); + + LayerCase::cleanup(this); +} + +template <typename LayerCase> +void CompositionTest::displayRefreshCompositionDirtyFrame() { + LayerCase::setupForDirtyFrame(this); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.onMessageReceived(MessageQueue::INVALIDATE); + mFlinger.onMessageReceived(MessageQueue::REFRESH); + + LayerCase::cleanup(this); +} + +template <typename LayerCase> +void CompositionTest::captureScreenComposition() { + LayerCase::setupForScreenCapture(this); + + const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); + constexpr bool useIdentityTransform = true; + constexpr bool forSystem = true; + + DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH, + DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB, + ui::Transform::ROT_0); + + auto traverseLayers = [this](const LayerVector::Visitor& visitor) { + return mFlinger.traverseLayersInDisplay(mDisplay, visitor); + }; + + // TODO: Eliminate expensive/real allocation if possible. + const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; + mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(), + HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot"); + + int fd = -1; + status_t result = + mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(), + useIdentityTransform, forSystem, &fd); + if (fd >= 0) { + close(fd); + } + + EXPECT_EQ(NO_ERROR, result); + + LayerCase::cleanup(this); +} + +/* ------------------------------------------------------------------------ + * Variants for each display configuration which can be tested + */ + +template <typename Derived> +struct BaseDisplayVariant { + static constexpr bool IS_SECURE = true; + static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_NORMAL; + + static void setupPreconditions(CompositionTest* test) { + EXPECT_CALL(*test->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))); + + FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, HWC2::DisplayType::Physical, + true /* isPrimary */) + .setCapabilities(&test->mDefaultCapabilities) + .inject(&test->mFlinger, test->mComposer); + + 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(); + test->mDisplay->setLayerStack(DEFAULT_LAYER_STACK); + } + + template <typename Case> + static void setupCommonCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY)) + .Times(1); + EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1); + EXPECT_CALL(*test->mComposer, getDisplayRequests(HWC_DISPLAY, _, _, _)).Times(1); + EXPECT_CALL(*test->mComposer, acceptDisplayChanges(HWC_DISPLAY)).Times(1); + EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1); + EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1); + + EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); + EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return()); + EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true)); + + EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() { + return base::unique_fd(0); + })); + + 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) { + // Called once with a non-null value to set a framebuffer, and then + // again with nullptr to clear it. + EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false)) + .WillOnce(Return(true)); + EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false)) + .WillOnce(Return(true)); + + EXPECT_CALL(*test->mRenderEngine, checkErrors()).WillRepeatedly(Return()); + EXPECT_CALL(*test->mRenderEngine, createFramebuffer()) + .WillOnce(Return( + ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer)))); + EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1); + EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1); + EXPECT_CALL(*test->mRenderEngine, clearWithColor(0, 0, 0, 1)).Times(1); + EXPECT_CALL(*test->mRenderEngine, flush()).WillOnce(Return(ByMove(base::unique_fd()))); + EXPECT_CALL(*test->mRenderEngine, finish()).WillOnce(Return(true)); + + EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE)) + .Times(1); + // This expectation retires on saturation as setViewportAndProjection is + // called an extra time for the code path this setup is for. + // TODO: Investigate this extra call + EXPECT_CALL(*test->mRenderEngine, + setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, + Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::Transform::ROT_0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1); + } + + 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(DisplaySurface::COMPOSITION_HWC)).Times(1); + } + + static void setupRECompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES)) + .Times(1); + EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence()) + .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence)); + + EXPECT_CALL(*test->mRenderEngine, setOutputDataSpace(ui::Dataspace::UNKNOWN)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setDisplayMaxLuminance(DEFAULT_DISPLAY_MAX_LUMINANCE)) + .Times(1); + EXPECT_CALL(*test->mRenderEngine, setColorTransform(_)).Times(2); + // 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->mRenderEngine, + setViewportAndProjection(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, + Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::Transform::ROT_0)) + .Times(1); + EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(NotNull(), false)) + .WillOnce(Return(true)); + EXPECT_CALL(*test->mReFrameBuffer, setNativeWindowBuffer(IsNull(), false)) + .WillOnce(Return(true)); + EXPECT_CALL(*test->mRenderEngine, createFramebuffer()) + .WillOnce(Return( + ByMove(std::unique_ptr<renderengine::Framebuffer>(test->mReFrameBuffer)))); + EXPECT_CALL(*test->mRenderEngine, bindFrameBuffer(test->mReFrameBuffer)).Times(1); + EXPECT_CALL(*test->mRenderEngine, unbindFrameBuffer(test->mReFrameBuffer)).Times(1); + EXPECT_CALL(*test->mNativeWindow, queueBuffer(_, _)).WillOnce(Return(0)); + EXPECT_CALL(*test->mNativeWindow, dequeueBuffer(_, _)) + .WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1), + Return(0))); + } + + template <typename Case> + static void setupRELayerCompositionCallExpectations(CompositionTest* test) { + Case::Layer::setupRECompositionCallExpectations(test); + } + + template <typename Case> + static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) { + Case::Layer::setupREScreenshotCompositionCallExpectations(test); + + EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true)); + } +}; + +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); + + // TODO: Investigate this extra call + EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1); + } + + template <typename Case> + static void setupRELayerScreenshotCompositionCallExpectations(CompositionTest* test) { + Case::Layer::setupInsecureREScreenshotCompositionCallExpectations(test); + + EXPECT_CALL(*test->mRenderEngine, isCurrent()).WillRepeatedly(Return(true)); + } +}; + +struct PoweredOffDisplaySetupVariant : public BaseDisplayVariant<PoweredOffDisplaySetupVariant> { + static constexpr int INIT_POWER_MODE = HWC_POWER_MODE_OFF; + + template <typename Case> + static void setupCommonCompositionCallExpectations(CompositionTest* test) { + // 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) { + // 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, Fence::NO_FENCE); + Mock::VerifyAndClear(test->mRenderEngine); + Mock::VerifyAndClear(test->mReImage); + } + + 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, + setupLayerBlending(true, false, false, + half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], + LayerProperties::COLOR[2], LayerProperties::COLOR[3]), + 0.0f)) + .Times(1); + + EXPECT_CALL(*test->mRenderEngine, createImage()) + .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage)))); + EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setupLayerTexturing(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1); + EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1); + EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1); + // This call retires on saturation as the code that renders a texture disables the state, + // along with a top-level disable to ensure it is disabled for non-buffer layers. + EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation(); + } + + static void setupREBufferCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREBufferCompositionCommonCallExpectations(test); + + // TODO - Investigate and eliminate these differences between display + // composition and screenshot composition. + EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1); + } + + 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 setupREColorCompositionCommonCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mRenderEngine, disableScissor()).Times(1); + } + + static void setupREColorCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1); + EXPECT_CALL(*test->mRenderEngine, + setupLayerBlending(true, false, true, + half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], + LayerProperties::COLOR[2], LayerProperties::COLOR[3]), + 0.0f)) + .Times(1); + EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1); + } + + 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) { + EXPECT_CALL(*test->mRenderEngine, setupFillWithColor(0, 0, 0, 1)).Times(1); + EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1); + } +}; + +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, createImage()) + .WillOnce(Return(ByMove(std::unique_ptr<renderengine::Image>(test->mReImage)))); + EXPECT_CALL(*test->mReImage, setNativeWindowBuffer(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*test->mRenderEngine, bindExternalTextureImage(DEFAULT_TEXTURE_ID, _)).Times(1); + EXPECT_CALL(*test->mRenderEngine, setupLayerBlackedOut()).Times(1); + + EXPECT_CALL(*test->mRenderEngine, + setupLayerBlending(true, false, false, + half4(Base::COLOR[0], Base::COLOR[1], Base::COLOR[2], + Base::COLOR[3]), 0.0f)) + .Times(1); + EXPECT_CALL(*test->mRenderEngine, setSourceDataSpace(ui::Dataspace::UNKNOWN)).Times(1); + EXPECT_CALL(*test->mRenderEngine, drawMesh(_)).Times(1); + EXPECT_CALL(*test->mRenderEngine, disableBlending()).Times(1); + EXPECT_CALL(*test->mRenderEngine, setSourceY410BT2020(false)).Times(1); + // This call retires on saturation as the code that renders a texture disables the state, + // along with a top-level disable to ensure it is disabled for non-buffer layers. + EXPECT_CALL(*test->mRenderEngine, disableTexturing()).Times(1).RetiresOnSaturation(); + } + + 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->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))); + + const auto displayId = test->mDisplay->getId(); + ASSERT_TRUE(displayId); + layer->createHwcLayer(test->mFlinger.mFlinger->getBE().mHwc.get(), *displayId); + + 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)); + const auto displayId = test->mDisplay->getId(); + ASSERT_TRUE(displayId); + for (auto layer : test->mFlinger.mutableDrawingState().layersSortedByZ) { + layer->destroyHwcLayer(*displayId); + } + 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)); + }); + + auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); + layerDrawingState.crop_legacy = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH); + return layer; + } + + static void setupRECompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREColorCompositionCommonCallExpectations(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)); + }); + + LayerProperties::setupLayerState(test, layer); + + return layer; + } + + static void cleanupInjectedLayers(CompositionTest* test) { + EXPECT_CALL(*test->mMessageQueue, postMessage(_, 0)).Times(2); + Base::cleanupInjectedLayers(test); + } + + static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) { + LayerProperties::setupHwcSetGeometryCallExpectations(test); + LayerProperties::setupHwcSetSourceCropBufferCallExpectations(test); + } + + static void setupCallExpectationsForDirtyFrame(CompositionTest* test) { + LayerProperties::setupHwcSetPerFrameCallExpectations(test); + LayerProperties::setupHwcSetPerFrameBufferCallExpectations(test); + } + + static void setupRECompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREBufferCompositionCallExpectations(test); + } + + static void setupInsecureRECompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupInsecureREBufferCompositionCallExpectations(test); + } + + static void setupREScreenshotCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupREBufferScreenshotCompositionCallExpectations(test); + } + + static void setupInsecureREScreenshotCompositionCallExpectations(CompositionTest* test) { + LayerProperties::setupInsecureREBufferScreenshotCompositionCallExpectations(test); + } +}; + +/* ------------------------------------------------------------------------ + * Variants to control how the composition type is changed + */ + +struct NoCompositionTypeVariant { + static void setupHwcSetCallExpectations(CompositionTest*) {} + + static void setupHwcGetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)).Times(1); + } +}; + +template <IComposerClient::Composition CompositionType> +struct KeepCompositionTypeVariant { + static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(CompositionType); + + static void setupHwcSetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, CompositionType)) + .Times(1); + } + + static void setupHwcGetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)).Times(1); + } +}; + +template <IComposerClient::Composition InitialCompositionType, + IComposerClient::Composition FinalCompositionType> +struct ChangeCompositionTypeVariant { + static constexpr HWC2::Composition TYPE = static_cast<HWC2::Composition>(FinalCompositionType); + + static void setupHwcSetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, + setLayerCompositionType(HWC_DISPLAY, HWC_LAYER, InitialCompositionType)) + .Times(1); + } + + static void setupHwcGetCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::Layer>{ + static_cast<Hwc2::Layer>(HWC_LAYER)}), + SetArgPointee<2>(std::vector<IComposerClient::Composition>{ + FinalCompositionType}), + Return(Error::NONE))); + } +}; + +/* ------------------------------------------------------------------------ + * Variants to select how the composition is expected to be handled + */ + +struct CompositionResultBaseVariant { + static void setupLayerState(CompositionTest*, sp<Layer>) {} + + template <typename Case> + static void setupCallExpectationsForDirtyGeometry(CompositionTest* test) { + Case::Layer::setupCallExpectationsForDirtyGeometry(test); + } + + template <typename Case> + static void setupCallExpectationsForDirtyFrame(CompositionTest* test) { + Case::Layer::setupCallExpectationsForDirtyFrame(test); + } +}; + +struct NoCompositionResultVariant : public CompositionResultBaseVariant { + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Case::Display::setupEmptyFrameCompositionCallExpectations(test); + Case::Display::setupHwcCompositionCallExpectations(test); + } +}; + +struct HwcCompositionResultVariant : public CompositionResultBaseVariant { + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Case::Display::setupNonEmptyFrameCompositionCallExpectations(test); + Case::Display::setupHwcCompositionCallExpectations(test); + } +}; + +struct RECompositionResultVariant : public CompositionResultBaseVariant { + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Case::Display::setupNonEmptyFrameCompositionCallExpectations(test); + Case::Display::setupRECompositionCallExpectations(test); + Case::Display::template setupRELayerCompositionCallExpectations<Case>(test); + } +}; + +struct ForcedClientCompositionResultVariant : public RECompositionResultVariant { + static void setupLayerState(CompositionTest*, sp<Layer> layer) { + layer->forceClientComposition(DEFAULT_DISPLAY_ID); + } + + template <typename Case> + static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {} + + template <typename Case> + static void setupCallExpectationsForDirtyFrame(CompositionTest*) {} +}; + +struct EmptyScreenshotResultVariant { + static void setupLayerState(CompositionTest*, sp<Layer>) {} + + template <typename Case> + static void setupCallExpectations(CompositionTest*) {} +}; + +struct REScreenshotResultVariant : public EmptyScreenshotResultVariant { + using Base = EmptyScreenshotResultVariant; + + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Base::template setupCallExpectations<Case>(test); + Case::Display::template setupRELayerScreenshotCompositionCallExpectations<Case>(test); + } +}; + +/* ------------------------------------------------------------------------ + * Composition test case, containing all the variants being tested + */ + +template <typename DisplayCase, typename LayerCase, typename CompositionTypeCase, + typename CompositionResultCase> +struct CompositionCase { + using ThisCase = + CompositionCase<DisplayCase, LayerCase, CompositionTypeCase, CompositionResultCase>; + using Display = DisplayCase; + using Layer = LayerCase; + using CompositionType = CompositionTypeCase; + using CompositionResult = CompositionResultCase; + + static void setupCommon(CompositionTest* test) { + Display::setupPreconditions(test); + + auto layer = Layer::createLayer(test); + Layer::injectLayer(test, layer); + CompositionResult::setupLayerState(test, layer); + } + + static void setupForDirtyGeometry(CompositionTest* test) { + setupCommon(test); + + Display::template setupCommonCompositionCallExpectations<ThisCase>(test); + CompositionResult::template setupCallExpectationsForDirtyGeometry<ThisCase>(test); + CompositionResult::template setupCallExpectationsForDirtyFrame<ThisCase>(test); + CompositionResult::template setupCallExpectations<ThisCase>(test); + } + + static void setupForDirtyFrame(CompositionTest* test) { + setupCommon(test); + + Display::template setupCommonCompositionCallExpectations<ThisCase>(test); + CompositionResult::template setupCallExpectationsForDirtyFrame<ThisCase>(test); + CompositionResult::template setupCallExpectations<ThisCase>(test); + } + + static void setupForScreenCapture(CompositionTest* test) { + setupCommon(test); + + Display::template setupCommonScreensCaptureCallExpectations<ThisCase>(test); + CompositionResult::template setupCallExpectations<ThisCase>(test); + } + + static void cleanup(CompositionTest* test) { + Layer::cleanupInjectedLayers(test); + + for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) { + hwcDisplay->mutableLayers().clear(); + } + + test->mDisplay->setVisibleLayersSortedByZ(Vector<sp<android::Layer>>()); + } +}; + +/* ------------------------------------------------------------------------ + * Composition cases to test + */ + +TEST_F(CompositionTest, noLayersDoesMinimalWorkWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant, + NoCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, noLayersDoesMinimalWorkWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant, + NoCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, noLayersDoesMinimalWorkToCaptureScreen) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, NoLayerVariant, NoCompositionTypeVariant, + EmptyScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Simple buffer layers + */ + +TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedNormalBufferLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenNormalBufferLayer) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Single-color layers + */ + +TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedColorLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedColorLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenColorLayer) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, ColorLayerVariant<ColorLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Layers with sideband buffers + */ + +TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedSidebandBufferLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::SIDEBAND, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenSidebandBufferLayer) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Layers with ISurfaceComposerClient::eSecure, on a secure display + */ + +TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedSecureBufferLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenSecureBufferLayerOnSecureDisplay) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Layers with ISurfaceComposerClient::eSecure, on a non-secure display + */ + +TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>, + ForcedClientCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>, + ForcedClientCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) { + captureScreenComposition< + CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Cursor layers + */ + +TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, REComposedCursorLayer) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::CURSOR, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenCursorLayer) { + captureScreenComposition< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ + * Simple buffer layer on a display which is powered off. + */ + +TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry<CompositionCase< + PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, displayOffHWCComposedNormalBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame<CompositionCase< + PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>, + HwcCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, displayOffREComposedNormalBufferLayer) { + displayRefreshCompositionDirtyFrame<CompositionCase< + PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE, + IComposerClient::Composition::CLIENT>, + RECompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenNormalBufferLayerOnPoweredOffDisplay) { + captureScreenComposition<CompositionCase< + PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp new file mode 100644 index 0000000000..55995d048a --- /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/DisplayIdentificationTest.h b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h new file mode 100644 index 0000000000..1c8e5cc9d3 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.h @@ -0,0 +1,27 @@ +/* + * 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 "DisplayHardware/DisplayIdentification.h" + +namespace android { + +const DisplayIdentificationData& getInternalEdid(); +const DisplayIdentificationData& getExternalEdid(); +const DisplayIdentificationData& getExternalEedid(); + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 9b308bfcc8..bd9b140528 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -17,14 +17,20 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <type_traits> + #include <gmock/gmock.h> #include <gtest/gtest.h> #include <log/log.h> +#include <ui/DebugUtils.h> + +#include "DisplayIdentificationTest.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" @@ -73,9 +79,11 @@ constexpr int HWC_POWER_MODE_LEET = 1337; // An out of range power mode value #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); /* ------------------------------------------------------------------------ * @@ -96,7 +104,7 @@ public: // -------------------------------------------------------------------- // 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); @@ -111,21 +119,22 @@ public: TestableSurfaceFlinger mFlinger; mock::EventThread* mEventThread = 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 +144,7 @@ DisplayTransactionTest::DisplayTransactionTest() { // Default to no wide color display support configured mFlinger.mutableHasWideColorDisplay() = false; + mFlinger.mutableUseColorManagement() = false; mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED; // Default to using HWC virtual displays @@ -152,8 +162,9 @@ DisplayTransactionTest::DisplayTransactionTest() { mFlinger.mutableEventControlThread().reset(mEventControlThread); mFlinger.mutableEventThread().reset(mEventThread); mFlinger.mutableEventQueue().reset(mMessageQueue); - mFlinger.setupRenderEngine(std::unique_ptr<RE::RenderEngine>(mRenderEngine)); + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.mutableInterceptor().reset(mSurfaceInterceptor); + mFlinger.mutablePrimaryDispSync().reset(mPrimaryDispSync); injectMockComposer(0); } @@ -191,15 +202,15 @@ void DisplayTransactionTest::injectFakeNativeWindowSurfaceFactory() { // 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 +218,11 @@ bool DisplayTransactionTest::hasTransactionFlagSet(int flag) { } 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 +245,67 @@ const DisplayDeviceState& DisplayTransactionTest::getDrawingDisplayState(sp<IBin * */ -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 +316,23 @@ struct DisplayVariant { // 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. + 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))); return injector; } @@ -266,19 +340,11 @@ struct DisplayVariant { 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))); } static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) { @@ -297,7 +363,8 @@ struct DisplayVariant { } }; -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; @@ -316,7 +383,10 @@ struct HwcDisplayVariant { // Called by tests to inject a HWC display setup static void injectHwcDisplay(DisplayTransactionTest* test) { - FakeHwcDisplayInjector(DisplayVariant::TYPE, HWC_DISPLAY_TYPE) + 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) @@ -353,6 +423,16 @@ struct HwcDisplayVariant { 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 +442,67 @@ struct HwcDisplayVariant { } }; -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> {}; + +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; +}; -// 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 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; +}; // 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 +515,14 @@ constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER 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 +545,7 @@ struct WideColorSupportNotConfiguredVariant { static void injectConfigChange(DisplayTransactionTest* test) { test->mFlinger.mutableHasWideColorDisplay() = false; + test->mFlinger.mutableUseColorManagement() = false; test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED; } @@ -471,6 +564,7 @@ struct WideColorP3ColorimetricSupportedVariant { 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; } @@ -499,6 +593,7 @@ struct WideColorNotSupportedVariant { static constexpr bool WIDE_COLOR_SUPPORTED = false; static void injectConfigChange(DisplayTransactionTest* test) { + test->mFlinger.mutableUseColorManagement() = true; test->mFlinger.mutableHasWideColorDisplay() = true; } @@ -512,6 +607,7 @@ struct WideColorNotSupportedVariant { // 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 +616,27 @@ struct NonHwcDisplayHdrSupportVariant { } }; +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 +651,7 @@ struct Hdr10SupportedVariant { // 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 +666,7 @@ struct HdrHlgSupportedVariant { // 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 +681,7 @@ struct HdrDolbyVisionSupportedVariant { // 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 +694,7 @@ struct HdrNotSupportedVariant { 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 +702,8 @@ template <typename Display> 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 +711,19 @@ template <typename Display> 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 +731,24 @@ template <typename Display> 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 +787,10 @@ using WideColorP3ColorimetricDisplayCase = 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 +811,7 @@ using HdrCta861_3_DisplayCase = Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, HdrNotSupportedVariant<PrimaryDisplayVariant>, Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfiguredVariant, - NonHwcDisplayHdrSupportVariant, - NoPerFrameMetadataSupportVariant<InvalidDisplayVariant>>; + /* ------------------------------------------------------------------------ * * SurfaceFlinger::onHotplugReceived @@ -694,8 +819,8 @@ using InvalidDisplayCase = Case<InvalidDisplayVariant, WideColorSupportNotConfig 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 +843,8 @@ TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) { // 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 +855,9 @@ TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) { // 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 +971,8 @@ TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForNonsecureDisplay) // 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 +1002,8 @@ TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForSecureDisplay) { // 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); // -------------------------------------------------------------------- @@ -965,8 +1090,8 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { // 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 @@ -991,6 +1116,107 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { } /* ------------------------------------------------------------------------ + * 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(9)).Times(1); + EXPECT_CALL(*mNativeWindow, perform(13)).Times(1); + EXPECT_CALL(*mNativeWindow, perform(30)).Times(1); + auto displayDevice = mInjector.inject(); + + displayDevice->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::setupNewDisplayDeviceInternal */ @@ -1032,19 +1258,27 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { // -------------------------------------------------------------------- // 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 +1303,24 @@ TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) { } 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; + + // 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<HwcVirtualDisplayCase>(); + setupNewDisplayDeviceInternalTest<Case>(); } TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) { setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>(); } +TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) { + setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>(); +} + TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) { setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>(); } @@ -1160,13 +1401,23 @@ void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessin Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, true)).Times(1); + EXPECT_CALL(*mEventThread, + onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY) + ? EventThread::DisplayType::Primary + : EventThread::DisplayType::External, + true)) + .Times(1); } template <typename Case> void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() { EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); - EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::TYPE, false)).Times(1); + EXPECT_CALL(*mEventThread, + onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY) + ? EventThread::DisplayType::Primary + : EventThread::DisplayType::External, + false)) + .Times(1); } template <typename Case> @@ -1175,30 +1426,35 @@ void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& di 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 +1520,7 @@ void HandleTransactionLockedTest::ignoresHotplugConnectCommon() { // 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 +1542,8 @@ void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { // Call Expectations EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false)); + EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _)) + .Times(0); setupCommonCallExpectationsForDisconnectProcessing<Case>(); @@ -1298,13 +1556,12 @@ void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { // 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()); @@ -1391,13 +1648,12 @@ TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary // 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 +1692,10 @@ TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary // 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 +1724,13 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { // 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 +1750,6 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { EXPECT_CALL(*surface, setAsyncMode(true)).Times(1); - EXPECT_CALL(*mProducer, connect(_, _, _, _)).Times(1); EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1); Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this); @@ -1532,8 +1790,11 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { // 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 +1813,7 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { // 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 +1823,9 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) { // 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(); @@ -1710,11 +1973,18 @@ TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { // A display is set up auto nativeWindow = new mock::NativeWindow(); auto displaySurface = new mock::DisplaySurface(); - auto renderSurface = new RE::mock::Surface(); + 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(9)).Times(1); + EXPECT_CALL(*nativeWindow, perform(13)).Times(1); + EXPECT_CALL(*nativeWindow, perform(30)).Times(1); display.inject(); // There is a change to the viewport state @@ -1726,11 +1996,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { // -------------------------------------------------------------------- // 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 @@ -1751,11 +2017,18 @@ TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { // A display is set up auto nativeWindow = new mock::NativeWindow(); auto displaySurface = new mock::DisplaySurface(); - auto renderSurface = new RE::mock::Surface(); + 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(9)).Times(1); + EXPECT_CALL(*nativeWindow, perform(13)).Times(1); + EXPECT_CALL(*nativeWindow, perform(30)).Times(1); display.inject(); // There is a change to the viewport state @@ -1767,11 +2040,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { // -------------------------------------------------------------------- // 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 +2080,6 @@ TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithUnknownDispla 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; @@ -2358,14 +2593,23 @@ TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) { */ // 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 +2617,12 @@ struct DozeNotSupportedVariant { 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 +2670,24 @@ struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { } }; +struct DispSyncIsSupportedVariant { + static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mPrimaryDispSync, reset()).Times(1); + EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1); + EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1); + } + + static void setupEndResyncCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mPrimaryDispSync, endResync()).Times(1); + } +}; + +struct DispSyncNotSupportedVariant { + static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {} + + static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {} +}; + // -------------------------------------------------------------------- // Note: // @@ -2441,6 +2709,7 @@ struct TransitionOffToOnVariant static void setupCallExpectations(DisplayTransactionTest* test) { Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::DispSync::setupBeginResyncCallExpectations(test); Case::setupRepaintEverythingCallExpectations(test); } @@ -2470,6 +2739,7 @@ struct TransitionOnToOffVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::DispSync::setupEndResyncCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); } @@ -2505,6 +2775,7 @@ struct TransitionDozeSuspendToDozeVariant 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 +2794,7 @@ struct TransitionDozeSuspendToOnVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::DispSync::setupBeginResyncCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); } }; @@ -2532,6 +2804,7 @@ struct TransitionOnToDozeSuspendVariant 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,11 +2827,12 @@ struct TransitionOnToUnknownVariant // -------------------------------------------------------------------- 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) { @@ -2605,16 +2879,19 @@ struct DisplayPowerCase { // 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 +2913,7 @@ void SetPowerModeInternalTest::transitionDisplayCommon() { // -------------------------------------------------------------------- // Preconditions + Case::Doze::setupComposerCallExpectations(this); auto display = Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE); Case::setInitialPrimaryHWVsyncEnabled(this, @@ -2671,7 +2949,7 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { 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 +2963,29 @@ TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { 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 b34645463f..9dc4193ecc 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 80fdb80264..acbed51e9d 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; @@ -47,10 +47,10 @@ public: class EventThreadTest : public testing::Test { protected: - class MockEventThreadConnection : public android::impl::EventThread::Connection { + class MockEventThreadConnection : public android::EventThreadConnection { public: explicit MockEventThreadConnection(android::impl::EventThread* eventThread) - : android::impl::EventThread::Connection(eventThread) {} + : android::EventThreadConnection(eventThread) {} MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event)); }; @@ -71,7 +71,8 @@ protected: ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount); void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount); - void expectHotplugEventReceivedByConnection(int expectedDisplayType, bool expectedConnected); + void expectHotplugEventReceivedByConnection(EventThread::DisplayType expectedDisplayType, + bool expectedConnected); AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; @@ -169,13 +170,16 @@ void EventThreadTest::expectVsyncEventReceivedByConnection(nsecs_t expectedTimes expectedCount); } -void EventThreadTest::expectHotplugEventReceivedByConnection(int expectedDisplayType, - bool expectedConnected) { +void EventThreadTest::expectHotplugEventReceivedByConnection( + EventThread::DisplayType expectedDisplayType, bool expectedConnected) { + const uint32_t expectedDisplayId = + expectedDisplayType == EventThread::DisplayType::Primary ? 0 : 1; + 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.id); EXPECT_EQ(expectedConnected, event.hotplug.connected); } @@ -394,28 +398,23 @@ TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) { } TEST_F(EventThreadTest, postHotplugPrimaryDisconnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, false); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, false); + mThread->onHotplugReceived(EventThread::DisplayType::Primary, false); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, false); } TEST_F(EventThreadTest, postHotplugPrimaryConnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_PRIMARY, true); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_PRIMARY, true); + mThread->onHotplugReceived(EventThread::DisplayType::Primary, true); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, true); } TEST_F(EventThreadTest, postHotplugExternalDisconnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, false); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, false); + mThread->onHotplugReceived(EventThread::DisplayType::External, false); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, false); } TEST_F(EventThreadTest, postHotplugExternalConnect) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_EXTERNAL, true); - expectHotplugEventReceivedByConnection(DisplayDevice::DISPLAY_EXTERNAL, true); -} - -TEST_F(EventThreadTest, postHotplugVirtualDisconnectIsFilteredOut) { - mThread->onHotplugReceived(DisplayDevice::DISPLAY_VIRTUAL, false); - EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + mThread->onHotplugReceived(EventThread::DisplayType::External, true); + expectHotplugEventReceivedByConnection(EventThread::DisplayType::External, true); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp new file mode 100644 index 0000000000..9fe9a18780 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp @@ -0,0 +1,126 @@ +/* + * 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; + + AsyncCallRecorder<void (*)()> mExpiredTimerCallback; + + std::unique_ptr<IdleTimer> mIdleTimer; +}; + +namespace { +TEST_F(IdleTimerTest, createAndDestroyTest) { + mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, [] {}); +} + +TEST_F(IdleTimerTest, startStopTest) { + mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mExpiredTimerCallback.getInvocable()); + mIdleTimer->start(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // The timer expires after 30 ms, so the call to the callback should not happen. + EXPECT_FALSE(mExpiredTimerCallback.waitForCall().has_value()); + mIdleTimer->stop(); +} + +TEST_F(IdleTimerTest, resetTest) { + mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mExpiredTimerCallback.getInvocable()); + mIdleTimer->start(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // The timer expires after 30 ms, so the call to the callback should not happen. + EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value()); + mIdleTimer->reset(); + // The timer was reset, so the call to the callback should not happen. + std::this_thread::sleep_for(std::chrono::milliseconds(15)); + EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value()); + mIdleTimer->stop(); +} + +TEST_F(IdleTimerTest, startNotCalledTest) { + mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable()); + std::this_thread::sleep_for(6ms); + // The start hasn't happened, so the callback does not happen. + EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value()); + mIdleTimer->stop(); +} + +TEST_F(IdleTimerTest, idleTimerIdlesTest) { + mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable()); + mIdleTimer->start(); + std::this_thread::sleep_for(6ms); + // The timer expires after 3 ms, so the call to the callback happens. + EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value()); + std::this_thread::sleep_for(6ms); + // Timer can be idle. + EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value()); + // Timer can be reset. + mIdleTimer->reset(); + std::this_thread::sleep_for(6ms); + // Timer fires again. + EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value()); + mIdleTimer->stop(); +} + +TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) { + mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable()); + + mIdleTimer->start(); + std::this_thread::sleep_for(6ms); + // The timer expires after 3 ms, so the call to the callback should happen. + EXPECT_TRUE(mExpiredTimerCallback.waitForCall(1us).has_value()); + mIdleTimer->stop(); +} + +TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) { + mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable()); + mIdleTimer->start(); + std::this_thread::sleep_for(6ms); + EXPECT_TRUE(mExpiredTimerCallback.waitForCall().has_value()); + mIdleTimer->stop(); + mIdleTimer->reset(); + std::this_thread::sleep_for(6ms); + EXPECT_FALSE(mExpiredTimerCallback.waitForCall().has_value()); +} + +TEST_F(IdleTimerTest, noCallbacksAfterStopTest) { + mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mExpiredTimerCallback.getInvocable()); + mIdleTimer->start(); + std::this_thread::sleep_for(1ms); + mIdleTimer->stop(); + std::this_thread::sleep_for(3ms); + EXPECT_FALSE(mExpiredTimerCallback.waitForCall(1us).has_value()); +} + +} // namespace +} // namespace scheduler +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp new file mode 100644 index 0000000000..3fb1a6e62a --- /dev/null +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -0,0 +1,174 @@ +#undef LOG_TAG +#define LOG_TAG "LayerHistoryUnittests" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <log/log.h> + +#include <mutex> + +#include "Scheduler/LayerHistory.h" + +using testing::_; +using testing::Return; + +namespace android { + +class LayerHistoryTest : public testing::Test { +public: + LayerHistoryTest(); + ~LayerHistoryTest() override; + +protected: + std::unique_ptr<LayerHistory> mLayerHistory; +}; + +LayerHistoryTest::LayerHistoryTest() { + mLayerHistory = std::make_unique<LayerHistory>(); +} +LayerHistoryTest::~LayerHistoryTest() {} + +namespace { +TEST_F(LayerHistoryTest, simpleInsertAndGet) { + mLayerHistory->insert("TestLayer", 0); + + const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0); + EXPECT_EQ(1, testMap.size()); + auto element = testMap.find("TestLayer"); + EXPECT_EQ("TestLayer", element->first); + EXPECT_EQ(0, element->second); + + // Testing accessing object at an empty container will return an empty map. + const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1); + EXPECT_EQ(0, emptyMap.size()); +} + +TEST_F(LayerHistoryTest, multipleInserts) { + mLayerHistory->insert("TestLayer0", 0); + mLayerHistory->insert("TestLayer1", 1); + mLayerHistory->insert("TestLayer2", 2); + mLayerHistory->insert("TestLayer3", 3); + + const std::unordered_map<std::string, nsecs_t>& testMap = mLayerHistory->get(0); + // Because the counter was not incremented, all elements were inserted into the first + // container. + EXPECT_EQ(4, testMap.size()); + auto element = testMap.find("TestLayer0"); + EXPECT_EQ("TestLayer0", element->first); + EXPECT_EQ(0, element->second); + + element = testMap.find("TestLayer1"); + EXPECT_EQ("TestLayer1", element->first); + EXPECT_EQ(1, element->second); + + element = testMap.find("TestLayer2"); + EXPECT_EQ("TestLayer2", element->first); + EXPECT_EQ(2, element->second); + + element = testMap.find("TestLayer3"); + EXPECT_EQ("TestLayer3", element->first); + EXPECT_EQ(3, element->second); + + // Testing accessing object at an empty container will return an empty map. + const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(1); + EXPECT_EQ(0, emptyMap.size()); +} + +TEST_F(LayerHistoryTest, incrementingCounter) { + mLayerHistory->insert("TestLayer0", 0); + mLayerHistory->incrementCounter(); + mLayerHistory->insert("TestLayer1", 1); + mLayerHistory->insert("TestLayer2", 2); + mLayerHistory->incrementCounter(); + mLayerHistory->insert("TestLayer3", 3); + + // Because the counter was incremented, the elements were inserted into different + // containers. + // We expect the get method to access the slot at the current counter of the index + // is 0. + const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0); + EXPECT_EQ(1, testMap1.size()); + auto element = testMap1.find("TestLayer3"); + EXPECT_EQ("TestLayer3", element->first); + EXPECT_EQ(3, element->second); + + const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(1); + EXPECT_EQ(2, testMap2.size()); + element = testMap2.find("TestLayer1"); + EXPECT_EQ("TestLayer1", element->first); + EXPECT_EQ(1, element->second); + element = testMap2.find("TestLayer2"); + EXPECT_EQ("TestLayer2", element->first); + EXPECT_EQ(2, element->second); + + const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(2); + EXPECT_EQ(1, testMap3.size()); + element = testMap3.find("TestLayer0"); + EXPECT_EQ("TestLayer0", element->first); + EXPECT_EQ(0, element->second); + + // Testing accessing object at an empty container will return an empty map. + const std::unordered_map<std::string, nsecs_t>& emptyMap = mLayerHistory->get(3); + EXPECT_EQ(0, emptyMap.size()); +} + +TEST_F(LayerHistoryTest, clearTheMap) { + mLayerHistory->insert("TestLayer0", 0); + + const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0); + EXPECT_EQ(1, testMap1.size()); + auto element = testMap1.find("TestLayer0"); + EXPECT_EQ("TestLayer0", element->first); + EXPECT_EQ(0, element->second); + + mLayerHistory->incrementCounter(); + // The array currently contains 30 elements. + for (int i = 1; i < 30; ++i) { + mLayerHistory->insert("TestLayer0", i); + mLayerHistory->incrementCounter(); + } + // Expect the map to be cleared. + const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(0); + EXPECT_EQ(0, testMap2.size()); + + mLayerHistory->insert("TestLayer30", 30); + const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(0); + element = testMap3.find("TestLayer30"); + EXPECT_EQ("TestLayer30", element->first); + EXPECT_EQ(30, element->second); + // The original element in this location does not exist anymore. + element = testMap3.find("TestLayer0"); + EXPECT_EQ(testMap3.end(), element); +} + +TEST_F(LayerHistoryTest, testingGet) { + // The array currently contains 30 elements. + for (int i = 0; i < 30; ++i) { + const auto name = "TestLayer" + std::to_string(i); + mLayerHistory->insert(name, i); + mLayerHistory->incrementCounter(); + } + + // The counter should be set to 0, and the map at 0 should be cleared. + const std::unordered_map<std::string, nsecs_t>& testMap1 = mLayerHistory->get(0); + EXPECT_EQ(0, testMap1.size()); + + // This should return (ARRAY_SIZE + (counter - 3)) % ARRAY_SIZE + const std::unordered_map<std::string, nsecs_t>& testMap2 = mLayerHistory->get(3); + EXPECT_EQ(1, testMap2.size()); + auto element = testMap2.find("TestLayer27"); + EXPECT_EQ("TestLayer27", element->first); + EXPECT_EQ(27, element->second); + + // If the user gives an out of bound index, we should mod it with ARRAY_SIZE first, + // so requesting element 40 would be the same as requesting element 10. + const std::unordered_map<std::string, nsecs_t>& testMap3 = mLayerHistory->get(40); + EXPECT_EQ(1, testMap3.size()); + element = testMap3.find("TestLayer20"); + EXPECT_EQ("TestLayer20", element->first); + EXPECT_EQ(20, element->second); +} + +} // namespace +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp new file mode 100644 index 0000000000..4253ad826f --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -0,0 +1,191 @@ +#undef LOG_TAG +#define LOG_TAG "SchedulerUnittests" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <log/log.h> + +#include <mutex> + +#include "AsyncCallRecorder.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/EventControlThread.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/Scheduler.h" +#include "mock/MockDispSync.h" +#include "mock/MockEventThread.h" + +using testing::_; +using testing::Return; + +namespace android { + +class SchedulerTest : public testing::Test { +protected: + class MockEventThreadConnection : public android::EventThreadConnection { + public: + explicit MockEventThreadConnection(EventThread* eventThread) + : EventThreadConnection(eventThread) {} + ~MockEventThreadConnection() = default; + + MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel)); + MOCK_METHOD1(setVsyncRate, status_t(uint32_t count)); + MOCK_METHOD0(requestNextVsync, void()); + }; + + /** + * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else + * the same. + */ + class MockScheduler : public android::Scheduler { + public: + MockScheduler(std::unique_ptr<EventThread> eventThread) + : Scheduler([](bool) {}), mEventThread(std::move(eventThread)) {} + + std::unique_ptr<EventThread> makeEventThread( + const std::string& /* connectionName */, DispSync* /* dispSync */, + nsecs_t /* phaseOffsetNs */, + impl::EventThread::ResyncWithRateLimitCallback /* resyncCallback */, + 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::DispSync* mPrimaryDispSync = new mock::DispSync(); + mock::EventThread* mEventThread; + std::unique_ptr<MockScheduler> mScheduler; + sp<MockEventThreadConnection> mEventThreadConnection; + + AsyncCallRecorder<void (*)()> mResyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; +}; + +SchedulerTest::SchedulerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + + std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>(); + mEventThread = eventThread.get(); + mScheduler = std::make_unique<MockScheduler>(std::move(eventThread)); + EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0)); + + mEventThreadConnection = new MockEventThreadConnection(mEventThread); + + // createConnection call to scheduler makes a createEventConnection call to EventThread. Make + // sure that call gets executed and returns an EventThread::Connection object. + EXPECT_CALL(*mEventThread, createEventConnection()) + .WillRepeatedly(Return(mEventThreadConnection)); + + mConnectionHandle = + mScheduler->createConnection("appConnection", 16, mResyncCallRecorder.getInvocable(), + mInterceptVSyncCallRecorder.getInvocable()); +} + +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, canCreateAndDestroyTest) { + EXPECT_FALSE(mResyncCallRecorder.waitForCall().has_value()); + EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall().has_value()); + EXPECT_EQ(0, mConnectionHandle->id); +} + +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)); + EXPECT_TRUE(returnedValue == nullptr); + EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr); + EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr); + ASSERT_NO_FATAL_FAILURE( + mScheduler->hotplugReceived(nullptr, EventThread::DisplayType::Primary, 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)); + 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, + EventThread::DisplayType::Primary, 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)); + EXPECT_TRUE(returnedValue != nullptr); + ASSERT_EQ(returnedValue, mEventThreadConnection); + + EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr); + EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr); + + EXPECT_CALL(*mEventThread, onHotplugReceived(EventThread::DisplayType::Primary, false)) + .Times(1); + ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(mConnectionHandle, + EventThread::DisplayType::Primary, 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 0000000000..5865579641 --- /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/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 558845f09b..4da08b840a 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -16,27 +16,148 @@ #pragma once +#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 "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> createScheduler(std::function<void(bool)>) override { + // TODO: Use test-fixture controlled factory + return nullptr; + } + + std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override { + // TODO: Use test-fixture controlled factory + return std::make_unique<android::impl::SurfaceInterceptor>(flinger); + } + + sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override { + // TODO: Use test-fixture controlled factory + return new StartPropertySetThread(timestampPropertyValue); + } + + sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override { + // TODO: Use test-fixture controlled factory + return new DisplayDevice(std::move(creationArgs)); + } + + sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + std::string requestorName) override { + // TODO: Use test-fixture controlled factory + return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); + } + + void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer, + bool consumerIsSurfaceFlinger) override { + if (!mCreateBufferQueue) return; + mCreateBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); + } + + std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer>& producer) override { + if (!mCreateNativeWindowSurface) return nullptr; + return mCreateNativeWindowSurface(producer); + } + + 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::unique_ptr<TimeStats> createTimeStats() override { + // TODO: Use test-fixture controlled factory + return std::make_unique<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; +}; + +} // 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) { + void setupRenderEngine(std::unique_ptr<renderengine::RenderEngine> renderEngine) { mFlinger->getBE().mRenderEngine = std::move(renderEngine); } @@ -44,18 +165,32 @@ public: mFlinger->getBE().mHwc.reset(new 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; } using HotplugEvent = SurfaceFlinger::HotplugEvent; + auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mState.current; } + auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mState.drawing; } + + void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) { + Mutex::Autolock lock(layer->mStateMutex); + layer->mState.drawing.sidebandStream = sidebandStream; + layer->getBE().compositionInfo.hwc.sidebandStream = sidebandStream; + } + + void setLayerPotentialCursor(sp<Layer> layer, bool potentialCursor) { + layer->mPotentialCursor = potentialCursor; + } + /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ @@ -64,15 +199,18 @@ public: 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<IGraphicBufferProducer>& producer) { - return mFlinger->setupNewDisplayDeviceInternal(display, hwcId, state, dispSurface, + return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface, producer); } @@ -89,8 +227,22 @@ public: auto onInitializeDisplays() { return mFlinger->onInitializeDisplays(); } - auto setPowerModeInternal(const sp<DisplayDevice>& hw, int mode, bool stateLockHeld = false) { - return mFlinger->setPowerModeInternal(hw, mode, stateLockHeld); + auto setPowerModeInternal(const sp<DisplayDevice>& display, int mode) { + return mFlinger->setPowerModeInternal(display, mode); + } + + auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what); } + + auto captureScreenImplLocked(const RenderArea& renderArea, + TraverseLayersFunction traverseLayers, ANativeWindowBuffer* buffer, + bool useIdentityTransform, bool forSystem, int* outSyncFd) { + return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer, + useIdentityTransform, forSystem, outSyncFd); + } + + auto traverseLayersInDisplay(const sp<const DisplayDevice>& display, + const LayerVector::Visitor& visitor) { + return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor); } /* ------------------------------------------------------------------------ @@ -110,26 +262,36 @@ public: */ auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; } + auto& mutablePrimaryDisplayOrientation() { return SurfaceFlinger::primaryDisplayOrientation; } + auto& mutableUseColorManagement() { return SurfaceFlinger::useColorManagement; } - auto& mutableBuiltinDisplays() { return mFlinger->mBuiltinDisplays; } 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& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; } auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; } auto& mutableInterceptor() { return mFlinger->mInterceptor; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; } + auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; } + auto& mutablePrimaryDispSync() { return mFlinger->mPrimaryDispSync; } auto& mutablePrimaryHWVsyncEnabled() { return mFlinger->mPrimaryHWVsyncEnabled; } + auto& mutableTexturePool() { return mFlinger->mTexturePool; } auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; } auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; } - auto& mutableHwcDisplayData() { return mFlinger->getBE().mHwc->mDisplayData; } - auto& mutableHwcDisplaySlots() { return mFlinger->getBE().mHwc->mHwcDisplaySlots; } + auto& mutableHwcDisplayData() { return mFlinger->getHwComposer().mDisplayData; } + auto& mutableHwcPhysicalDisplayIdMap() { + return mFlinger->getHwComposer().mPhysicalDisplayIdMap; + } + + auto& mutableInternalHwcDisplayId() { return mFlinger->getHwComposer().mInternalHwcDisplayId; } + auto& mutableExternalHwcDisplayId() { return mFlinger->getHwComposer().mExternalHwcDisplayId; } ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does @@ -141,6 +303,7 @@ public: mutableEventQueue().reset(); mutableEventThread().reset(); mutableInterceptor().reset(); + mutablePrimaryDispSync().reset(); mFlinger->getBE().mHwc.reset(); mFlinger->getBE().mRenderEngine.reset(); } @@ -153,7 +316,7 @@ public: public: FakePowerAdvisor() = default; ~FakePowerAdvisor() override = default; - void setExpensiveRenderingExpected(hwc2_display_t, bool) override { } + void setExpensiveRenderingExpected(hwc2_display_t, bool) override {} }; struct HWC2Display : public HWC2::Display { @@ -168,6 +331,7 @@ public: auto& mutableIsConnected() { return this->mIsConnected; } auto& mutableConfigs() { return this->mConfigs; } + auto& mutableLayers() { return this->mLayers; } }; class FakeHwcDisplayInjector { @@ -179,8 +343,9 @@ public: static constexpr int32_t DEFAULT_DPI = 320; static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0; - FakeHwcDisplayInjector(DisplayDevice::DisplayType type, HWC2::DisplayType hwcDisplayType) - : mType(type), mHwcDisplayType(hwcDisplayType) {} + FakeHwcDisplayInjector(DisplayId displayId, HWC2::DisplayType hwcDisplayType, + bool isPrimary) + : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {} auto& setHwcDisplayId(hwc2_display_t displayId) { mHwcDisplayId = displayId; @@ -249,17 +414,22 @@ public: display->mutableConfigs().emplace(mActiveConfig, config.build()); display->mutableIsConnected() = true; - ASSERT_TRUE(flinger->mutableHwcDisplayData().size() > static_cast<size_t>(mType)); - flinger->mutableHwcDisplayData()[mType].reset(); - flinger->mutableHwcDisplayData()[mType].hwcDisplay = display.get(); - flinger->mutableHwcDisplaySlots().emplace(mHwcDisplayId, mType); + flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = display.get(); + + if (mHwcDisplayType == HWC2::DisplayType::Physical) { + flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId); + (mIsPrimary ? flinger->mutableInternalHwcDisplayId() + : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId; + } flinger->mFakeHwcDisplays.push_back(std::move(display)); } private: - DisplayDevice::DisplayType mType; - HWC2::DisplayType mHwcDisplayType; + const DisplayId mDisplayId; + const HWC2::DisplayType mHwcDisplayType; + const bool mIsPrimary; + hwc2_display_t mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID; int32_t mWidth = DEFAULT_WIDTH; int32_t mHeight = DEFAULT_HEIGHT; @@ -273,9 +443,13 @@ public: 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,43 +469,53 @@ public: 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; + mCreationArgs.displaySurface = displaySurface; return *this; } - auto& setRenderSurface(std::unique_ptr<RE::Surface> renderSurface) { - mRenderSurface = std::move(renderSurface); + auto& setSecure(bool secure) { + mCreationArgs.isSecure = secure; return *this; } - auto& setSecure(bool secure) { - mSecure = secure; + 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, false, HdrCapabilities(), 0, hdrAndRenderIntents, - HWC_POWER_MODE_NORMAL); - mFlinger.mutableDisplays().add(mDisplayToken, device); - - DisplayDeviceState state(mType, mSecure); + DisplayDeviceState state; + state.displayId = mCreationArgs.isVirtual ? std::nullopt : mCreationArgs.displayId; + state.isSecure = mCreationArgs.isSecure; + + 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; @@ -340,15 +524,11 @@ public: 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 0000000000..86f1a39b81 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -0,0 +1,518 @@ +/* + * 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 <gtest/gtest.h> + +#include <log/log.h> +#include <utils/String16.h> +#include <utils/Vector.h> + +#include <random> + +#include "TimeStats/TimeStats.h" + +#include "libsurfaceflinger_unittest_main.h" + +using namespace android::surfaceflinger; +using namespace google::protobuf; + +namespace android { +namespace { + +// 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<TimeStats>(); +}; + +std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) { + size_t index = 0; + 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, index, 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, 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 < 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 0000000000..bc1f00d906 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.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 <gtest/gtest.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); + + 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/libsurfaceflinger_unittest_main.h b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h new file mode 100644 index 0000000000..e742c5079c --- /dev/null +++ b/services/surfaceflinger/tests/unittests/libsurfaceflinger_unittest_main.h @@ -0,0 +1,20 @@ +/* + * 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 + +// 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 267670aca5..551fae714b 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -28,12 +28,12 @@ namespace Hwc2 { 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::composer::V2_1::Config; using android::hardware::graphics::composer::V2_1::Display; @@ -41,7 +41,7 @@ using android::hardware::graphics::composer::V2_1::Error; 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 @@ public: 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,15 @@ public: 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>&)); }; } // 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 0000000000..2f7e5ead0f --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp @@ -0,0 +1,27 @@ +/* + * 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" + +namespace android { +namespace mock { + +// Explicit default instantiation is recommended. +DispSync::DispSync() = default; +DispSync::~DispSync() = default; + +} // 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 0000000000..9213ae5fdf --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.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. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "Scheduler/DispSync.h" + +namespace android { +namespace mock { + +class DispSync : public android::DispSync { +public: + DispSync(); + ~DispSync() override; + + MOCK_METHOD0(reset, void()); + MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&)); + MOCK_METHOD0(beginResync, void()); + MOCK_METHOD1(addResyncSample, bool(nsecs_t)); + MOCK_METHOD0(endResync, void()); + MOCK_METHOD1(setPeriod, void(nsecs_t)); + MOCK_METHOD0(getPeriod, nsecs_t()); + MOCK_METHOD1(setRefreshSkipCount, void(int)); + MOCK_METHOD3(addEventListener, status_t(const char*, nsecs_t, Callback*)); + MOCK_METHOD1(removeEventListener, status_t(Callback*)); + MOCK_METHOD2(changePhaseOffset, status_t(Callback*, nsecs_t)); + MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int)); + MOCK_METHOD1(setIgnorePresentFences, void(bool)); + MOCK_METHOD0(expectedPresentTime, nsecs_t()); + + MOCK_CONST_METHOD1(dump, void(std::string&)); +}; + +} // namespace mock +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h index 8ac09a962d..6ef352a548 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 e6ea6634c0..bb6e1831df 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,16 @@ public: EventThread(); ~EventThread() override; - MOCK_CONST_METHOD0(createEventConnection, sp<BnDisplayEventConnection>()); + MOCK_CONST_METHOD0(createEventConnection, sp<EventThreadConnection>()); MOCK_METHOD0(onScreenReleased, void()); MOCK_METHOD0(onScreenAcquired, void()); - MOCK_METHOD2(onHotplugReceived, void(int, bool)); - MOCK_CONST_METHOD1(dump, void(String8&)); + MOCK_METHOD2(onHotplugReceived, void(DisplayType, bool)); + 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> &)); }; } // namespace mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h index cf07cf7ba9..dc8d606547 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 { @@ -30,6 +31,7 @@ public: MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&)); MOCK_METHOD1(setEventThread, void(android::EventThread*)); + MOCK_METHOD1(setEventConnection, void(const sp<android::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 25ff39bbd1..9cc6d38397 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 88d1a9fc51..7bc1151d61 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/RenderEngine/MockRenderEngine.cpp b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp index a98beceebd..fbfbc3fc99 100644 --- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp +++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.cpp @@ -16,20 +16,22 @@ #include "mock/RenderEngine/MockRenderEngine.h" +#include <ui/Region.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; - Image::Image() = default; Image::~Image() = default; +Framebuffer::Framebuffer() = default; +Framebuffer::~Framebuffer() = default; + } // namespace mock -} // namespace RE +} // namespace renderengine } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h index ac082933a3..81a7768cc6 100644 --- a/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h +++ b/services/surfaceflinger/tests/unittests/mock/RenderEngine/MockRenderEngine.h @@ -17,50 +17,51 @@ #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" +#include <renderengine/DisplaySettings.h> +#include <renderengine/Framebuffer.h> +#include <renderengine/Image.h> +#include <renderengine/LayerSettings.h> +#include <renderengine/Mesh.h> +#include <renderengine/RenderEngine.h> +#include <renderengine/Texture.h> +#include <ui/GraphicBuffer.h> namespace android { -namespace RE { +namespace renderengine { namespace mock { -class RenderEngine : public RE::RenderEngine { +class RenderEngine : public renderengine::RenderEngine { public: RenderEngine(); ~RenderEngine() override; - MOCK_METHOD0(createSurface, std::unique_ptr<RE::Surface>()); - MOCK_METHOD0(createImage, std::unique_ptr<RE::Image>()); + MOCK_METHOD0(createFramebuffer, std::unique_ptr<Framebuffer>()); + MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>()); MOCK_CONST_METHOD0(primeCache, void()); - MOCK_METHOD1(dump, void(String8&)); - MOCK_CONST_METHOD0(supportsImageCrop, bool()); + MOCK_METHOD1(dump, void(std::string&)); + MOCK_CONST_METHOD0(useNativeFenceSync, bool()); + MOCK_CONST_METHOD0(useWaitSync, 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_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float)); + MOCK_METHOD1(setScissor, void(const Rect&)); MOCK_METHOD0(disableScissor, void()); MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); - MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const RE::Image&)); - MOCK_METHOD5(readPixels, void(size_t, size_t, size_t, size_t, uint32_t*)); + MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); 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_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_METHOD1(setupColorTransform, void(const mat4&)); + 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()); @@ -68,41 +69,35 @@ public: 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(bindFrameBuffer, status_t(Framebuffer*)); + MOCK_METHOD1(unbindFrameBuffer, void(Framebuffer*)); MOCK_METHOD1(drawMesh, void(const 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_METHOD4(drawLayers, + status_t(const DisplaySettings&, const std::vector<LayerSettings>&, + ANativeWindowBuffer*, base::unique_fd*)); }; -class Surface : public RE::Surface { +class Image : public renderengine::Image { public: - Surface(); - ~Surface() override; + Image(); + ~Image() 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()); + MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool)); }; -class Image : public RE::Image { +class Framebuffer : public renderengine::Framebuffer { public: - Image(); - ~Image() override; + Framebuffer(); + ~Framebuffer() override; - MOCK_METHOD4(setNativeWindowBuffer, - bool(ANativeWindowBuffer* buffer, bool isProtected, int32_t cropWidth, - int32_t cropHeight)); + MOCK_METHOD2(setNativeWindowBuffer, bool(ANativeWindowBuffer*, bool)); }; } // namespace mock -} // namespace RE +} // namespace renderengine } // 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 561fd5869e..4950a4b9e2 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 @@ public: 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 d754560ea5..0000000000 --- a/services/thermalservice/Android.bp +++ /dev/null @@ -1,61 +0,0 @@ -subdirs = [ - "libthermalcallback" -] - -cc_library { - name: "libthermalservice", - - srcs: [ - "aidl/android/os/IThermalEventListener.aidl", - "aidl/android/os/IThermalService.aidl", - "aidl/android/os/Temperature.cpp", - ], - aidl: { - include_dirs: ["frameworks/native/services/thermalservice/aidl"], - export_aidl_headers: true, - }, - export_include_dirs: ["aidl"], - - shared_libs: [ - "libbinder", - "libutils", - ], - - cflags: [ - "-Wall", - "-Werror", - "-Wunused", - "-Wunreachable-code", - ], -} - -cc_binary { - name: "thermalserviced", - - srcs: [ - "ThermalService.cpp", - "thermalserviced.cpp", - ], - - include_dirs: ["frameworks/native"], - - shared_libs: [ - "libthermalservice", - "libbinder", - "libutils", - "libthermalcallback", - "android.hardware.thermal@1.1", - "libhidlbase", - "libhidltransport", - "liblog", - ], - - cflags: [ - "-Wall", - "-Werror", - "-Wunused", - "-Wunreachable-code", - ], - - init_rc: ["thermalservice.rc"], -} diff --git a/services/thermalservice/ThermalService.cpp b/services/thermalservice/ThermalService.cpp deleted file mode 100644 index 6e09a83872..0000000000 --- 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 17dfcbcd37..0000000000 --- a/services/thermalservice/ThermalService.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_THERMALSERVICE_THERMALSERVICE_H -#define ANDROID_THERMALSERVICE_THERMALSERVICE_H - -#include <android/os/BnThermalService.h> -#include <android/os/IThermalEventListener.h> -#include <android/os/Temperature.h> -#include <utils/Mutex.h> -#include <utils/String16.h> -#include <utils/Vector.h> - -namespace android { -namespace os { - -class ThermalService : public BnThermalService, - public IBinder::DeathRecipient { -public: - ThermalService() : mThrottled(false) {}; - void publish(const sp<ThermalService>& service); - binder::Status notifyThrottling( - const bool isThrottling, const Temperature& temperature); - -private: - Mutex mListenersLock; - Vector<sp<IThermalEventListener> > mListeners; - bool mThrottled; - Temperature mThrottleTemperature; - - binder::Status registerThermalEventListener( - const sp<IThermalEventListener>& listener); - binder::Status unregisterThermalEventListener( - const sp<IThermalEventListener>& listener); - binder::Status isThrottling(bool* _aidl_return); - void binderDied(const wp<IBinder>& who); -}; - -}; // namespace os -}; // namespace android - -#endif // ANDROID_THERMALSERVICE_THERMALSERVICE_H diff --git a/services/thermalservice/aidl/android/os/IThermalService.aidl b/services/thermalservice/aidl/android/os/IThermalService.aidl deleted file mode 100644 index e699202e64..0000000000 --- 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 0293c39fa9..0000000000 --- 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 df207b7ac3..0000000000 --- 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 bbc5607f8b..0000000000 --- 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 e98506e47e..0000000000 --- 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 5e094fa259..0000000000 --- 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 3d72c680b4..0000000000 --- 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 94c2c78df3..0000000000 --- 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 8e2726669f..0000000000 --- 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 309e2fe422..0000000000 --- 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 6122846540..7a7e437744 100644 --- a/services/vr/bufferhubd/Android.bp +++ b/services/vr/bufferhubd/Android.bp @@ -12,45 +12,59 @@ // 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", + "libbufferhubservice", "libcutils", + "libgtest_prod", + "libgui", "liblog", + "libpdx_default_transport", "libsync", - "libutils", - "libgui", "libui", - "libpdx_default_transport", + "libutils", ] +cc_library_static { + name: "libbufferhubd", + srcs: [ + "buffer_channel.cpp", + "buffer_hub.cpp", + "consumer_channel.cpp", + "consumer_queue_channel.cpp", + "producer_channel.cpp", + "producer_queue_channel.cpp", + ], + cflags: [ + "-DLOG_TAG=\"libbufferhubd\"", + "-DTRACE=0", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", + ], + export_include_dirs: ["include"], + header_libs: ["libdvr_headers"], + shared_libs: sharedLibraries, + static_libs: [ + "libbufferhub", + ], + + // TODO(b/117568153): Temporarily opt out using libcrt. + no_libcrt: true, +} + cc_binary { - srcs: sourceFiles, + srcs: ["bufferhubd.cpp"], cflags: [ "-DLOG_TAG=\"bufferhubd\"", "-DTRACE=0", "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", ], - header_libs: headerLibraries, - static_libs: staticLibraries, + header_libs: ["libdvr_headers"], shared_libs: sharedLibraries, + static_libs: [ + "libbufferhub", + "libbufferhubd", + "libperformance", + ], name: "bufferhubd", init_rc: ["bufferhubd.rc"], } diff --git a/services/vr/bufferhubd/buffer_channel.cpp b/services/vr/bufferhubd/buffer_channel.cpp new file mode 100644 index 0000000000..695396caf2 --- /dev/null +++ b/services/vr/bufferhubd/buffer_channel.cpp @@ -0,0 +1,138 @@ +#include <errno.h> +#include <private/dvr/buffer_channel.h> +#include <private/dvr/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 { + +BufferChannel::BufferChannel(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) { + buffer_node_ = std::make_shared<BufferNode>( + width, height, layer_count, format, usage, user_metadata_size); + if (!buffer_node_->IsValid()) { + ALOGE("BufferChannel::BufferChannel: Failed to create BufferNode."); + return; + } + client_state_mask_ = buffer_node_->AddNewActiveClientsBitToMask(); +} + +BufferChannel::BufferChannel(BufferHubService* service, int buffer_id, + int channel_id, + std::shared_ptr<BufferNode> buffer_node) + : BufferHubChannel(service, buffer_id, channel_id, kDetachedBufferType), + buffer_node_(buffer_node) { + client_state_mask_ = buffer_node_->AddNewActiveClientsBitToMask(); + if (client_state_mask_ == 0U) { + ALOGE("BufferChannel::BufferChannel: %s", strerror(errno)); + buffer_node_ = nullptr; + } +} + +BufferChannel::~BufferChannel() { + ALOGD_IF(TRACE, "BufferChannel::~BufferChannel: channel_id=%d buffer_id=%d.", + channel_id(), buffer_id()); + if (client_state_mask_ != 0U) { + buffer_node_->RemoveClientsBitFromMask(client_state_mask_); + } + Hangup(); +} + +BufferHubChannel::BufferInfo BufferChannel::GetBufferInfo() const { + return BufferInfo( + buffer_id(), /*consumer_count=*/0, buffer_node_->buffer_desc().width, + buffer_node_->buffer_desc().height, buffer_node_->buffer_desc().layers, + buffer_node_->buffer_desc().format, buffer_node_->buffer_desc().usage, + /*state=*/0, /*signaled_mask=*/0, /*index=*/0); +} + +void BufferChannel::HandleImpulse(Message& /*message*/) { + ATRACE_NAME("BufferChannel::HandleImpulse"); +} + +bool BufferChannel::HandleMessage(Message& message) { + ATRACE_NAME("BufferChannel::HandleMessage"); + switch (message.GetOp()) { + case DetachedBufferRPC::Import::Opcode: + DispatchRemoteMethod<DetachedBufferRPC::Import>( + *this, &BufferChannel::OnImport, message); + return true; + + case DetachedBufferRPC::Duplicate::Opcode: + DispatchRemoteMethod<DetachedBufferRPC::Duplicate>( + *this, &BufferChannel::OnDuplicate, message); + return true; + + default: + return false; + } +} + +Status<BufferTraits<BorrowedHandle>> BufferChannel::OnImport( + Message& /*message*/) { + ATRACE_NAME("BufferChannel::OnImport"); + ALOGD_IF(TRACE, "BufferChannel::OnImport: buffer=%d.", buffer_id()); + + BorrowedHandle ashmem_handle = + BorrowedHandle(buffer_node_->metadata().ashmem_fd().get()); + + // TODO(b/112057680) Move away from the GraphicBuffer-based IonBuffer. + return BufferTraits<BorrowedHandle>{ + /*buffer_handle=*/buffer_node_->buffer_handle(), + /*metadata_handle=*/ashmem_handle, + /*id=*/buffer_id(), + /*client_state_mask=*/client_state_mask_, + /*metadata_size=*/buffer_node_->metadata().metadata_size(), + /*width=*/buffer_node_->buffer_desc().width, + /*height=*/buffer_node_->buffer_desc().height, + /*layer_count=*/buffer_node_->buffer_desc().layers, + /*format=*/buffer_node_->buffer_desc().format, + /*usage=*/buffer_node_->buffer_desc().usage, + /*stride=*/buffer_node_->buffer_desc().stride, + /*acquire_fence_fd=*/BorrowedHandle{}, + /*released_fence_fd=*/BorrowedHandle{}}; +} + +Status<RemoteChannelHandle> BufferChannel::OnDuplicate(Message& message) { + ATRACE_NAME("BufferChannel::OnDuplicate"); + ALOGD_IF(TRACE, "BufferChannel::OnDuplicate: buffer=%d.", buffer_id()); + + int channel_id; + auto status = message.PushChannel(0, nullptr, &channel_id); + if (!status.ok()) { + ALOGE("BufferChannel::OnDuplicate: Failed to push buffer channel: %s", + status.GetErrorMessage().c_str()); + return ErrorStatus(ENOMEM); + } + + auto channel = std::shared_ptr<BufferChannel>( + new BufferChannel(service(), buffer_id(), channel_id, buffer_node_)); + if (!channel->IsValid()) { + ALOGE("BufferChannel::OnDuplicate: Invalid buffer. %s", strerror(errno)); + return ErrorStatus(EINVAL); + } + + 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( + "BufferChannel::OnDuplicate: Failed to set new buffer channel: %s.", + channel_status.GetErrorMessage().c_str()); + } + + return status; +} + +} // namespace dvr +} // namespace android diff --git a/services/vr/bufferhubd/buffer_hub.cpp b/services/vr/bufferhubd/buffer_hub.cpp index e57c8edfe3..f50d2920f2 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,15 @@ #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_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 +56,6 @@ std::string BufferHubService::DumpState(size_t /*max_length*/) { 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 +88,6 @@ std::string BufferHubService::DumpState(size_t /*max_length*/) { 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 << " "; @@ -257,23 +252,6 @@ pdx::Status<void> BufferHubService::HandleMessage(Message& message) { *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); } @@ -332,9 +310,9 @@ pdx::Status<void> BufferHubService::OnCreateDetachedBuffer( return ErrorStatus(EALREADY); } - std::unique_ptr<DetachedBufferChannel> channel = - DetachedBufferChannel::Create(this, buffer_id, width, height, layer_count, - format, usage, user_metadata_size); + std::unique_ptr<BufferChannel> channel = + BufferChannel::Create(this, buffer_id, width, height, layer_count, format, + usage, user_metadata_size); if (!channel) { ALOGE( "BufferHubService::OnCreateDetachedBuffer: Failed to allocate buffer, " diff --git a/services/vr/bufferhubd/bufferhubd.cpp b/services/vr/bufferhubd/bufferhubd.cpp index b27f218eb6..50cb3b7beb 100644 --- a/services/vr/bufferhubd/bufferhubd.cpp +++ b/services/vr/bufferhubd/bufferhubd.cpp @@ -1,17 +1,15 @@ #include <sched.h> -#include <unistd.h> - -#include <log/log.h> #include <sys/resource.h> +#include <unistd.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 @@ int main(int, char**) { 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 a6d2dbb60c..c7695bc51f 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 android { 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 @@ BufferHubChannel::BufferInfo ConsumerChannel::GetBufferInfo() const { // 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 @@ bool ConsumerChannel::HandleMessage(Message& message) { *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 @@ Status<BufferDescription<BorrowedHandle>> ConsumerChannel::OnGetBuffer( 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 @@ Status<LocalFence> ConsumerChannel::OnConsumerAcquire(Message& message) { 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 @@ Status<void> ConsumerChannel::OnConsumerRelease(Message& message, 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::OnConsumerRelease(Message& message, } } -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 4d430012f1..5d7d4e9057 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 @@ BufferHubChannel::BufferInfo ConsumerQueueChannel::GetBufferInfo() const { } 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); + if (silent_) { + return; + } - // Signal the client that there is new buffer available. - SignalAvailable(); + 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 @@ ConsumerQueueChannel::OnConsumerQueueImportBuffers(Message& message) { 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 a5cf68dcfd..0000000000 --- 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 8b6dab8538..0000000000 --- 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/include/private/dvr/buffer_channel.h b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h new file mode 100644 index 0000000000..9888db6642 --- /dev/null +++ b/services/vr/bufferhubd/include/private/dvr/buffer_channel.h @@ -0,0 +1,61 @@ +#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_ +#define ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_ + +#include <pdx/channel_handle.h> +#include <pdx/file_handle.h> +#include <private/dvr/buffer_hub.h> +#include <private/dvr/buffer_hub_defs.h> +#include <private/dvr/buffer_node.h> + +namespace android { +namespace dvr { + +class BufferChannel : public BufferHubChannel { + public: + ~BufferChannel() override; + + template <typename... Args> + static std::unique_ptr<BufferChannel> Create(Args&&... args) { + auto buffer = std::unique_ptr<BufferChannel>( + new BufferChannel(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_node_ != nullptr && buffer_node_->IsValid(); + } + + // 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: + + // Allocates a new detached buffer. + BufferChannel(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); + + // Creates a detached buffer from an existing BufferNode. This method is used + // in OnDuplicate method. + BufferChannel(BufferHubService* service, int buffer_id, int channel_id, + std::shared_ptr<BufferNode> buffer_node); + + pdx::Status<BufferTraits<pdx::BorrowedHandle>> OnImport( + pdx::Message& message); + pdx::Status<pdx::RemoteChannelHandle> OnDuplicate(pdx::Message& message); + + // The concrete implementation of the Buffer object. + std::shared_ptr<BufferNode> buffer_node_ = nullptr; + + // The state bit of this buffer. + uint32_t client_state_mask_ = 0U; +}; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFERHUBD_BUFFER_CHANNEL_H_ diff --git a/services/vr/bufferhubd/buffer_hub.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h index e47ffa3417..676617d902 100644 --- a/services/vr/bufferhubd/buffer_hub.h +++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub.h @@ -52,7 +52,6 @@ class BufferHubChannel : public pdx::Channel { 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 @@ class BufferHubChannel : public pdx::Channel { 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 @@ class BufferHubChannel : public pdx::Channel { layer_count(layer_count), format(format), usage(usage), - pending_count(pending_count), state(state), signaled_mask(signaled_mask), index(index) {} diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_node.h b/services/vr/bufferhubd/include/private/dvr/buffer_node.h new file mode 100644 index 0000000000..997aeda917 --- /dev/null +++ b/services/vr/bufferhubd/include/private/dvr/buffer_node.h @@ -0,0 +1,16 @@ +#ifndef ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ +#define ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ +// TODO(b/118891412) Remove this file + +#include <bufferhub/BufferNode.h> + +namespace android { +namespace dvr { + +typedef android::frameworks::bufferhub::V1_0::implementation::BufferNode + BufferNode; + +} // namespace dvr +} // namespace android + +#endif // ANDROID_DVR_BUFFERHUBD_BUFFER_NODE_H_ diff --git a/services/vr/bufferhubd/consumer_channel.h b/services/vr/bufferhubd/include/private/dvr/consumer_channel.h index 55cf96920d..5ee551f115 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 @@ class ConsumerChannel : public BufferHubChannel { 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 @@ class ConsumerChannel : public BufferHubChannel { 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 index 8437c4cd04..3a81b03761 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 @@ class ConsumerQueueChannel : public BufferHubChannel { // 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 @@ class ConsumerQueueChannel : public BufferHubChannel { 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 index 67fdf150a0..96ef1a20a0 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,22 +42,27 @@ class ProducerChannel : public BufferHubChannel { ~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); @@ -69,30 +73,24 @@ class ProducerChannel : public BufferHubChannel { 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,10 @@ class ProducerChannel : public BufferHubChannel { 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); 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 index e825f47774..c4003da419 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 @@ class ProducerQueueChannel : public BufferHubChannel { 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 BufferProducer 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 BufferProducer 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 b6977aaa63..5c484b8505 100644 --- a/services/vr/bufferhubd/producer_channel.cpp +++ b/services/vr/bufferhubd/producer_channel.cpp @@ -1,19 +1,18 @@ -#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/buffer_channel.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 +26,6 @@ using android::pdx::rpc::WrapBuffer; 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 +56,6 @@ ProducerChannel::ProducerChannel(BufferHubService* service, int channel_id, 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) { @@ -104,9 +93,16 @@ int ProducerChannel::InitializeBuffer() { // 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); + new (&metadata_header_->buffer_state) std::atomic<uint32_t>(0); + fence_state_ = new (&metadata_header_->fence_state) std::atomic<uint32_t>(0); + active_clients_bit_mask_ = + new (&metadata_header_->active_clients_bit_mask) 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 +119,7 @@ int ProducerChannel::InitializeBuffer() { 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 +164,9 @@ Status<std::shared_ptr<ProducerChannel>> ProducerChannel::Create( 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,14 +175,15 @@ ProducerChannel::~ProducerChannel() { 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(), + buffer_.usage(), + buffer_state_->load(std::memory_order_acquire), signaled_mask, metadata_header_->queue_index); } @@ -224,107 +222,187 @@ bool ProducerChannel::HandleMessage(Message& message) { *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); + if (BufferHubDefs::IsBufferReleased(current_buffer_state) || + BufferHubDefs::AnyClientGained(current_buffer_state)) { + return {status.take()}; + } + + // 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 (BufferHubDefs::IsBufferReleased(current_buffer_state) || + BufferHubDefs::AnyClientGained(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) { + consumer->OnProducerPosted(); } - active_consumer_bit_mask_ |= consumer_state_bit; 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("ProducerChannel::OnProducerPost: buffer_id=%d, state=0x%x", + 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 +421,44 @@ Status<void> ProducerChannel::OnProducerPost( 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); - } + ALOGW("ProducerChannel::OnGain: buffer_id=%d", 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)) { + uint32_t buffer_state = buffer_state_->load(std::memory_order_acquire); + if (!BufferHubDefs::IsClientGained( + buffer_state, BufferHubDefs::kFirstClientStateMask)) { // Can only detach a BufferProducer 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 +480,9 @@ Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach( 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 +491,23 @@ Status<RemoteChannelHandle> ProducerChannel::OnProducerDetach( 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 +516,6 @@ Status<void> ProducerChannel::OnConsumerRelease(Message&, 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 +535,55 @@ Status<void> ProducerChannel::OnConsumerRelease(Message&, } } - 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()); - + uint32_t current_buffer_state = + buffer_state_->load(std::memory_order_acquire); + if (BufferHubDefs::IsBufferReleased(current_buffer_state & + ~orphaned_consumer_bit_mask_)) { + 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_, + "%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_->queue_index); 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; + + uint32_t current_buffer_state = + buffer_state_->load(std::memory_order_acquire); + if (BufferHubDefs::IsBufferReleased(current_buffer_state & + ~orphaned_consumer_bit_mask_)) { + 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_->queue_index, + buffer_state_->load(std::memory_order_acquire), + fence_state_->load(std::memory_order_acquire)); } void ProducerChannel::AddConsumer(ConsumerChannel* channel) { @@ -557,43 +593,66 @@ void ProducerChannel::AddConsumer(ConsumerChannel* channel) { 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(); - - const uint64_t buffer_state = buffer_state_->load(); - if (BufferHubDefs::IsBufferPosted(buffer_state) || - BufferHubDefs::IsBufferAcquired(buffer_state)) { + // 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 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::AnyClientGained(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, diff --git a/services/vr/bufferhubd/producer_queue_channel.cpp b/services/vr/bufferhubd/producer_queue_channel.cpp index c0c48c2dc1..6b33f5094c 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 @@ bool ProducerQueueChannel::HandleMessage(Message& message) { 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 @@ ProducerQueueChannel::AllocateBuffer(Message& message, uint32_t width, 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 90edf69697..258b45b95a 100644 --- a/services/vr/hardware_composer/Android.bp +++ b/services/vr/hardware_composer/Android.bp @@ -39,6 +39,10 @@ cc_library_shared { "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 +52,9 @@ cc_library_shared { cflags: [ "-DLOG_TAG=\"vr_hwc\"", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", "-Wall", "-Werror", - // mVrClient unused in vr_composer_client.cpp "-Wno-error=unused-private-field", // Warnings in vr_hwc.cpp to be fixed after sync of goog/master. "-Wno-sign-compare", @@ -115,6 +119,7 @@ cc_library_static { 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 d775711ef7..786d5fac98 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::createCommandEngine() { } 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 76b1c4ff83..de22640321 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 @@ class VrComposerClient : public ComposerClient { 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 4af47d2a35..fb7932d804 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 @@ void HwcDisplay::dumpDebugInfo(std::string* result) const { //////////////////////////////////////////////////////////////////////////////// // 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 @@ Error VrHwc::getActiveConfig(Display display, Config* outConfig) { 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 @@ Error VrHwc::setColorMode(Display display, ColorMode mode) { 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 @@ Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) { 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 @@ Error VrHwc::presentDisplay(Display display, int32_t* outPresentFence, 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 @@ HwcDisplay* VrHwc::FindDisplay(Display display) { 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 @@ void HwcLayer::dumpDebugInfo(std::string* result) const { 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 85e587abbe..f9872b2290 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> @@ -156,10 +157,8 @@ class HwcDisplay { 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_; } @@ -187,7 +186,7 @@ class HwcDisplay { 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_; @@ -299,8 +298,23 @@ class VrHwc : public IComposer, public ComposerHal, public ComposerView { 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. @@ -312,6 +326,8 @@ class VrHwc : public IComposer, public ComposerHal, public ComposerView { 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 0000000000..1068cac33a --- /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 1a7264c701..d940b79086 100644 --- a/services/vr/performanced/cpu_set.cpp +++ b/services/vr/performanced/cpu_set.cpp @@ -106,7 +106,7 @@ std::vector<CpuSet*> CpuSetManager::GetCpuSets() { 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 @@ std::string CpuSetManager::DumpState() const { return a->path() < b->path(); }); - std::ostringstream stream; - stream << std::left; stream << std::setw(max_path) << "Path"; stream << " "; @@ -146,8 +144,6 @@ std::string CpuSetManager::DumpState() const { 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 6879272dc4..4c25e9ec4a 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 @@ class CpuSetManager { // 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 4c26671b11..73dcf76fd0 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 kCpuSetBasePath[] = "/dev/cpuset"; 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 @@ PerformanceService::PerformanceService() // 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 @@ PerformanceService::PerformanceService() 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 @@ PerformanceService::PerformanceService() {.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 @@ bool PerformanceService::IsInitialized() const { } 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 @@ Status<void> PerformanceService::OnSetSchedulerPolicy( // 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::OnSetSchedulerPolicy( 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 @@ Status<void> PerformanceService::OnSetSchedulerClass( // 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 @@ Status<std::string> PerformanceService::OnGetCpuPartition(Message& message, 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 @@ Status<void> PerformanceService::HandleMessage(Message& message) { } } +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 6b519abac3..fe63756dbd 100644 --- a/services/vr/performanced/performance_service.h +++ b/services/vr/performanced/performance_service.h @@ -39,6 +39,14 @@ class PerformanceService : public pdx::ServiceBase<PerformanceService> { 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 @@ class PerformanceService : public pdx::ServiceBase<PerformanceService> { std::function<bool(const pdx::Message& message, const Task& task)> partition_permission_check_; + pid_t vr_app_render_thread_ = -1; + PerformanceService(const PerformanceService&) = delete; void operator=(const PerformanceService&) = delete; }; diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index fdbd969d66..a607a5d098 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -984,7 +984,7 @@ VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, 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; diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 3db8a3962e..86013737c6 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -335,15 +335,15 @@ uint32_t get_num_ready_timings(Swapchain& swapchain) { 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; @@ -569,21 +569,17 @@ VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/, switch (native_format) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_FP16: 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; } @@ -700,6 +696,10 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, 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}, }; const uint32_t kNumWideColorFormats = sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]); diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp index 93620f4157..78d6694270 100644 --- a/vulkan/vkjson/Android.bp +++ b/vulkan/vkjson/Android.bp @@ -10,7 +10,6 @@ cc_library_static { "-Wimplicit-fallthrough", ], cppflags: [ - "-std=c++11", "-Wno-sign-compare", ], export_include_dirs: [ @@ -37,7 +36,6 @@ cc_library_static { "-Wimplicit-fallthrough", ], cppflags: [ - "-std=c++11", "-Wno-sign-compare", ], export_include_dirs: [ |