diff options
651 files changed, 7628 insertions, 4879 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 2383732234..d427ecf280 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -170,6 +170,7 @@ void add_mountinfo(); #define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0" #define BLK_DEV_SYS_DIR "/sys/block" +#define AFLAGS "/system/bin/aflags" #define RECOVERY_DIR "/cache/recovery" #define RECOVERY_DATA_DIR "/data/misc/recovery" #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log" @@ -1792,6 +1793,10 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { RunCommand("ACONFIG FLAGS", {PRINT_FLAGS}, CommandOptions::WithTimeout(10).Always().DropRoot().Build()); + RunCommand("ACONFIG FLAGS DUMP", {AFLAGS, "list"}, + CommandOptions::WithTimeout(10).Always().AsRootIfAvailable().Build()); + RunCommand("WHICH ACONFIG FLAG STORAGE", {AFLAGS, "which-backing"}, + CommandOptions::WithTimeout(10).Always().AsRootIfAvailable().Build()); RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh index 28bd7932a2..b7ad33144b 100644 --- a/cmds/installd/otapreopt_script.sh +++ b/cmds/installd/otapreopt_script.sh @@ -50,6 +50,37 @@ else exit 1 fi +# A source that infinitely emits arbitrary lines. +# When connected to STDIN of another process, this source keeps STDIN open until +# the consumer process closes STDIN or this script dies. +function infinite_source { + while echo .; do + sleep 1 + done +} + +PR_DEXOPT_JOB_VERSION="$(pm art pr-dexopt-job --version)" +if (( $? == 0 )) && (( $PR_DEXOPT_JOB_VERSION >= 3 )); then + # Delegate to Pre-reboot Dexopt, a feature of ART Service. + # ART Service decides what to do with this request: + # - If Pre-reboot Dexopt is disabled or unsupported, the command returns + # non-zero. This is always the case if the current system is Android 14 or + # earlier. + # - If Pre-reboot Dexopt is enabled in synchronous mode, the command blocks + # until Pre-reboot Dexopt finishes, and returns zero no matter it succeeds or + # not. This is the default behavior if the current system is Android 15. + # - If Pre-reboot Dexopt is enabled in asynchronous mode, the command schedules + # an asynchronous job and returns 0 immediately. The job will then run by the + # job scheduler when the device is idle and charging. + if infinite_source | pm art on-ota-staged --slot "$TARGET_SLOT_SUFFIX"; then + # Handled by Pre-reboot Dexopt. + exit 0 + fi + echo "Pre-reboot Dexopt not enabled. Fall back to otapreopt." +else + echo "Pre-reboot Dexopt is too old. Fall back to otapreopt." +fi + if [ "$(/system/bin/otapreopt_chroot --version)" != 2 ]; then # We require an updated chroot wrapper that reads dexopt commands from stdin. # Even if we kept compat with the old binary, the OTA preopt wouldn't work due diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-0 b/cmds/installd/tests/corpus/seed-2024-08-29-0 Binary files differnew file mode 100644 index 0000000000..a09fc84fc5 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-0 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-1 b/cmds/installd/tests/corpus/seed-2024-08-29-1 Binary files differnew file mode 100644 index 0000000000..c96616aa29 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-1 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-10 b/cmds/installd/tests/corpus/seed-2024-08-29-10 Binary files differnew file mode 100644 index 0000000000..0b21bd157d --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-10 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-100 b/cmds/installd/tests/corpus/seed-2024-08-29-100 Binary files differnew file mode 100644 index 0000000000..225d123966 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-100 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-101 b/cmds/installd/tests/corpus/seed-2024-08-29-101 Binary files differnew file mode 100644 index 0000000000..c507b576cc --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-101 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-102 b/cmds/installd/tests/corpus/seed-2024-08-29-102 Binary files differnew file mode 100644 index 0000000000..e75ef89ad3 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-102 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-103 b/cmds/installd/tests/corpus/seed-2024-08-29-103 Binary files differnew file mode 100644 index 0000000000..fb28f4d712 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-103 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-104 b/cmds/installd/tests/corpus/seed-2024-08-29-104 Binary files differnew file mode 100644 index 0000000000..b5a22222e0 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-104 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-105 b/cmds/installd/tests/corpus/seed-2024-08-29-105 Binary files differnew file mode 100644 index 0000000000..a126c0eb6a --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-105 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-106 b/cmds/installd/tests/corpus/seed-2024-08-29-106 Binary files differnew file mode 100644 index 0000000000..ad84e5788e --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-106 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-107 b/cmds/installd/tests/corpus/seed-2024-08-29-107 Binary files differnew file mode 100644 index 0000000000..6a2bc6f35a --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-107 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-108 b/cmds/installd/tests/corpus/seed-2024-08-29-108 Binary files differnew file mode 100644 index 0000000000..578b55ad17 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-108 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-109 b/cmds/installd/tests/corpus/seed-2024-08-29-109 Binary files differnew file mode 100644 index 0000000000..44f853d014 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-109 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-11 b/cmds/installd/tests/corpus/seed-2024-08-29-11 Binary files differnew file mode 100644 index 0000000000..28fd841c63 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-11 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-110 b/cmds/installd/tests/corpus/seed-2024-08-29-110 Binary files differnew file mode 100644 index 0000000000..a013ee8997 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-110 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-111 b/cmds/installd/tests/corpus/seed-2024-08-29-111 Binary files differnew file mode 100644 index 0000000000..1bb6185965 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-111 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-112 b/cmds/installd/tests/corpus/seed-2024-08-29-112 Binary files differnew file mode 100644 index 0000000000..83008e956d --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-112 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-113 b/cmds/installd/tests/corpus/seed-2024-08-29-113 Binary files differnew file mode 100644 index 0000000000..c9460cbbc6 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-113 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-114 b/cmds/installd/tests/corpus/seed-2024-08-29-114 Binary files differnew file mode 100644 index 0000000000..feb0384a92 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-114 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-115 b/cmds/installd/tests/corpus/seed-2024-08-29-115 Binary files differnew file mode 100644 index 0000000000..cd28076566 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-115 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-116 b/cmds/installd/tests/corpus/seed-2024-08-29-116 Binary files differnew file mode 100644 index 0000000000..c48730ee76 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-116 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-117 b/cmds/installd/tests/corpus/seed-2024-08-29-117 Binary files differnew file mode 100644 index 0000000000..bde1be0c10 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-117 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-118 b/cmds/installd/tests/corpus/seed-2024-08-29-118 Binary files differnew file mode 100644 index 0000000000..0d86d18c97 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-118 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-119 b/cmds/installd/tests/corpus/seed-2024-08-29-119 Binary files differnew file mode 100644 index 0000000000..de358941fe --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-119 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-12 b/cmds/installd/tests/corpus/seed-2024-08-29-12 Binary files differnew file mode 100644 index 0000000000..5565f811bb --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-12 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-120 b/cmds/installd/tests/corpus/seed-2024-08-29-120 Binary files differnew file mode 100644 index 0000000000..51c05261f9 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-120 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-121 b/cmds/installd/tests/corpus/seed-2024-08-29-121 Binary files differnew file mode 100644 index 0000000000..2d84c76377 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-121 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-122 b/cmds/installd/tests/corpus/seed-2024-08-29-122 Binary files differnew file mode 100644 index 0000000000..f25a7c48e4 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-122 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-123 b/cmds/installd/tests/corpus/seed-2024-08-29-123 Binary files differnew file mode 100644 index 0000000000..fe8eb342bf --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-123 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-124 b/cmds/installd/tests/corpus/seed-2024-08-29-124 Binary files differnew file mode 100644 index 0000000000..170e8ec1a2 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-124 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-125 b/cmds/installd/tests/corpus/seed-2024-08-29-125 Binary files differnew file mode 100644 index 0000000000..24e8bb8b72 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-125 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-126 b/cmds/installd/tests/corpus/seed-2024-08-29-126 Binary files differnew file mode 100644 index 0000000000..92536a3515 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-126 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-127 b/cmds/installd/tests/corpus/seed-2024-08-29-127 Binary files differnew file mode 100644 index 0000000000..3a5436a0e7 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-127 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-128 b/cmds/installd/tests/corpus/seed-2024-08-29-128 Binary files differnew file mode 100644 index 0000000000..93d131da9e --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-128 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-129 b/cmds/installd/tests/corpus/seed-2024-08-29-129 Binary files differnew file mode 100644 index 0000000000..842dae41c3 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-129 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-13 b/cmds/installd/tests/corpus/seed-2024-08-29-13 Binary files differnew file mode 100644 index 0000000000..bc0ec3d94f --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-13 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-130 b/cmds/installd/tests/corpus/seed-2024-08-29-130 Binary files differnew file mode 100644 index 0000000000..9b6ed59902 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-130 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-131 b/cmds/installd/tests/corpus/seed-2024-08-29-131 Binary files differnew file mode 100644 index 0000000000..82a5d2f34f --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-131 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-132 b/cmds/installd/tests/corpus/seed-2024-08-29-132 Binary files differnew file mode 100644 index 0000000000..445fdc5880 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-132 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-133 b/cmds/installd/tests/corpus/seed-2024-08-29-133 Binary files differnew file mode 100644 index 0000000000..0a6e9caac7 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-133 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-134 b/cmds/installd/tests/corpus/seed-2024-08-29-134 Binary files differnew file mode 100644 index 0000000000..a359603e95 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-134 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-135 b/cmds/installd/tests/corpus/seed-2024-08-29-135 Binary files differnew file mode 100644 index 0000000000..c16b303dba --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-135 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-136 b/cmds/installd/tests/corpus/seed-2024-08-29-136 Binary files differnew file mode 100644 index 0000000000..f7a360ffb9 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-136 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-137 b/cmds/installd/tests/corpus/seed-2024-08-29-137 Binary files differnew file mode 100644 index 0000000000..38a113486b --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-137 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-138 b/cmds/installd/tests/corpus/seed-2024-08-29-138 Binary files differnew file mode 100644 index 0000000000..b9db4a7d09 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-138 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-139 b/cmds/installd/tests/corpus/seed-2024-08-29-139 Binary files differnew file mode 100644 index 0000000000..eb1cf938f3 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-139 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-14 b/cmds/installd/tests/corpus/seed-2024-08-29-14 Binary files differnew file mode 100644 index 0000000000..74f9ad0440 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-14 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-140 b/cmds/installd/tests/corpus/seed-2024-08-29-140 Binary files differnew file mode 100644 index 0000000000..0cf217c041 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-140 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-141 b/cmds/installd/tests/corpus/seed-2024-08-29-141 Binary files differnew file mode 100644 index 0000000000..82763f0b3c --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-141 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-142 b/cmds/installd/tests/corpus/seed-2024-08-29-142 Binary files differnew file mode 100644 index 0000000000..fa1d65665f --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-142 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-15 b/cmds/installd/tests/corpus/seed-2024-08-29-15 Binary files differnew file mode 100644 index 0000000000..729c604230 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-15 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-16 b/cmds/installd/tests/corpus/seed-2024-08-29-16 Binary files differnew file mode 100644 index 0000000000..4dc08793c2 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-16 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-17 b/cmds/installd/tests/corpus/seed-2024-08-29-17 Binary files differnew file mode 100644 index 0000000000..ac7ff13e52 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-17 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-18 b/cmds/installd/tests/corpus/seed-2024-08-29-18 Binary files differnew file mode 100644 index 0000000000..2b240f46fb --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-18 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-19 b/cmds/installd/tests/corpus/seed-2024-08-29-19 Binary files differnew file mode 100644 index 0000000000..a0c881b37f --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-19 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-2 b/cmds/installd/tests/corpus/seed-2024-08-29-2 Binary files differnew file mode 100644 index 0000000000..2593acb6e4 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-2 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-20 b/cmds/installd/tests/corpus/seed-2024-08-29-20 Binary files differnew file mode 100644 index 0000000000..c55dc7f245 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-20 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-21 b/cmds/installd/tests/corpus/seed-2024-08-29-21 Binary files differnew file mode 100644 index 0000000000..63d7a1455b --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-21 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-22 b/cmds/installd/tests/corpus/seed-2024-08-29-22 Binary files differnew file mode 100644 index 0000000000..209f426a01 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-22 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-23 b/cmds/installd/tests/corpus/seed-2024-08-29-23 Binary files differnew file mode 100644 index 0000000000..8e1775f382 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-23 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-24 b/cmds/installd/tests/corpus/seed-2024-08-29-24 Binary files differnew file mode 100644 index 0000000000..4c40f3cffa --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-24 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-25 b/cmds/installd/tests/corpus/seed-2024-08-29-25 Binary files differnew file mode 100644 index 0000000000..d006b202f6 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-25 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-26 b/cmds/installd/tests/corpus/seed-2024-08-29-26 Binary files differnew file mode 100644 index 0000000000..26893b0118 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-26 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-27 b/cmds/installd/tests/corpus/seed-2024-08-29-27 Binary files differnew file mode 100644 index 0000000000..ac81138dfa --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-27 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-28 b/cmds/installd/tests/corpus/seed-2024-08-29-28 Binary files differnew file mode 100644 index 0000000000..71f074b7a0 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-28 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-29 b/cmds/installd/tests/corpus/seed-2024-08-29-29 Binary files differnew file mode 100644 index 0000000000..65dbb6dff7 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-29 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-3 b/cmds/installd/tests/corpus/seed-2024-08-29-3 Binary files differnew file mode 100644 index 0000000000..28ab83fbfd --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-3 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-30 b/cmds/installd/tests/corpus/seed-2024-08-29-30 Binary files differnew file mode 100644 index 0000000000..3b96286573 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-30 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-31 b/cmds/installd/tests/corpus/seed-2024-08-29-31 Binary files differnew file mode 100644 index 0000000000..76101b36ec --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-31 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-32 b/cmds/installd/tests/corpus/seed-2024-08-29-32 Binary files differnew file mode 100644 index 0000000000..79a44523e0 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-32 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-33 b/cmds/installd/tests/corpus/seed-2024-08-29-33 Binary files differnew file mode 100644 index 0000000000..e6a1306838 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-33 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-34 b/cmds/installd/tests/corpus/seed-2024-08-29-34 Binary files differnew file mode 100644 index 0000000000..4a7247f888 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-34 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-35 b/cmds/installd/tests/corpus/seed-2024-08-29-35 Binary files differnew file mode 100644 index 0000000000..f420b34fce --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-35 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-36 b/cmds/installd/tests/corpus/seed-2024-08-29-36 Binary files differnew file mode 100644 index 0000000000..83a33acecb --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-36 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-37 b/cmds/installd/tests/corpus/seed-2024-08-29-37 Binary files differnew file mode 100644 index 0000000000..687bf06ea7 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-37 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-38 b/cmds/installd/tests/corpus/seed-2024-08-29-38 Binary files differnew file mode 100644 index 0000000000..40ab0ad591 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-38 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-39 b/cmds/installd/tests/corpus/seed-2024-08-29-39 Binary files differnew file mode 100644 index 0000000000..3e13978d87 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-39 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-4 b/cmds/installd/tests/corpus/seed-2024-08-29-4 Binary files differnew file mode 100644 index 0000000000..8c47ea3f96 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-4 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-40 b/cmds/installd/tests/corpus/seed-2024-08-29-40 Binary files differnew file mode 100644 index 0000000000..f71791862a --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-40 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-41 b/cmds/installd/tests/corpus/seed-2024-08-29-41 Binary files differnew file mode 100644 index 0000000000..d9c51b93d6 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-41 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-42 b/cmds/installd/tests/corpus/seed-2024-08-29-42 Binary files differnew file mode 100644 index 0000000000..d806e5ee39 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-42 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-43 b/cmds/installd/tests/corpus/seed-2024-08-29-43 Binary files differnew file mode 100644 index 0000000000..3bc2708f12 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-43 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-44 b/cmds/installd/tests/corpus/seed-2024-08-29-44 Binary files differnew file mode 100644 index 0000000000..230839af36 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-44 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-45 b/cmds/installd/tests/corpus/seed-2024-08-29-45 Binary files differnew file mode 100644 index 0000000000..40726b9f5d --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-45 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-46 b/cmds/installd/tests/corpus/seed-2024-08-29-46 Binary files differnew file mode 100644 index 0000000000..bf56bd4a47 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-46 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-47 b/cmds/installd/tests/corpus/seed-2024-08-29-47 Binary files differnew file mode 100644 index 0000000000..80cabaf62b --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-47 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-48 b/cmds/installd/tests/corpus/seed-2024-08-29-48 Binary files differnew file mode 100644 index 0000000000..8f2c5f5dd6 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-48 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-49 b/cmds/installd/tests/corpus/seed-2024-08-29-49 Binary files differnew file mode 100644 index 0000000000..f93fbcdfc5 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-49 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-5 b/cmds/installd/tests/corpus/seed-2024-08-29-5 Binary files differnew file mode 100644 index 0000000000..b3f49d1659 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-5 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-50 b/cmds/installd/tests/corpus/seed-2024-08-29-50 Binary files differnew file mode 100644 index 0000000000..68912aeb94 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-50 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-51 b/cmds/installd/tests/corpus/seed-2024-08-29-51 Binary files differnew file mode 100644 index 0000000000..27b315dec6 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-51 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-52 b/cmds/installd/tests/corpus/seed-2024-08-29-52 Binary files differnew file mode 100644 index 0000000000..159eee6ac0 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-52 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-53 b/cmds/installd/tests/corpus/seed-2024-08-29-53 Binary files differnew file mode 100644 index 0000000000..b07cb3c699 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-53 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-54 b/cmds/installd/tests/corpus/seed-2024-08-29-54 Binary files differnew file mode 100644 index 0000000000..a5e7f2cc4d --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-54 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-55 b/cmds/installd/tests/corpus/seed-2024-08-29-55 Binary files differnew file mode 100644 index 0000000000..bd038adc84 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-55 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-56 b/cmds/installd/tests/corpus/seed-2024-08-29-56 Binary files differnew file mode 100644 index 0000000000..8166cb8cb3 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-56 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-57 b/cmds/installd/tests/corpus/seed-2024-08-29-57 Binary files differnew file mode 100644 index 0000000000..fba1e2f35f --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-57 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-58 b/cmds/installd/tests/corpus/seed-2024-08-29-58 Binary files differnew file mode 100644 index 0000000000..f7af8f8e12 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-58 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-59 b/cmds/installd/tests/corpus/seed-2024-08-29-59 Binary files differnew file mode 100644 index 0000000000..2fd68d7c40 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-59 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-6 b/cmds/installd/tests/corpus/seed-2024-08-29-6 Binary files differnew file mode 100644 index 0000000000..9b02a47877 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-6 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-60 b/cmds/installd/tests/corpus/seed-2024-08-29-60 Binary files differnew file mode 100644 index 0000000000..b4c11292ef --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-60 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-61 b/cmds/installd/tests/corpus/seed-2024-08-29-61 Binary files differnew file mode 100644 index 0000000000..46989aa11a --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-61 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-62 b/cmds/installd/tests/corpus/seed-2024-08-29-62 Binary files differnew file mode 100644 index 0000000000..9298d0c33d --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-62 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-63 b/cmds/installd/tests/corpus/seed-2024-08-29-63 Binary files differnew file mode 100644 index 0000000000..326098c31e --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-63 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-64 b/cmds/installd/tests/corpus/seed-2024-08-29-64 Binary files differnew file mode 100644 index 0000000000..61daf4facb --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-64 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-65 b/cmds/installd/tests/corpus/seed-2024-08-29-65 Binary files differnew file mode 100644 index 0000000000..a993900ff5 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-65 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-66 b/cmds/installd/tests/corpus/seed-2024-08-29-66 Binary files differnew file mode 100644 index 0000000000..85e857bae8 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-66 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-67 b/cmds/installd/tests/corpus/seed-2024-08-29-67 Binary files differnew file mode 100644 index 0000000000..b7754838fc --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-67 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-68 b/cmds/installd/tests/corpus/seed-2024-08-29-68 Binary files differnew file mode 100644 index 0000000000..161e7ab41a --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-68 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-69 b/cmds/installd/tests/corpus/seed-2024-08-29-69 Binary files differnew file mode 100644 index 0000000000..6a45dfe0ff --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-69 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-7 b/cmds/installd/tests/corpus/seed-2024-08-29-7 Binary files differnew file mode 100644 index 0000000000..33f61b0fbc --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-7 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-70 b/cmds/installd/tests/corpus/seed-2024-08-29-70 Binary files differnew file mode 100644 index 0000000000..4c16b49151 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-70 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-71 b/cmds/installd/tests/corpus/seed-2024-08-29-71 Binary files differnew file mode 100644 index 0000000000..1534ce12d1 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-71 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-72 b/cmds/installd/tests/corpus/seed-2024-08-29-72 Binary files differnew file mode 100644 index 0000000000..eaa5831917 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-72 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-73 b/cmds/installd/tests/corpus/seed-2024-08-29-73 Binary files differnew file mode 100644 index 0000000000..9df4a75b88 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-73 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-74 b/cmds/installd/tests/corpus/seed-2024-08-29-74 Binary files differnew file mode 100644 index 0000000000..9558ac019d --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-74 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-75 b/cmds/installd/tests/corpus/seed-2024-08-29-75 Binary files differnew file mode 100644 index 0000000000..a399271250 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-75 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-76 b/cmds/installd/tests/corpus/seed-2024-08-29-76 Binary files differnew file mode 100644 index 0000000000..866541d038 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-76 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-77 b/cmds/installd/tests/corpus/seed-2024-08-29-77 Binary files differnew file mode 100644 index 0000000000..e3940d9af6 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-77 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-78 b/cmds/installd/tests/corpus/seed-2024-08-29-78 Binary files differnew file mode 100644 index 0000000000..8122306d16 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-78 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-79 b/cmds/installd/tests/corpus/seed-2024-08-29-79 Binary files differnew file mode 100644 index 0000000000..0f23dfd2f9 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-79 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-8 b/cmds/installd/tests/corpus/seed-2024-08-29-8 Binary files differnew file mode 100644 index 0000000000..7390735a93 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-8 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-80 b/cmds/installd/tests/corpus/seed-2024-08-29-80 Binary files differnew file mode 100644 index 0000000000..e3c36405e7 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-80 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-81 b/cmds/installd/tests/corpus/seed-2024-08-29-81 Binary files differnew file mode 100644 index 0000000000..6c42b9e72f --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-81 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-82 b/cmds/installd/tests/corpus/seed-2024-08-29-82 Binary files differnew file mode 100644 index 0000000000..09184c98d0 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-82 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-83 b/cmds/installd/tests/corpus/seed-2024-08-29-83 Binary files differnew file mode 100644 index 0000000000..734570af84 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-83 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-84 b/cmds/installd/tests/corpus/seed-2024-08-29-84 Binary files differnew file mode 100644 index 0000000000..1a32561b37 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-84 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-85 b/cmds/installd/tests/corpus/seed-2024-08-29-85 Binary files differnew file mode 100644 index 0000000000..5315dfcab3 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-85 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-86 b/cmds/installd/tests/corpus/seed-2024-08-29-86 Binary files differnew file mode 100644 index 0000000000..5f798b9a15 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-86 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-87 b/cmds/installd/tests/corpus/seed-2024-08-29-87 Binary files differnew file mode 100644 index 0000000000..dd1ebe16bb --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-87 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-88 b/cmds/installd/tests/corpus/seed-2024-08-29-88 Binary files differnew file mode 100644 index 0000000000..45cf713628 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-88 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-89 b/cmds/installd/tests/corpus/seed-2024-08-29-89 Binary files differnew file mode 100644 index 0000000000..1053b71f9e --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-89 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-9 b/cmds/installd/tests/corpus/seed-2024-08-29-9 Binary files differnew file mode 100644 index 0000000000..86d511df98 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-9 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-90 b/cmds/installd/tests/corpus/seed-2024-08-29-90 Binary files differnew file mode 100644 index 0000000000..7ce82a08d0 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-90 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-91 b/cmds/installd/tests/corpus/seed-2024-08-29-91 Binary files differnew file mode 100644 index 0000000000..57c43d0803 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-91 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-92 b/cmds/installd/tests/corpus/seed-2024-08-29-92 Binary files differnew file mode 100644 index 0000000000..32a0f3a72c --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-92 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-93 b/cmds/installd/tests/corpus/seed-2024-08-29-93 Binary files differnew file mode 100644 index 0000000000..56dcb66521 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-93 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-94 b/cmds/installd/tests/corpus/seed-2024-08-29-94 Binary files differnew file mode 100644 index 0000000000..17b5a651e1 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-94 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-95 b/cmds/installd/tests/corpus/seed-2024-08-29-95 Binary files differnew file mode 100644 index 0000000000..09630392b0 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-95 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-96 b/cmds/installd/tests/corpus/seed-2024-08-29-96 Binary files differnew file mode 100644 index 0000000000..1c9590579f --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-96 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-97 b/cmds/installd/tests/corpus/seed-2024-08-29-97 Binary files differnew file mode 100644 index 0000000000..518910e7ef --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-97 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-98 b/cmds/installd/tests/corpus/seed-2024-08-29-98 Binary files differnew file mode 100644 index 0000000000..520feb29f2 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-98 diff --git a/cmds/installd/tests/corpus/seed-2024-08-29-99 b/cmds/installd/tests/corpus/seed-2024-08-29-99 Binary files differnew file mode 100644 index 0000000000..c1da923585 --- /dev/null +++ b/cmds/installd/tests/corpus/seed-2024-08-29-99 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-0 b/cmds/servicemanager/corpus/seed-2024-08-29-0 Binary files differnew file mode 100644 index 0000000000..fe4942e53c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-0 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-1 b/cmds/servicemanager/corpus/seed-2024-08-29-1 Binary files differnew file mode 100644 index 0000000000..05c8be2f8a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-1 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-10 b/cmds/servicemanager/corpus/seed-2024-08-29-10 Binary files differnew file mode 100644 index 0000000000..427dc45ddd --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-10 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-100 b/cmds/servicemanager/corpus/seed-2024-08-29-100 Binary files differnew file mode 100644 index 0000000000..92584e3ac6 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-100 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-101 b/cmds/servicemanager/corpus/seed-2024-08-29-101 Binary files differnew file mode 100644 index 0000000000..4dd73ac592 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-101 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-102 b/cmds/servicemanager/corpus/seed-2024-08-29-102 Binary files differnew file mode 100644 index 0000000000..30c37a0d0e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-102 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-103 b/cmds/servicemanager/corpus/seed-2024-08-29-103 Binary files differnew file mode 100644 index 0000000000..76ae112ef3 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-103 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-104 b/cmds/servicemanager/corpus/seed-2024-08-29-104 Binary files differnew file mode 100644 index 0000000000..8ca22015ab --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-104 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-105 b/cmds/servicemanager/corpus/seed-2024-08-29-105 Binary files differnew file mode 100644 index 0000000000..987fcc1969 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-105 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-106 b/cmds/servicemanager/corpus/seed-2024-08-29-106 Binary files differnew file mode 100644 index 0000000000..9f09e29cd1 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-106 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-107 b/cmds/servicemanager/corpus/seed-2024-08-29-107 Binary files differnew file mode 100644 index 0000000000..8f9518d1e4 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-107 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-108 b/cmds/servicemanager/corpus/seed-2024-08-29-108 Binary files differnew file mode 100644 index 0000000000..decb38a8be --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-108 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-109 b/cmds/servicemanager/corpus/seed-2024-08-29-109 Binary files differnew file mode 100644 index 0000000000..e3b4426d4e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-109 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-11 b/cmds/servicemanager/corpus/seed-2024-08-29-11 Binary files differnew file mode 100644 index 0000000000..177a1cdedd --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-11 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-110 b/cmds/servicemanager/corpus/seed-2024-08-29-110 Binary files differnew file mode 100644 index 0000000000..35de9ca395 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-110 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-111 b/cmds/servicemanager/corpus/seed-2024-08-29-111 Binary files differnew file mode 100644 index 0000000000..ae6076fc14 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-111 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-112 b/cmds/servicemanager/corpus/seed-2024-08-29-112 Binary files differnew file mode 100644 index 0000000000..3d64f37139 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-112 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-113 b/cmds/servicemanager/corpus/seed-2024-08-29-113 Binary files differnew file mode 100644 index 0000000000..2b14f1d91f --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-113 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-114 b/cmds/servicemanager/corpus/seed-2024-08-29-114 Binary files differnew file mode 100644 index 0000000000..180831f25c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-114 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-115 b/cmds/servicemanager/corpus/seed-2024-08-29-115 Binary files differnew file mode 100644 index 0000000000..71184d205d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-115 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-116 b/cmds/servicemanager/corpus/seed-2024-08-29-116 Binary files differnew file mode 100644 index 0000000000..98c6163e2d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-116 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-117 b/cmds/servicemanager/corpus/seed-2024-08-29-117 Binary files differnew file mode 100644 index 0000000000..e6dd7bbdfb --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-117 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-118 b/cmds/servicemanager/corpus/seed-2024-08-29-118 Binary files differnew file mode 100644 index 0000000000..dd181ae4a4 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-118 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-119 b/cmds/servicemanager/corpus/seed-2024-08-29-119 Binary files differnew file mode 100644 index 0000000000..25de1b262f --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-119 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-12 b/cmds/servicemanager/corpus/seed-2024-08-29-12 Binary files differnew file mode 100644 index 0000000000..1312d2c9be --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-12 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-120 b/cmds/servicemanager/corpus/seed-2024-08-29-120 Binary files differnew file mode 100644 index 0000000000..cef973d8e9 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-120 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-121 b/cmds/servicemanager/corpus/seed-2024-08-29-121 Binary files differnew file mode 100644 index 0000000000..7fd1df2620 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-121 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-122 b/cmds/servicemanager/corpus/seed-2024-08-29-122 Binary files differnew file mode 100644 index 0000000000..5fefc4b95e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-122 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-123 b/cmds/servicemanager/corpus/seed-2024-08-29-123 Binary files differnew file mode 100644 index 0000000000..714b6b5f10 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-123 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-124 b/cmds/servicemanager/corpus/seed-2024-08-29-124 Binary files differnew file mode 100644 index 0000000000..925bfcc657 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-124 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-125 b/cmds/servicemanager/corpus/seed-2024-08-29-125 Binary files differnew file mode 100644 index 0000000000..6dbec24c18 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-125 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-126 b/cmds/servicemanager/corpus/seed-2024-08-29-126 Binary files differnew file mode 100644 index 0000000000..d5cdcaa6ab --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-126 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-127 b/cmds/servicemanager/corpus/seed-2024-08-29-127 Binary files differnew file mode 100644 index 0000000000..13d0eb544a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-127 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-128 b/cmds/servicemanager/corpus/seed-2024-08-29-128 Binary files differnew file mode 100644 index 0000000000..471371c657 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-128 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-129 b/cmds/servicemanager/corpus/seed-2024-08-29-129 Binary files differnew file mode 100644 index 0000000000..29087958c4 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-129 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-13 b/cmds/servicemanager/corpus/seed-2024-08-29-13 Binary files differnew file mode 100644 index 0000000000..6c8bd0ac60 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-13 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-130 b/cmds/servicemanager/corpus/seed-2024-08-29-130 Binary files differnew file mode 100644 index 0000000000..3a64ac5eb4 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-130 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-131 b/cmds/servicemanager/corpus/seed-2024-08-29-131 Binary files differnew file mode 100644 index 0000000000..d1da2ea61d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-131 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-132 b/cmds/servicemanager/corpus/seed-2024-08-29-132 Binary files differnew file mode 100644 index 0000000000..6de377e760 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-132 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-133 b/cmds/servicemanager/corpus/seed-2024-08-29-133 Binary files differnew file mode 100644 index 0000000000..38ffcb922d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-133 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-134 b/cmds/servicemanager/corpus/seed-2024-08-29-134 Binary files differnew file mode 100644 index 0000000000..6e828ae599 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-134 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-135 b/cmds/servicemanager/corpus/seed-2024-08-29-135 Binary files differnew file mode 100644 index 0000000000..c3eb8275aa --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-135 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-136 b/cmds/servicemanager/corpus/seed-2024-08-29-136 Binary files differnew file mode 100644 index 0000000000..9b1fafb22a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-136 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-137 b/cmds/servicemanager/corpus/seed-2024-08-29-137 Binary files differnew file mode 100644 index 0000000000..059b55ba8b --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-137 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-138 b/cmds/servicemanager/corpus/seed-2024-08-29-138 Binary files differnew file mode 100644 index 0000000000..391bd8c2c5 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-138 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-139 b/cmds/servicemanager/corpus/seed-2024-08-29-139 Binary files differnew file mode 100644 index 0000000000..8ea28db180 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-139 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-14 b/cmds/servicemanager/corpus/seed-2024-08-29-14 Binary files differnew file mode 100644 index 0000000000..2c704b4f47 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-14 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-140 b/cmds/servicemanager/corpus/seed-2024-08-29-140 Binary files differnew file mode 100644 index 0000000000..621c5365b7 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-140 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-141 b/cmds/servicemanager/corpus/seed-2024-08-29-141 Binary files differnew file mode 100644 index 0000000000..1d8532475f --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-141 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-142 b/cmds/servicemanager/corpus/seed-2024-08-29-142 Binary files differnew file mode 100644 index 0000000000..1df0205b6d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-142 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-143 b/cmds/servicemanager/corpus/seed-2024-08-29-143 Binary files differnew file mode 100644 index 0000000000..be5ddea45e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-143 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-144 b/cmds/servicemanager/corpus/seed-2024-08-29-144 Binary files differnew file mode 100644 index 0000000000..dd7eedfecf --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-144 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-145 b/cmds/servicemanager/corpus/seed-2024-08-29-145 Binary files differnew file mode 100644 index 0000000000..a9c28f91db --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-145 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-146 b/cmds/servicemanager/corpus/seed-2024-08-29-146 Binary files differnew file mode 100644 index 0000000000..8e64a65134 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-146 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-147 b/cmds/servicemanager/corpus/seed-2024-08-29-147 Binary files differnew file mode 100644 index 0000000000..f65abe0cf6 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-147 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-148 b/cmds/servicemanager/corpus/seed-2024-08-29-148 Binary files differnew file mode 100644 index 0000000000..174e50a2f8 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-148 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-149 b/cmds/servicemanager/corpus/seed-2024-08-29-149 Binary files differnew file mode 100644 index 0000000000..3d58671f16 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-149 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-15 b/cmds/servicemanager/corpus/seed-2024-08-29-15 Binary files differnew file mode 100644 index 0000000000..a1c47d31d5 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-15 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-150 b/cmds/servicemanager/corpus/seed-2024-08-29-150 Binary files differnew file mode 100644 index 0000000000..a41c9c84b4 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-150 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-151 b/cmds/servicemanager/corpus/seed-2024-08-29-151 Binary files differnew file mode 100644 index 0000000000..013f84d92a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-151 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-152 b/cmds/servicemanager/corpus/seed-2024-08-29-152 Binary files differnew file mode 100644 index 0000000000..ada2ead1e2 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-152 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-153 b/cmds/servicemanager/corpus/seed-2024-08-29-153 Binary files differnew file mode 100644 index 0000000000..1b565618e9 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-153 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-154 b/cmds/servicemanager/corpus/seed-2024-08-29-154 Binary files differnew file mode 100644 index 0000000000..8fea50f9a3 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-154 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-155 b/cmds/servicemanager/corpus/seed-2024-08-29-155 Binary files differnew file mode 100644 index 0000000000..ddcd8f31c1 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-155 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-156 b/cmds/servicemanager/corpus/seed-2024-08-29-156 Binary files differnew file mode 100644 index 0000000000..19ab7aec2c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-156 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-157 b/cmds/servicemanager/corpus/seed-2024-08-29-157 Binary files differnew file mode 100644 index 0000000000..bc89bf5b68 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-157 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-158 b/cmds/servicemanager/corpus/seed-2024-08-29-158 Binary files differnew file mode 100644 index 0000000000..64867f1c2a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-158 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-159 b/cmds/servicemanager/corpus/seed-2024-08-29-159 Binary files differnew file mode 100644 index 0000000000..fe77d0b3f8 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-159 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-16 b/cmds/servicemanager/corpus/seed-2024-08-29-16 Binary files differnew file mode 100644 index 0000000000..f1002d7d06 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-16 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-160 b/cmds/servicemanager/corpus/seed-2024-08-29-160 Binary files differnew file mode 100644 index 0000000000..9c2123f2b9 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-160 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-161 b/cmds/servicemanager/corpus/seed-2024-08-29-161 Binary files differnew file mode 100644 index 0000000000..0fc8e863c5 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-161 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-162 b/cmds/servicemanager/corpus/seed-2024-08-29-162 Binary files differnew file mode 100644 index 0000000000..a13408506c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-162 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-163 b/cmds/servicemanager/corpus/seed-2024-08-29-163 Binary files differnew file mode 100644 index 0000000000..c23e78c02f --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-163 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-164 b/cmds/servicemanager/corpus/seed-2024-08-29-164 Binary files differnew file mode 100644 index 0000000000..d4feab0651 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-164 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-165 b/cmds/servicemanager/corpus/seed-2024-08-29-165 Binary files differnew file mode 100644 index 0000000000..9cbdc4f4cb --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-165 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-166 b/cmds/servicemanager/corpus/seed-2024-08-29-166 Binary files differnew file mode 100644 index 0000000000..d4cf647d8e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-166 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-167 b/cmds/servicemanager/corpus/seed-2024-08-29-167 Binary files differnew file mode 100644 index 0000000000..5023909539 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-167 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-168 b/cmds/servicemanager/corpus/seed-2024-08-29-168 Binary files differnew file mode 100644 index 0000000000..846d0ec5bc --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-168 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-169 b/cmds/servicemanager/corpus/seed-2024-08-29-169 Binary files differnew file mode 100644 index 0000000000..cf6d882330 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-169 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-17 b/cmds/servicemanager/corpus/seed-2024-08-29-17 Binary files differnew file mode 100644 index 0000000000..6c21de860e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-17 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-170 b/cmds/servicemanager/corpus/seed-2024-08-29-170 Binary files differnew file mode 100644 index 0000000000..d9707cb129 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-170 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-171 b/cmds/servicemanager/corpus/seed-2024-08-29-171 Binary files differnew file mode 100644 index 0000000000..ea947f6b3f --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-171 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-172 b/cmds/servicemanager/corpus/seed-2024-08-29-172 Binary files differnew file mode 100644 index 0000000000..2754437308 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-172 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-173 b/cmds/servicemanager/corpus/seed-2024-08-29-173 Binary files differnew file mode 100644 index 0000000000..96e8d563a2 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-173 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-174 b/cmds/servicemanager/corpus/seed-2024-08-29-174 Binary files differnew file mode 100644 index 0000000000..aa6472e7e5 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-174 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-175 b/cmds/servicemanager/corpus/seed-2024-08-29-175 Binary files differnew file mode 100644 index 0000000000..41e789479b --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-175 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-176 b/cmds/servicemanager/corpus/seed-2024-08-29-176 Binary files differnew file mode 100644 index 0000000000..b94712aefe --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-176 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-177 b/cmds/servicemanager/corpus/seed-2024-08-29-177 Binary files differnew file mode 100644 index 0000000000..4925e6254a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-177 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-178 b/cmds/servicemanager/corpus/seed-2024-08-29-178 Binary files differnew file mode 100644 index 0000000000..9ec943d9fe --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-178 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-179 b/cmds/servicemanager/corpus/seed-2024-08-29-179 Binary files differnew file mode 100644 index 0000000000..e173bd3331 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-179 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-18 b/cmds/servicemanager/corpus/seed-2024-08-29-18 Binary files differnew file mode 100644 index 0000000000..aa0b101ecd --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-18 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-180 b/cmds/servicemanager/corpus/seed-2024-08-29-180 Binary files differnew file mode 100644 index 0000000000..f6f4ba780b --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-180 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-181 b/cmds/servicemanager/corpus/seed-2024-08-29-181 Binary files differnew file mode 100644 index 0000000000..2ca01e693d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-181 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-182 b/cmds/servicemanager/corpus/seed-2024-08-29-182 Binary files differnew file mode 100644 index 0000000000..18966c0ad7 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-182 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-183 b/cmds/servicemanager/corpus/seed-2024-08-29-183 Binary files differnew file mode 100644 index 0000000000..887de1013d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-183 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-184 b/cmds/servicemanager/corpus/seed-2024-08-29-184 Binary files differnew file mode 100644 index 0000000000..fee8cdb6a3 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-184 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-185 b/cmds/servicemanager/corpus/seed-2024-08-29-185 Binary files differnew file mode 100644 index 0000000000..10dd34d8eb --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-185 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-186 b/cmds/servicemanager/corpus/seed-2024-08-29-186 Binary files differnew file mode 100644 index 0000000000..6ad247bec0 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-186 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-187 b/cmds/servicemanager/corpus/seed-2024-08-29-187 Binary files differnew file mode 100644 index 0000000000..613456daef --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-187 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-188 b/cmds/servicemanager/corpus/seed-2024-08-29-188 Binary files differnew file mode 100644 index 0000000000..851b25fee7 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-188 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-189 b/cmds/servicemanager/corpus/seed-2024-08-29-189 Binary files differnew file mode 100644 index 0000000000..c4cebe91fd --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-189 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-19 b/cmds/servicemanager/corpus/seed-2024-08-29-19 Binary files differnew file mode 100644 index 0000000000..c0792c09c3 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-19 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-190 b/cmds/servicemanager/corpus/seed-2024-08-29-190 Binary files differnew file mode 100644 index 0000000000..4370a318b4 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-190 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-191 b/cmds/servicemanager/corpus/seed-2024-08-29-191 Binary files differnew file mode 100644 index 0000000000..09704284ee --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-191 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-192 b/cmds/servicemanager/corpus/seed-2024-08-29-192 Binary files differnew file mode 100644 index 0000000000..6cec4004da --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-192 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-193 b/cmds/servicemanager/corpus/seed-2024-08-29-193 Binary files differnew file mode 100644 index 0000000000..15a766167d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-193 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-194 b/cmds/servicemanager/corpus/seed-2024-08-29-194 Binary files differnew file mode 100644 index 0000000000..3cabe77e6d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-194 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-195 b/cmds/servicemanager/corpus/seed-2024-08-29-195 Binary files differnew file mode 100644 index 0000000000..4c5274bca9 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-195 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-196 b/cmds/servicemanager/corpus/seed-2024-08-29-196 Binary files differnew file mode 100644 index 0000000000..9d7a3d6a76 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-196 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-197 b/cmds/servicemanager/corpus/seed-2024-08-29-197 Binary files differnew file mode 100644 index 0000000000..4e69238bf3 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-197 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-198 b/cmds/servicemanager/corpus/seed-2024-08-29-198 Binary files differnew file mode 100644 index 0000000000..5f6df994be --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-198 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-199 b/cmds/servicemanager/corpus/seed-2024-08-29-199 Binary files differnew file mode 100644 index 0000000000..a902bba205 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-199 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-2 b/cmds/servicemanager/corpus/seed-2024-08-29-2 Binary files differnew file mode 100644 index 0000000000..ffa97196a9 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-2 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-20 b/cmds/servicemanager/corpus/seed-2024-08-29-20 Binary files differnew file mode 100644 index 0000000000..2090ef620a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-20 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-200 b/cmds/servicemanager/corpus/seed-2024-08-29-200 Binary files differnew file mode 100644 index 0000000000..2c91da6f3e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-200 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-201 b/cmds/servicemanager/corpus/seed-2024-08-29-201 Binary files differnew file mode 100644 index 0000000000..eb77655ce1 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-201 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-202 b/cmds/servicemanager/corpus/seed-2024-08-29-202 Binary files differnew file mode 100644 index 0000000000..bcbe3b7619 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-202 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-203 b/cmds/servicemanager/corpus/seed-2024-08-29-203 Binary files differnew file mode 100644 index 0000000000..7c3dc94e42 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-203 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-204 b/cmds/servicemanager/corpus/seed-2024-08-29-204 Binary files differnew file mode 100644 index 0000000000..a4b660e5f7 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-204 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-205 b/cmds/servicemanager/corpus/seed-2024-08-29-205 Binary files differnew file mode 100644 index 0000000000..aee1c21fae --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-205 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-206 b/cmds/servicemanager/corpus/seed-2024-08-29-206 Binary files differnew file mode 100644 index 0000000000..6863c2ef0a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-206 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-207 b/cmds/servicemanager/corpus/seed-2024-08-29-207 Binary files differnew file mode 100644 index 0000000000..bf2c59f084 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-207 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-208 b/cmds/servicemanager/corpus/seed-2024-08-29-208 Binary files differnew file mode 100644 index 0000000000..78081b969e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-208 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-209 b/cmds/servicemanager/corpus/seed-2024-08-29-209 Binary files differnew file mode 100644 index 0000000000..76df969df6 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-209 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-21 b/cmds/servicemanager/corpus/seed-2024-08-29-21 Binary files differnew file mode 100644 index 0000000000..510b9cf365 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-21 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-210 b/cmds/servicemanager/corpus/seed-2024-08-29-210 Binary files differnew file mode 100644 index 0000000000..b5174e0966 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-210 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-211 b/cmds/servicemanager/corpus/seed-2024-08-29-211 Binary files differnew file mode 100644 index 0000000000..51af471916 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-211 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-212 b/cmds/servicemanager/corpus/seed-2024-08-29-212 Binary files differnew file mode 100644 index 0000000000..f260df4564 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-212 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-213 b/cmds/servicemanager/corpus/seed-2024-08-29-213 Binary files differnew file mode 100644 index 0000000000..2d322b9584 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-213 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-214 b/cmds/servicemanager/corpus/seed-2024-08-29-214 Binary files differnew file mode 100644 index 0000000000..8df3af4716 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-214 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-215 b/cmds/servicemanager/corpus/seed-2024-08-29-215 Binary files differnew file mode 100644 index 0000000000..b82d03b171 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-215 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-216 b/cmds/servicemanager/corpus/seed-2024-08-29-216 Binary files differnew file mode 100644 index 0000000000..16f6d4d6ae --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-216 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-217 b/cmds/servicemanager/corpus/seed-2024-08-29-217 Binary files differnew file mode 100644 index 0000000000..d4c2bb36c6 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-217 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-218 b/cmds/servicemanager/corpus/seed-2024-08-29-218 Binary files differnew file mode 100644 index 0000000000..d0c1970146 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-218 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-219 b/cmds/servicemanager/corpus/seed-2024-08-29-219 Binary files differnew file mode 100644 index 0000000000..75edd86eef --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-219 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-22 b/cmds/servicemanager/corpus/seed-2024-08-29-22 Binary files differnew file mode 100644 index 0000000000..aa87441e0d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-22 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-220 b/cmds/servicemanager/corpus/seed-2024-08-29-220 Binary files differnew file mode 100644 index 0000000000..b3b6788f9d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-220 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-221 b/cmds/servicemanager/corpus/seed-2024-08-29-221 Binary files differnew file mode 100644 index 0000000000..429da0ef05 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-221 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-222 b/cmds/servicemanager/corpus/seed-2024-08-29-222 Binary files differnew file mode 100644 index 0000000000..be8e3f30fd --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-222 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-223 b/cmds/servicemanager/corpus/seed-2024-08-29-223 Binary files differnew file mode 100644 index 0000000000..a5a6d9c9da --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-223 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-224 b/cmds/servicemanager/corpus/seed-2024-08-29-224 Binary files differnew file mode 100644 index 0000000000..9a7d07e60c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-224 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-225 b/cmds/servicemanager/corpus/seed-2024-08-29-225 Binary files differnew file mode 100644 index 0000000000..39a5644d71 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-225 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-226 b/cmds/servicemanager/corpus/seed-2024-08-29-226 Binary files differnew file mode 100644 index 0000000000..c32f26ad65 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-226 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-227 b/cmds/servicemanager/corpus/seed-2024-08-29-227 Binary files differnew file mode 100644 index 0000000000..5af105be86 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-227 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-23 b/cmds/servicemanager/corpus/seed-2024-08-29-23 Binary files differnew file mode 100644 index 0000000000..4399c39593 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-23 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-24 b/cmds/servicemanager/corpus/seed-2024-08-29-24 Binary files differnew file mode 100644 index 0000000000..133c59a8ae --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-24 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-25 b/cmds/servicemanager/corpus/seed-2024-08-29-25 Binary files differnew file mode 100644 index 0000000000..ec1ac02447 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-25 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-26 b/cmds/servicemanager/corpus/seed-2024-08-29-26 Binary files differnew file mode 100644 index 0000000000..55397b96d5 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-26 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-27 b/cmds/servicemanager/corpus/seed-2024-08-29-27 Binary files differnew file mode 100644 index 0000000000..517af0b887 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-27 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-28 b/cmds/servicemanager/corpus/seed-2024-08-29-28 Binary files differnew file mode 100644 index 0000000000..0401668a65 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-28 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-29 b/cmds/servicemanager/corpus/seed-2024-08-29-29 Binary files differnew file mode 100644 index 0000000000..05ad4ecbb5 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-29 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-3 b/cmds/servicemanager/corpus/seed-2024-08-29-3 Binary files differnew file mode 100644 index 0000000000..14dcdd0c0d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-3 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-30 b/cmds/servicemanager/corpus/seed-2024-08-29-30 Binary files differnew file mode 100644 index 0000000000..d941024dab --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-30 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-31 b/cmds/servicemanager/corpus/seed-2024-08-29-31 Binary files differnew file mode 100644 index 0000000000..e93a19271e --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-31 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-32 b/cmds/servicemanager/corpus/seed-2024-08-29-32 Binary files differnew file mode 100644 index 0000000000..36f82dd1e8 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-32 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-33 b/cmds/servicemanager/corpus/seed-2024-08-29-33 Binary files differnew file mode 100644 index 0000000000..5f64227f41 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-33 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-34 b/cmds/servicemanager/corpus/seed-2024-08-29-34 Binary files differnew file mode 100644 index 0000000000..13f76344b4 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-34 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-35 b/cmds/servicemanager/corpus/seed-2024-08-29-35 Binary files differnew file mode 100644 index 0000000000..3a4476e55c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-35 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-36 b/cmds/servicemanager/corpus/seed-2024-08-29-36 Binary files differnew file mode 100644 index 0000000000..da9c20870f --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-36 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-37 b/cmds/servicemanager/corpus/seed-2024-08-29-37 Binary files differnew file mode 100644 index 0000000000..969a957c35 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-37 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-38 b/cmds/servicemanager/corpus/seed-2024-08-29-38 Binary files differnew file mode 100644 index 0000000000..ab6f1061ed --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-38 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-39 b/cmds/servicemanager/corpus/seed-2024-08-29-39 Binary files differnew file mode 100644 index 0000000000..248a549811 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-39 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-4 b/cmds/servicemanager/corpus/seed-2024-08-29-4 Binary files differnew file mode 100644 index 0000000000..0bd7cd5813 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-4 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-40 b/cmds/servicemanager/corpus/seed-2024-08-29-40 Binary files differnew file mode 100644 index 0000000000..7031a918fc --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-40 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-41 b/cmds/servicemanager/corpus/seed-2024-08-29-41 Binary files differnew file mode 100644 index 0000000000..8b8925c6e2 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-41 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-42 b/cmds/servicemanager/corpus/seed-2024-08-29-42 Binary files differnew file mode 100644 index 0000000000..c6e2167d12 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-42 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-43 b/cmds/servicemanager/corpus/seed-2024-08-29-43 Binary files differnew file mode 100644 index 0000000000..671a82172c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-43 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-44 b/cmds/servicemanager/corpus/seed-2024-08-29-44 Binary files differnew file mode 100644 index 0000000000..7c365b06c3 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-44 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-45 b/cmds/servicemanager/corpus/seed-2024-08-29-45 Binary files differnew file mode 100644 index 0000000000..a38d1382d8 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-45 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-46 b/cmds/servicemanager/corpus/seed-2024-08-29-46 Binary files differnew file mode 100644 index 0000000000..62acb777dc --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-46 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-47 b/cmds/servicemanager/corpus/seed-2024-08-29-47 Binary files differnew file mode 100644 index 0000000000..aea84c6ded --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-47 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-48 b/cmds/servicemanager/corpus/seed-2024-08-29-48 Binary files differnew file mode 100644 index 0000000000..a5bab7c63b --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-48 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-49 b/cmds/servicemanager/corpus/seed-2024-08-29-49 Binary files differnew file mode 100644 index 0000000000..4f19f09a02 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-49 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-5 b/cmds/servicemanager/corpus/seed-2024-08-29-5 Binary files differnew file mode 100644 index 0000000000..4e8a8534cf --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-5 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-50 b/cmds/servicemanager/corpus/seed-2024-08-29-50 Binary files differnew file mode 100644 index 0000000000..2f1d78b4ff --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-50 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-51 b/cmds/servicemanager/corpus/seed-2024-08-29-51 Binary files differnew file mode 100644 index 0000000000..7a44b4a5c7 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-51 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-52 b/cmds/servicemanager/corpus/seed-2024-08-29-52 Binary files differnew file mode 100644 index 0000000000..3da177b3df --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-52 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-53 b/cmds/servicemanager/corpus/seed-2024-08-29-53 Binary files differnew file mode 100644 index 0000000000..c67df718ba --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-53 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-54 b/cmds/servicemanager/corpus/seed-2024-08-29-54 Binary files differnew file mode 100644 index 0000000000..b1e8fec010 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-54 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-55 b/cmds/servicemanager/corpus/seed-2024-08-29-55 Binary files differnew file mode 100644 index 0000000000..20b268a3c1 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-55 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-56 b/cmds/servicemanager/corpus/seed-2024-08-29-56 Binary files differnew file mode 100644 index 0000000000..146192696c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-56 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-57 b/cmds/servicemanager/corpus/seed-2024-08-29-57 Binary files differnew file mode 100644 index 0000000000..fab8065815 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-57 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-58 b/cmds/servicemanager/corpus/seed-2024-08-29-58 Binary files differnew file mode 100644 index 0000000000..676f9e46d3 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-58 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-59 b/cmds/servicemanager/corpus/seed-2024-08-29-59 Binary files differnew file mode 100644 index 0000000000..a8e2c7220f --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-59 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-6 b/cmds/servicemanager/corpus/seed-2024-08-29-6 Binary files differnew file mode 100644 index 0000000000..585f1f0c04 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-6 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-60 b/cmds/servicemanager/corpus/seed-2024-08-29-60 Binary files differnew file mode 100644 index 0000000000..ef4b098b58 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-60 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-61 b/cmds/servicemanager/corpus/seed-2024-08-29-61 Binary files differnew file mode 100644 index 0000000000..5f45443fd4 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-61 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-62 b/cmds/servicemanager/corpus/seed-2024-08-29-62 Binary files differnew file mode 100644 index 0000000000..7ffd776948 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-62 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-63 b/cmds/servicemanager/corpus/seed-2024-08-29-63 Binary files differnew file mode 100644 index 0000000000..fa026cd452 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-63 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-64 b/cmds/servicemanager/corpus/seed-2024-08-29-64 Binary files differnew file mode 100644 index 0000000000..422c823386 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-64 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-65 b/cmds/servicemanager/corpus/seed-2024-08-29-65 Binary files differnew file mode 100644 index 0000000000..c811c44438 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-65 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-66 b/cmds/servicemanager/corpus/seed-2024-08-29-66 Binary files differnew file mode 100644 index 0000000000..8407da2006 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-66 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-67 b/cmds/servicemanager/corpus/seed-2024-08-29-67 Binary files differnew file mode 100644 index 0000000000..76dfdc3c95 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-67 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-68 b/cmds/servicemanager/corpus/seed-2024-08-29-68 Binary files differnew file mode 100644 index 0000000000..d93e0e3a5a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-68 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-69 b/cmds/servicemanager/corpus/seed-2024-08-29-69 Binary files differnew file mode 100644 index 0000000000..12b501b7da --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-69 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-7 b/cmds/servicemanager/corpus/seed-2024-08-29-7 Binary files differnew file mode 100644 index 0000000000..647836362d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-7 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-70 b/cmds/servicemanager/corpus/seed-2024-08-29-70 Binary files differnew file mode 100644 index 0000000000..e6206230cc --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-70 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-71 b/cmds/servicemanager/corpus/seed-2024-08-29-71 Binary files differnew file mode 100644 index 0000000000..dc32a5f830 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-71 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-72 b/cmds/servicemanager/corpus/seed-2024-08-29-72 Binary files differnew file mode 100644 index 0000000000..24217c62c6 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-72 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-73 b/cmds/servicemanager/corpus/seed-2024-08-29-73 Binary files differnew file mode 100644 index 0000000000..a9a0b2be58 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-73 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-74 b/cmds/servicemanager/corpus/seed-2024-08-29-74 Binary files differnew file mode 100644 index 0000000000..fd8a429232 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-74 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-75 b/cmds/servicemanager/corpus/seed-2024-08-29-75 Binary files differnew file mode 100644 index 0000000000..090b4894b0 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-75 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-76 b/cmds/servicemanager/corpus/seed-2024-08-29-76 Binary files differnew file mode 100644 index 0000000000..c92c45f97c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-76 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-77 b/cmds/servicemanager/corpus/seed-2024-08-29-77 Binary files differnew file mode 100644 index 0000000000..002a2336d0 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-77 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-78 b/cmds/servicemanager/corpus/seed-2024-08-29-78 Binary files differnew file mode 100644 index 0000000000..633f937341 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-78 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-79 b/cmds/servicemanager/corpus/seed-2024-08-29-79 Binary files differnew file mode 100644 index 0000000000..77782404bb --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-79 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-8 b/cmds/servicemanager/corpus/seed-2024-08-29-8 Binary files differnew file mode 100644 index 0000000000..580e20005a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-8 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-80 b/cmds/servicemanager/corpus/seed-2024-08-29-80 Binary files differnew file mode 100644 index 0000000000..90d74e44fd --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-80 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-81 b/cmds/servicemanager/corpus/seed-2024-08-29-81 Binary files differnew file mode 100644 index 0000000000..1fd76686f7 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-81 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-82 b/cmds/servicemanager/corpus/seed-2024-08-29-82 Binary files differnew file mode 100644 index 0000000000..d771501508 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-82 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-83 b/cmds/servicemanager/corpus/seed-2024-08-29-83 Binary files differnew file mode 100644 index 0000000000..6a4a1ca251 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-83 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-84 b/cmds/servicemanager/corpus/seed-2024-08-29-84 Binary files differnew file mode 100644 index 0000000000..bf8459b34c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-84 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-85 b/cmds/servicemanager/corpus/seed-2024-08-29-85 Binary files differnew file mode 100644 index 0000000000..8c88cacdc1 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-85 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-86 b/cmds/servicemanager/corpus/seed-2024-08-29-86 Binary files differnew file mode 100644 index 0000000000..62f676585a --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-86 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-87 b/cmds/servicemanager/corpus/seed-2024-08-29-87 Binary files differnew file mode 100644 index 0000000000..eb54dcbca2 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-87 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-88 b/cmds/servicemanager/corpus/seed-2024-08-29-88 Binary files differnew file mode 100644 index 0000000000..f38aaba211 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-88 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-89 b/cmds/servicemanager/corpus/seed-2024-08-29-89 Binary files differnew file mode 100644 index 0000000000..b4154aeeea --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-89 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-9 b/cmds/servicemanager/corpus/seed-2024-08-29-9 Binary files differnew file mode 100644 index 0000000000..5dca38a5a2 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-9 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-90 b/cmds/servicemanager/corpus/seed-2024-08-29-90 Binary files differnew file mode 100644 index 0000000000..2725a79022 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-90 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-91 b/cmds/servicemanager/corpus/seed-2024-08-29-91 Binary files differnew file mode 100644 index 0000000000..9140e28e10 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-91 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-92 b/cmds/servicemanager/corpus/seed-2024-08-29-92 Binary files differnew file mode 100644 index 0000000000..88dda1e85c --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-92 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-93 b/cmds/servicemanager/corpus/seed-2024-08-29-93 Binary files differnew file mode 100644 index 0000000000..6dd114e84d --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-93 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-94 b/cmds/servicemanager/corpus/seed-2024-08-29-94 Binary files differnew file mode 100644 index 0000000000..462c185d07 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-94 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-95 b/cmds/servicemanager/corpus/seed-2024-08-29-95 Binary files differnew file mode 100644 index 0000000000..4472deb9b1 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-95 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-96 b/cmds/servicemanager/corpus/seed-2024-08-29-96 Binary files differnew file mode 100644 index 0000000000..875efc5b2b --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-96 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-97 b/cmds/servicemanager/corpus/seed-2024-08-29-97 Binary files differnew file mode 100644 index 0000000000..3f0277e129 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-97 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-98 b/cmds/servicemanager/corpus/seed-2024-08-29-98 Binary files differnew file mode 100644 index 0000000000..2c66436172 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-98 diff --git a/cmds/servicemanager/corpus/seed-2024-08-29-99 b/cmds/servicemanager/corpus/seed-2024-08-29-99 Binary files differnew file mode 100644 index 0000000000..9a6ff1dff0 --- /dev/null +++ b/cmds/servicemanager/corpus/seed-2024-08-29-99 diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 82caccaf68..bf9acb37da 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -145,6 +145,9 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * Buffers which are replaced or removed from the scene in the transaction invoking * this callback may be reused after this point. * + * Starting with API level 36, prefer using \a ASurfaceTransaction_OnBufferRelease to listen + * to when a buffer is ready to be reused. + * * \param context Optional context provided by the client that is passed into * the callback. * @@ -157,8 +160,7 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * Available since API level 29. */ typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context, - ASurfaceTransactionStats* _Nonnull stats) - __INTRODUCED_IN(29); + ASurfaceTransactionStats* _Nonnull stats); /** * The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates @@ -186,8 +188,36 @@ typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context, * Available since API level 31. */ typedef void (*ASurfaceTransaction_OnCommit)(void* _Null_unspecified context, - ASurfaceTransactionStats* _Nonnull stats) - __INTRODUCED_IN(31); + ASurfaceTransactionStats* _Nonnull stats); + +/** + * The ASurfaceTransaction_OnBufferRelease callback is invoked when a buffer that was passed in + * ASurfaceTransaction_setBuffer is ready to be reused. + * + * This callback is guaranteed to be invoked if ASurfaceTransaction_setBuffer is called with a non + * null buffer. If the buffer in the transaction is replaced via another call to + * ASurfaceTransaction_setBuffer, the callback will be invoked immediately. Otherwise the callback + * will be invoked before the ASurfaceTransaction_OnComplete callback after the buffer was + * presented. + * + * If this callback is set, caller should not release the buffer using the + * ASurfaceTransaction_OnComplete. + * + * \param context Optional context provided by the client that is passed into the callback. + * + * \param release_fence_fd Returns the fence file descriptor used to signal the release of buffer + * associated with this callback. If this fence is valid (>=0), the buffer has not yet been released + * and the fence will signal when the buffer has been released. If the fence is -1 , the buffer is + * already released. The recipient of the callback takes ownership of the fence fd and is + * responsible for closing it. + * + * THREADING + * The callback can be invoked on any thread. + * + * Available since API level 36. + */ +typedef void (*ASurfaceTransaction_OnBufferRelease)(void* _Null_unspecified context, + int release_fence_fd); /** * Returns the timestamp of when the frame was latched by the framework. Once a frame is @@ -251,7 +281,7 @@ int64_t ASurfaceTransactionStats_getAcquireTime( /** * The returns the fence used to signal the release of the PREVIOUS buffer set on * this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the - * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS + * fence will signal when the PREVIOUS buffer has been released. If the fence is -1, the PREVIOUS * buffer is already released. The recipient of the callback takes ownership of the * previousReleaseFenceFd and is responsible for closing it. * @@ -353,6 +383,9 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* _Nonnull transaction, * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE * as the surface control might be composited using the GPU. * + * Starting with API level 36, prefer using \a ASurfaceTransaction_setBufferWithRelease to + * set a buffer and a callback which will be invoked when the buffer is ready to be reused. + * * Available since API level 29. */ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction, @@ -361,6 +394,29 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction, __INTRODUCED_IN(29); /** + * Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the + * acquire_fence_fd should be a file descriptor that is signaled when all pending work + * for the buffer is complete and the buffer can be safely read. + * + * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible + * for closing it. + * + * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE + * as the surface control might be composited using the GPU. + * + * When the buffer is ready to be reused, the ASurfaceTransaction_OnBufferRelease + * callback will be invoked. If the buffer is null, the callback will not be invoked. + * + * Available since API level 36. + */ +void ASurfaceTransaction_setBufferWithRelease(ASurfaceTransaction* _Nonnull transaction, + ASurfaceControl* _Nonnull surface_control, + AHardwareBuffer* _Nonnull buffer, + int acquire_fence_fd, void* _Null_unspecified context, + ASurfaceTransaction_OnBufferRelease _Nonnull func) + __INTRODUCED_IN(36); + +/** * Updates the color for \a surface_control. This will make the background color for the * ASurfaceControl visible in transparent regions of the surface. Colors \a r, \a g, * and \a b must be within the range that is valid for \a dataspace. \a dataspace and \a alpha diff --git a/include/input/Input.h b/include/input/Input.h index 1a3cb6a884..a8684bd19b 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -251,6 +251,8 @@ enum class InputEventType { TOUCH_MODE = AINPUT_EVENT_TYPE_TOUCH_MODE, ftl_first = KEY, ftl_last = TOUCH_MODE, + // Used by LatencyTracker fuzzer + kMaxValue = ftl_last }; std::string inputEventSourceToString(int32_t source); diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index 65c2914b3c..c98b9cf8c1 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -16,6 +16,11 @@ #pragma once +#include <map> +#include <memory> +#include <optional> + +#include <input/Input.h> #include <input/InputTransport.h> #include <input/Resampler.h> #include <utils/Looper.h> @@ -35,7 +40,7 @@ public: /** * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents". * If you don't want batching, then call "consumeBatchedInputEvents" immediately with - * std::nullopt frameTime to receive the pending motion event(s). + * std::nullopt requestedFrameTime to receive the pending motion event(s). * @param pendingBatchSource the source of the pending batch. */ virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0; @@ -85,15 +90,17 @@ public: void finishInputEvent(uint32_t seq, bool handled); void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime); /** - * If you want to consume all events immediately (disable batching), the you still must call - * this. For frameTime, use a std::nullopt. - * @param frameTime the time up to which consume the events. When there's double (or triple) - * buffering, you may want to not consume all events currently available, because you could be - * still working on an older frame, but there could already have been events that arrived that - * are more recent. + * If you want to consume all events immediately (disable batching), then you still must call + * this. For requestedFrameTime, use a std::nullopt. It is not guaranteed that the consumption + * will occur at requestedFrameTime. The resampling strategy may modify it. + * @param requestedFrameTime the time up to which consume the events. When there's double (or + * triple) buffering, you may want to not consume all events currently available, because you + * could be still working on an older frame, but there could already have been events that + * arrived that are more recent. * @return whether any events were actually consumed */ - bool consumeBatchedInputEvents(std::optional<nsecs_t> frameTime); + bool consumeBatchedInputEvents(std::optional<nsecs_t> requestedFrameTime); + /** * Returns true when there is *likely* a pending batch or a pending event in the channel. * @@ -189,20 +196,33 @@ private: /** * Batched InputMessages, per deviceId. * For each device, we are storing a queue of batched messages. These will all be collapsed into - * a single MotionEvent (up to a specific frameTime) when the consumer calls + * a single MotionEvent (up to a specific requestedFrameTime) when the consumer calls * `consumeBatchedInputEvents`. */ std::map<DeviceId, std::queue<InputMessage>> mBatches; /** * Creates a MotionEvent by consuming samples from the provided queue. If one message has - * eventTime > frameTime, all subsequent messages in the queue will be skipped. It is assumed - * that messages are queued in chronological order. In other words, only events that occurred - * prior to the requested frameTime will be consumed. - * @param frameTime the time up to which to consume events + * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is + * assumed that messages are queued in chronological order. In other words, only events that + * occurred prior to the adjustedFrameTime will be consumed. + * @param requestedFrameTime the time up to which to consume events. * @param messages the queue of messages to consume from */ std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent( - const nsecs_t frameTime, std::queue<InputMessage>& messages); + const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages); + + /** + * Consumes the batched input events, optionally allowing the caller to specify a device id + * and/or requestedFrameTime threshold. It is not guaranteed that consumption will occur at + * requestedFrameTime. + * @param deviceId The device id from which to consume events. If std::nullopt, consumes events + * from any device id. + * @param requestedFrameTime The time up to which consume the events. If std::nullopt, consumes + * input events with any timestamp. + * @return Whether or not any events were consumed. + */ + bool consumeBatchedInputEvents(std::optional<DeviceId> deviceId, + std::optional<nsecs_t> requestedFrameTime); /** * A map from a single sequence number to several sequence numbers. This is needed because of * batching. When batching is enabled, a single MotionEvent will contain several samples. Each diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 7d8c19e702..1a482396ee 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -389,6 +389,7 @@ enum class InputDeviceConfigurationFileType : int32_t { CONFIGURATION = 0, /* .idc file */ KEY_LAYOUT = 1, /* .kl file */ KEY_CHARACTER_MAP = 2, /* .kcm file */ + ftl_last = KEY_CHARACTER_MAP, }; /* diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 7d11f76c85..0cd87201fb 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -263,7 +263,7 @@ public: * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t sendMessage(const InputMessage* msg); + virtual status_t sendMessage(const InputMessage* msg); /* Receive a message sent by the other endpoint. * @@ -275,14 +275,14 @@ public: * Return DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - android::base::Result<InputMessage> receiveMessage(); + virtual android::base::Result<InputMessage> receiveMessage(); /* Tells whether there is a message in the channel available to be received. * * This is only a performance hint and may return false negative results. Clients should not * rely on availability of the message based on the return value. */ - bool probablyHasInput() const; + virtual bool probablyHasInput() const; /* Wait until there is a message in the channel. * @@ -323,11 +323,12 @@ public: */ sp<IBinder> getConnectionToken() const; +protected: + InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); + private: static std::unique_ptr<InputChannel> create(const std::string& name, android::base::unique_fd fd, sp<IBinder> token); - - InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); }; /* diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 92d5ec4d4e..67b37b1213 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -126,9 +126,9 @@ public: bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, Vector<KeyEvent>& outEvents) const; - /* Maps an Android key code to another Android key code. This mapping is applied after scanCode - * and usageCodes are mapped to corresponding Android Keycode */ - void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); + /* Maps some Android key code to another Android key code. This mapping is applied after + * scanCode and usageCodes are mapped to corresponding Android Keycode */ + void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping); /* Maps a scan code and usage code to a key code, in case this key map overrides * the mapping in some way. */ diff --git a/include/input/Resampler.h b/include/input/Resampler.h index 2892137ae7..dcb25b729f 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -35,9 +35,9 @@ struct Resampler { virtual ~Resampler() = default; /** - * Tries to resample motionEvent at resampleTime. The provided resampleTime must be greater than + * Tries to resample motionEvent at frameTime. The provided frameTime must be greater than * the latest sample time of motionEvent. It is not guaranteed that resampling occurs at - * resampleTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent + * frameTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent * may be resampled by another method, or not resampled at all. Furthermore, it is the * implementer's responsibility to guarantee the following: * - If resampling occurs, a single additional sample should be added to motionEvent. That is, @@ -45,15 +45,21 @@ struct Resampler { * samples by the end of the resampling. No other field of motionEvent should be modified. * - If resampling does not occur, then motionEvent must not be modified in any way. */ - virtual void resampleMotionEvent(std::chrono::nanoseconds resampleTime, - MotionEvent& motionEvent, + virtual void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent, const InputMessage* futureSample) = 0; + + /** + * Returns resample latency. Resample latency is the time difference between frame time and + * resample time. More precisely, let frameTime and resampleTime be two timestamps, and + * frameTime > resampleTime. Resample latency is defined as frameTime - resampleTime. + */ + virtual std::chrono::nanoseconds getResampleLatency() const = 0; }; class LegacyResampler final : public Resampler { public: /** - * Tries to resample `motionEvent` at `resampleTime` by adding a resampled sample at the end of + * Tries to resample `motionEvent` at `frameTime` by adding a resampled sample at the end of * `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by * linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is @@ -61,9 +67,11 @@ public: * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and * `motionEvent` is unmodified. */ - void resampleMotionEvent(std::chrono::nanoseconds resampleTime, MotionEvent& motionEvent, + void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent, const InputMessage* futureSample) override; + std::chrono::nanoseconds getResampleLatency() const override; + private: struct Pointer { PointerProperties properties; diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h index 8c356d0140..e5eee340ca 100644 --- a/include/private/performance_hint_private.h +++ b/include/private/performance_hint_private.h @@ -108,6 +108,10 @@ APerformanceHintSession* APerformanceHint_createSessionInternal(APerformanceHint const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos, SessionTag tag); +/** + * Forces FMQ to be enabled or disabled, for testing only. + */ +void APerformanceHint_setUseFMQForTesting(bool enabled); __END_DECLS diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp index 78896ed553..d31cb3dff0 100644 --- a/libs/adbd_auth/adbd_auth.cpp +++ b/libs/adbd_auth/adbd_auth.cpp @@ -390,13 +390,16 @@ public: } } - static constexpr const char* key_paths[] = {"/adb_keys", "/data/misc/adb/adb_keys"}; + static constexpr std::pair<const char*, bool> key_paths[] = { + {"/adb_keys", true /* follow symlinks */ }, + {"/data/misc/adb/adb_keys", false /* don't follow symlinks */ }, + }; void IteratePublicKeys(bool (*callback)(void*, const char*, size_t), void* opaque) { - for (const auto& path : key_paths) { + for (const auto& [path, follow_symlinks] : key_paths) { if (access(path, R_OK) == 0) { LOG(INFO) << "adbd_auth: loading keys from " << path; std::string content; - if (!android::base::ReadFileToString(path, &content)) { + if (!android::base::ReadFileToString(path, &content, follow_symlinks)) { PLOG(ERROR) << "adbd_auth: couldn't read " << path; continue; } diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 319716ec81..cbba7118b6 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -40,6 +40,7 @@ ndk_headers { cc_library_headers { name: "libarect_headers", + host_supported: true, vendor_available: true, min_sdk_version: "29", // TODO(b/153609531): remove when no longer needed. diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp index 54f687b280..52b485a6f6 100644 --- a/libs/binder/BackendUnifiedServiceManager.cpp +++ b/libs/binder/BackendUnifiedServiceManager.cpp @@ -24,11 +24,124 @@ namespace android { +#ifdef LIBBINDER_CLIENT_CACHE +constexpr bool kUseCache = true; +#else +constexpr bool kUseCache = false; +#endif + using AidlServiceManager = android::os::IServiceManager; using IAccessor = android::os::IAccessor; +static const char* kStaticCachableList[] = { + // go/keep-sorted start + "accessibility", + "account", + "activity", + "alarm", + "android.system.keystore2.IKeystoreService/default", + "appops", + "audio", + "batterystats", + "carrier_config", + "connectivity", + "content", + "content_capture", + "device_policy", + "display", + "dropbox", + "econtroller", + "graphicsstats", + "input", + "input_method", + "isub", + "jobscheduler", + "legacy_permission", + "location", + "media.extractor", + "media.metrics", + "media.player", + "media.resource_manager", + "media_resource_monitor", + "mount", + "netd_listener", + "netstats", + "network_management", + "nfc", + "notification", + "package", + "package_native", + "performance_hint", + "permission", + "permission_checker", + "permissionmgr", + "phone", + "platform_compat", + "power", + "role", + "sensorservice", + "statscompanion", + "telephony.registry", + "thermalservice", + "time_detector", + "trust", + "uimode", + "user", + "virtualdevice", + "virtualdevice_native", + "webviewupdate", + "window", + // go/keep-sorted end +}; + +bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) { + if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() <= 0) { + ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be " + "implemented. serviceName: %s", + serviceName.c_str()); + return false; + } + for (const char* name : kStaticCachableList) { + if (name == serviceName) { + return true; + } + } + return false; +} + +binder::Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName, + const os::Service& service) { + if (!kUseCache) { + return binder::Status::ok(); + } + if (service.getTag() == os::Service::Tag::binder) { + sp<IBinder> binder = service.get<os::Service::Tag::binder>(); + if (binder && mCacheForGetService->isClientSideCachingEnabled(serviceName) && + binder->isBinderAlive()) { + return mCacheForGetService->setItem(serviceName, binder); + } + } + return binder::Status::ok(); +} + +bool BackendUnifiedServiceManager::returnIfCached(const std::string& serviceName, + os::Service* _out) { + if (!kUseCache) { + return false; + } + sp<IBinder> item = mCacheForGetService->getItem(serviceName); + // TODO(b/363177618): Enable caching for binders which are always null. + if (item != nullptr && item->isBinderAlive()) { + *_out = os::Service::make<os::Service::Tag::binder>(item); + return true; + } + return false; +} + BackendUnifiedServiceManager::BackendUnifiedServiceManager(const sp<AidlServiceManager>& impl) - : mTheRealServiceManager(impl) {} + : mTheRealServiceManager(impl) { + mCacheForGetService = std::make_shared<BinderCacheWithInvalidation>(); +} sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() { return mTheRealServiceManager; @@ -44,25 +157,64 @@ binder::Status BackendUnifiedServiceManager::getService(const ::std::string& nam binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& name, os::Service* _out) { + if (returnIfCached(name, _out)) { + return binder::Status::ok(); + } os::Service service; binder::Status status = mTheRealServiceManager->getService2(name, &service); - toBinderService(service, _out); + + if (status.isOk()) { + status = toBinderService(name, service, _out); + if (status.isOk()) { + return updateCache(name, service); + } + } return status; } binder::Status BackendUnifiedServiceManager::checkService(const ::std::string& name, os::Service* _out) { os::Service service; + if (returnIfCached(name, _out)) { + return binder::Status::ok(); + } + binder::Status status = mTheRealServiceManager->checkService(name, &service); - toBinderService(service, _out); + if (status.isOk()) { + status = toBinderService(name, service, _out); + if (status.isOk()) { + return updateCache(name, service); + } + } return status; } -void BackendUnifiedServiceManager::toBinderService(const os::Service& in, os::Service* _out) { +binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name, + const os::Service& in, + os::Service* _out) { switch (in.getTag()) { case os::Service::Tag::binder: { + if (in.get<os::Service::Tag::binder>() == nullptr) { + // failed to find a service. Check to see if we have any local + // injected Accessors for this service. + os::Service accessor; + binder::Status status = getInjectedAccessor(name, &accessor); + if (!status.isOk()) { + *_out = os::Service::make<os::Service::Tag::binder>(nullptr); + return status; + } + if (accessor.getTag() == os::Service::Tag::accessor && + accessor.get<os::Service::Tag::accessor>() != nullptr) { + ALOGI("Found local injected service for %s, will attempt to create connection", + name.c_str()); + // Call this again using the accessor Service to get the real + // service's binder into _out + return toBinderService(name, accessor, _out); + } + } + *_out = in; - break; + return binder::Status::ok(); } case os::Service::Tag::accessor: { sp<IBinder> accessorBinder = in.get<os::Service::Tag::accessor>(); @@ -70,7 +222,7 @@ void BackendUnifiedServiceManager::toBinderService(const os::Service& in, os::Se if (accessor == nullptr) { ALOGE("Service#accessor doesn't have accessor. VM is maybe starting..."); *_out = os::Service::make<os::Service::Tag::binder>(nullptr); - break; + return binder::Status::ok(); } auto request = [=] { os::ParcelFileDescriptor fd; @@ -83,10 +235,15 @@ void BackendUnifiedServiceManager::toBinderService(const os::Service& in, os::Se } }; auto session = RpcSession::make(); - session->setupPreconnectedClient(base::unique_fd{}, request); + status_t status = session->setupPreconnectedClient(base::unique_fd{}, request); + if (status != OK) { + ALOGE("Failed to set up preconnected binder RPC client: %s", + statusToString(status).c_str()); + return binder::Status::fromStatusT(status); + } session->setSessionSpecificRoot(accessorBinder); *_out = os::Service::make<os::Service::Tag::binder>(session->getRootObject()); - break; + return binder::Status::ok(); } default: { LOG_ALWAYS_FATAL("Unknown service type: %d", in.getTag()); @@ -177,4 +334,4 @@ sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager() { return gUnifiedServiceManager; } -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h index 8f3839fcb2..47b2ec9f2c 100644 --- a/libs/binder/BackendUnifiedServiceManager.h +++ b/libs/binder/BackendUnifiedServiceManager.h @@ -18,9 +18,87 @@ #include <android/os/BnServiceManager.h> #include <android/os/IServiceManager.h> #include <binder/IPCThreadState.h> +#include <map> +#include <memory> namespace android { +class BinderCacheWithInvalidation + : public std::enable_shared_from_this<BinderCacheWithInvalidation> { + class BinderInvalidation : public IBinder::DeathRecipient { + public: + BinderInvalidation(std::weak_ptr<BinderCacheWithInvalidation> cache, const std::string& key) + : mCache(cache), mKey(key) {} + + void binderDied(const wp<IBinder>& who) override { + sp<IBinder> binder = who.promote(); + if (std::shared_ptr<BinderCacheWithInvalidation> cache = mCache.lock()) { + cache->removeItem(mKey, binder); + } else { + ALOGI("Binder Cache pointer expired: %s", mKey.c_str()); + } + } + + private: + std::weak_ptr<BinderCacheWithInvalidation> mCache; + std::string mKey; + }; + struct Entry { + sp<IBinder> service; + sp<BinderInvalidation> deathRecipient; + }; + +public: + sp<IBinder> getItem(const std::string& key) const { + std::lock_guard<std::mutex> lock(mCacheMutex); + + if (auto it = mCache.find(key); it != mCache.end()) { + return it->second.service; + } + return nullptr; + } + + bool removeItem(const std::string& key, const sp<IBinder>& who) { + std::lock_guard<std::mutex> lock(mCacheMutex); + if (auto it = mCache.find(key); it != mCache.end()) { + if (it->second.service == who) { + status_t result = who->unlinkToDeath(it->second.deathRecipient); + if (result != DEAD_OBJECT) { + ALOGW("Unlinking to dead binder resulted in: %d", result); + } + mCache.erase(key); + return true; + } + } + return false; + } + + binder::Status setItem(const std::string& key, const sp<IBinder>& item) { + sp<BinderInvalidation> deathRecipient = + sp<BinderInvalidation>::make(shared_from_this(), key); + + // linkToDeath if binder is a remote binder. + if (item->localBinder() == nullptr) { + status_t status = item->linkToDeath(deathRecipient); + if (status != android::OK) { + ALOGE("Failed to linkToDeath binder for service %s. Error: %d", key.c_str(), + status); + return binder::Status::fromStatusT(status); + } + } + std::lock_guard<std::mutex> lock(mCacheMutex); + Entry entry = {.service = item, .deathRecipient = deathRecipient}; + mCache[key] = entry; + return binder::Status::ok(); + } + + bool isClientSideCachingEnabled(const std::string& serviceName); + +private: + std::map<std::string, Entry> mCache; + mutable std::mutex mCacheMutex; +}; + class BackendUnifiedServiceManager : public android::os::BnServiceManager { public: explicit BackendUnifiedServiceManager(const sp<os::IServiceManager>& impl); @@ -58,10 +136,16 @@ public: } private: + std::shared_ptr<BinderCacheWithInvalidation> mCacheForGetService; sp<os::IServiceManager> mTheRealServiceManager; - void toBinderService(const os::Service& in, os::Service* _out); + binder::Status toBinderService(const ::std::string& name, const os::Service& in, + os::Service* _out); + binder::Status updateCache(const std::string& serviceName, const os::Service& service); + bool returnIfCached(const std::string& serviceName, os::Service* _out); }; sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager(); -} // namespace android
\ No newline at end of file +android::binder::Status getInjectedAccessor(const std::string& name, android::os::Service* service); + +} // namespace android diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp index 455a4338e5..7263e236fc 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -82,7 +82,9 @@ status_t FdTrigger::triggerablePoll(const android::RpcTransportFd& transportFd, int ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1)); if (ret < 0) { - return -errno; + int saved_errno = errno; + ALOGE("FdTrigger poll returned error: %d, with error: %s", ret, strerror(saved_errno)); + return -saved_errno; } LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", transportFd.fd.get()); @@ -106,6 +108,7 @@ status_t FdTrigger::triggerablePoll(const android::RpcTransportFd& transportFd, // POLLNVAL: invalid FD number, e.g. not opened. if (pfd[0].revents & POLLNVAL) { + LOG_ALWAYS_FATAL("Invalid FD number (%d) in FdTrigger (POLLNVAL)", pfd[0].fd); return BAD_VALUE; } diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 1d26d8543d..6698d0c0cd 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -232,6 +232,15 @@ static const void* printReturnCommand(std::ostream& out, const void* _cmd) { return cmd; } +static void printReturnCommandParcel(std::ostream& out, const Parcel& parcel) { + const void* cmds = parcel.data(); + out << "\t" << HexDump(cmds, parcel.dataSize()) << "\n"; + IF_LOG_COMMANDS() { + const void* end = parcel.data() + parcel.dataSize(); + while (cmds < end) cmds = printReturnCommand(out, cmds); + } +} + static const void* printCommand(std::ostream& out, const void* _cmd) { static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); const int32_t* cmd = (const int32_t*)_cmd; @@ -1235,13 +1244,15 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) if (err >= NO_ERROR) { if (bwr.write_consumed > 0) { - if (bwr.write_consumed < mOut.dataSize()) + if (bwr.write_consumed < mOut.dataSize()) { + std::ostringstream logStream; + printReturnCommandParcel(logStream, mIn); LOG_ALWAYS_FATAL("Driver did not consume write buffer. " - "err: %s consumed: %zu of %zu", - statusToString(err).c_str(), - (size_t)bwr.write_consumed, - mOut.dataSize()); - else { + "err: %s consumed: %zu of %zu.\n" + "Return command: %s", + statusToString(err).c_str(), (size_t)bwr.write_consumed, + mOut.dataSize(), logStream.str().c_str()); + } else { mOut.setDataSize(0); processPostWriteDerefs(); } @@ -1252,14 +1263,8 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) } IF_LOG_COMMANDS() { std::ostringstream logStream; - logStream << "Remaining data size: " << mOut.dataSize() << "\n"; - logStream << "Received commands from driver: "; - const void* cmds = mIn.data(); - const void* end = mIn.data() + mIn.dataSize(); - logStream << "\t" << HexDump(cmds, mIn.dataSize()) << "\n"; - while (cmds < end) cmds = printReturnCommand(logStream, cmds); - std::string message = logStream.str(); - ALOGI("%s", message.c_str()); + printReturnCommandParcel(logStream, mIn); + ALOGI("%s", logStream.str().c_str()); } return NO_ERROR; } diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index c55dd9de1b..77b80ef3de 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ +#include <sys/socket.h> #define LOG_TAG "ServiceManagerCppClient" #include <binder/IServiceManager.h> +#include <binder/IServiceManagerUnitTestHelper.h> #include "BackendUnifiedServiceManager.h" #include <inttypes.h> @@ -24,14 +26,19 @@ #include <chrono> #include <condition_variable> +#include <FdTrigger.h> +#include <RpcSocketAddress.h> #include <android-base/properties.h> +#include <android/os/BnAccessor.h> #include <android/os/BnServiceCallback.h> +#include <android/os/BnServiceManager.h> #include <android/os/IAccessor.h> #include <android/os/IServiceManager.h> #include <binder/IPCThreadState.h> #include <binder/Parcel.h> +#include <binder/RpcSession.h> #include <utils/String8.h> - +#include <variant> #ifndef __ANDROID_VNDK__ #include <binder/IPermissionController.h> #endif @@ -148,8 +155,151 @@ protected: } }; +class AccessorProvider { +public: + AccessorProvider(std::set<std::string>&& instances, RpcAccessorProvider&& provider) + : mInstances(std::move(instances)), mProvider(std::move(provider)) {} + sp<IBinder> provide(const String16& name) { + if (mInstances.count(String8(name).c_str()) > 0) { + return mProvider(name); + } else { + return nullptr; + } + } + const std::set<std::string>& instances() { return mInstances; } + +private: + AccessorProvider() = delete; + + std::set<std::string> mInstances; + RpcAccessorProvider mProvider; +}; + +class AccessorProviderEntry { +public: + AccessorProviderEntry(std::shared_ptr<AccessorProvider>&& provider) + : mProvider(std::move(provider)) {} + std::shared_ptr<AccessorProvider> mProvider; + +private: + AccessorProviderEntry() = delete; +}; + [[clang::no_destroy]] static std::once_flag gSmOnce; [[clang::no_destroy]] static sp<IServiceManager> gDefaultServiceManager; +[[clang::no_destroy]] static std::mutex gAccessorProvidersMutex; +[[clang::no_destroy]] static std::vector<AccessorProviderEntry> gAccessorProviders; + +class LocalAccessor : public android::os::BnAccessor { +public: + LocalAccessor(const String16& instance, RpcSocketAddressProvider&& connectionInfoProvider) + : mInstance(instance), mConnectionInfoProvider(std::move(connectionInfoProvider)) { + LOG_ALWAYS_FATAL_IF(!mConnectionInfoProvider, + "LocalAccessor object needs a valid connection info provider"); + } + + ~LocalAccessor() { + if (mOnDelete) mOnDelete(); + } + + ::android::binder::Status addConnection(::android::os::ParcelFileDescriptor* outFd) { + using android::os::IAccessor; + sockaddr_storage addrStorage; + std::unique_ptr<FdTrigger> trigger = FdTrigger::make(); + RpcTransportFd fd; + status_t status = + mConnectionInfoProvider(mInstance, reinterpret_cast<sockaddr*>(&addrStorage), + sizeof(addrStorage)); + if (status != OK) { + const std::string error = "The connection info provider was unable to provide " + "connection info for instance " + + std::string(String8(mInstance).c_str()) + + " with status: " + statusToString(status); + ALOGE("%s", error.c_str()); + return Status::fromServiceSpecificError(IAccessor::ERROR_CONNECTION_INFO_NOT_FOUND, + error.c_str()); + } + if (addrStorage.ss_family == AF_VSOCK) { + sockaddr_vm* addr = reinterpret_cast<sockaddr_vm*>(&addrStorage); + status = singleSocketConnection(VsockSocketAddress(addr->svm_cid, addr->svm_port), + trigger, &fd); + } else if (addrStorage.ss_family == AF_UNIX) { + sockaddr_un* addr = reinterpret_cast<sockaddr_un*>(&addrStorage); + status = singleSocketConnection(UnixSocketAddress(addr->sun_path), trigger, &fd); + } else if (addrStorage.ss_family == AF_INET) { + sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(&addrStorage); + status = singleSocketConnection(InetSocketAddress(reinterpret_cast<sockaddr*>(addr), + sizeof(sockaddr_in), + inet_ntoa(addr->sin_addr), + ntohs(addr->sin_port)), + trigger, &fd); + } else { + const std::string error = + "Unsupported socket family type or the ConnectionInfoProvider failed to find a " + "valid address. Family type: " + + std::to_string(addrStorage.ss_family); + ALOGE("%s", error.c_str()); + return Status::fromServiceSpecificError(IAccessor::ERROR_UNSUPPORTED_SOCKET_FAMILY, + error.c_str()); + } + if (status != OK) { + const std::string error = "Failed to connect to socket for " + + std::string(String8(mInstance).c_str()) + + " with status: " + statusToString(status); + ALOGE("%s", error.c_str()); + int err = 0; + if (status == -EACCES) { + err = IAccessor::ERROR_FAILED_TO_CONNECT_EACCES; + } else { + err = IAccessor::ERROR_FAILED_TO_CONNECT_TO_SOCKET; + } + return Status::fromServiceSpecificError(err, error.c_str()); + } + *outFd = os::ParcelFileDescriptor(std::move(fd.fd)); + return Status::ok(); + } + + ::android::binder::Status getInstanceName(String16* instance) { + *instance = mInstance; + return Status::ok(); + } + +private: + LocalAccessor() = delete; + String16 mInstance; + RpcSocketAddressProvider mConnectionInfoProvider; + std::function<void()> mOnDelete; +}; + +android::binder::Status getInjectedAccessor(const std::string& name, + android::os::Service* service) { + std::vector<AccessorProviderEntry> copiedProviders; + { + std::lock_guard<std::mutex> lock(gAccessorProvidersMutex); + copiedProviders.insert(copiedProviders.begin(), gAccessorProviders.begin(), + gAccessorProviders.end()); + } + + // Unlocked to call the providers. This requires the providers to be + // threadsafe and not contain any references to objects that could be + // deleted. + for (const auto& provider : copiedProviders) { + sp<IBinder> binder = provider.mProvider->provide(String16(name.c_str())); + if (binder == nullptr) continue; + status_t status = validateAccessor(String16(name.c_str()), binder); + if (status != OK) { + ALOGE("A provider returned a binder that is not an IAccessor for instance %s. Status: " + "%s", + name.c_str(), statusToString(status).c_str()); + return android::binder::Status::fromStatusT(android::INVALID_OPERATION); + } + *service = os::Service::make<os::Service::Tag::accessor>(binder); + return android::binder::Status::ok(); + } + + *service = os::Service::make<os::Service::Tag::accessor>(nullptr); + return android::binder::Status::ok(); +} sp<IServiceManager> defaultServiceManager() { @@ -172,6 +322,104 @@ void setDefaultServiceManager(const sp<IServiceManager>& sm) { } } +sp<IServiceManager> getServiceManagerShimFromAidlServiceManagerForTests( + const sp<AidlServiceManager>& sm) { + return sp<CppBackendShim>::make(sp<BackendUnifiedServiceManager>::make(sm)); +} + +// gAccessorProvidersMutex must be locked already +static bool isInstanceProvidedLocked(const std::string& instance) { + return gAccessorProviders.end() != + std::find_if(gAccessorProviders.begin(), gAccessorProviders.end(), + [&instance](const AccessorProviderEntry& entry) { + return entry.mProvider->instances().count(instance) > 0; + }); +} + +std::weak_ptr<AccessorProvider> addAccessorProvider(std::set<std::string>&& instances, + RpcAccessorProvider&& providerCallback) { + if (instances.empty()) { + ALOGE("Set of instances is empty! Need a non empty set of instances to provide for."); + return std::weak_ptr<AccessorProvider>(); + } + std::lock_guard<std::mutex> lock(gAccessorProvidersMutex); + for (const auto& instance : instances) { + if (isInstanceProvidedLocked(instance)) { + ALOGE("The instance %s is already provided for by a previously added " + "RpcAccessorProvider.", + instance.c_str()); + return std::weak_ptr<AccessorProvider>(); + } + } + std::shared_ptr<AccessorProvider> provider = + std::make_shared<AccessorProvider>(std::move(instances), std::move(providerCallback)); + std::weak_ptr<AccessorProvider> receipt = provider; + gAccessorProviders.push_back(AccessorProviderEntry(std::move(provider))); + + return receipt; +} + +status_t removeAccessorProvider(std::weak_ptr<AccessorProvider> wProvider) { + std::shared_ptr<AccessorProvider> provider = wProvider.lock(); + if (provider == nullptr) { + ALOGE("The provider supplied to removeAccessorProvider has already been removed or the " + "argument to this function was nullptr."); + return BAD_VALUE; + } + std::lock_guard<std::mutex> lock(gAccessorProvidersMutex); + size_t sizeBefore = gAccessorProviders.size(); + gAccessorProviders.erase(std::remove_if(gAccessorProviders.begin(), gAccessorProviders.end(), + [&](AccessorProviderEntry entry) { + return entry.mProvider == provider; + }), + gAccessorProviders.end()); + if (sizeBefore == gAccessorProviders.size()) { + ALOGE("Failed to find an AccessorProvider for removeAccessorProvider"); + return NAME_NOT_FOUND; + } + + return OK; +} + +status_t validateAccessor(const String16& instance, const sp<IBinder>& binder) { + if (binder == nullptr) { + ALOGE("Binder is null"); + return BAD_VALUE; + } + sp<IAccessor> accessor = interface_cast<IAccessor>(binder); + if (accessor == nullptr) { + ALOGE("This binder for %s is not an IAccessor binder", String8(instance).c_str()); + return BAD_TYPE; + } + String16 reportedInstance; + Status status = accessor->getInstanceName(&reportedInstance); + if (!status.isOk()) { + ALOGE("Failed to validate the binder being used to create a new ARpc_Accessor for %s with " + "status: %s", + String8(instance).c_str(), status.toString8().c_str()); + return NAME_NOT_FOUND; + } + if (reportedInstance != instance) { + ALOGE("Instance %s doesn't match the Accessor's instance of %s", String8(instance).c_str(), + String8(reportedInstance).c_str()); + return NAME_NOT_FOUND; + } + return OK; +} + +sp<IBinder> createAccessor(const String16& instance, + RpcSocketAddressProvider&& connectionInfoProvider) { + // Try to create a new accessor + if (!connectionInfoProvider) { + ALOGE("Could not find an Accessor for %s and no ConnectionInfoProvider provided to " + "create a new one", + String8(instance).c_str()); + return nullptr; + } + sp<IBinder> binder = sp<LocalAccessor>::make(instance, std::move(connectionInfoProvider)); + return binder; +} + #if !defined(__ANDROID_VNDK__) // IPermissionController is not accessible to vendors diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index 49def82b5d..cd21a91d2c 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -589,6 +589,21 @@ status_t RpcSession::setupSocketClient(const RpcSocketAddress& addr) { status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, const std::vector<uint8_t>& sessionId, bool incoming) { + RpcTransportFd transportFd; + status_t status = singleSocketConnection(addr, mShutdownTrigger, &transportFd); + if (status != OK) return status; + + return initAndAddConnection(std::move(transportFd), sessionId, incoming); +} + +status_t singleSocketConnection(const RpcSocketAddress& addr, + const std::unique_ptr<FdTrigger>& shutdownTrigger, + RpcTransportFd* outFd) { + LOG_ALWAYS_FATAL_IF(outFd == nullptr, + "There is no reason to call this function without an outFd"); + LOG_ALWAYS_FATAL_IF(shutdownTrigger == nullptr, + "FdTrigger argument is required so we don't get stuck in the connect call " + "if the server process shuts down."); for (size_t tries = 0; tries < 5; tries++) { if (tries > 0) usleep(10000); @@ -620,7 +635,7 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, if (connErrno == EAGAIN || connErrno == EINPROGRESS) { // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or // EINPROGRESS (for others). Call poll() and getsockopt() to get the error. - status_t pollStatus = mShutdownTrigger->triggerablePoll(transportFd, POLLOUT); + status_t pollStatus = shutdownTrigger->triggerablePoll(transportFd, POLLOUT); if (pollStatus != OK) { ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s", statusToString(pollStatus).c_str()); @@ -654,7 +669,8 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), transportFd.fd.get()); - return initAndAddConnection(std::move(transportFd), sessionId, incoming); + *outFd = std::move(transportFd); + return OK; } ALOGE("Ran out of retries to connect to %s", addr.toString().c_str()); diff --git a/libs/binder/RpcSocketAddress.h b/libs/binder/RpcSocketAddress.h index c7ba5d96a7..ee7d448c43 100644 --- a/libs/binder/RpcSocketAddress.h +++ b/libs/binder/RpcSocketAddress.h @@ -113,4 +113,11 @@ private: unsigned int mPort; }; +/** + * Connects to a single socket and produces a RpcTransportFd. + */ +status_t singleSocketConnection(const RpcSocketAddress& address, + const std::unique_ptr<FdTrigger>& shutdownTrigger, + RpcTransportFd* outFd); + } // namespace android diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 125617363a..95a5da20ae 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -133,6 +133,9 @@ { "name": "binder_sdk_test", "host": true + }, + { + "name": "binderCacheUnitTest" } ], "imports": [ diff --git a/libs/binder/aidl/android/os/IAccessor.aidl b/libs/binder/aidl/android/os/IAccessor.aidl index a3134a3f7c..c06e05c444 100644 --- a/libs/binder/aidl/android/os/IAccessor.aidl +++ b/libs/binder/aidl/android/os/IAccessor.aidl @@ -25,15 +25,56 @@ import android.os.ParcelFileDescriptor; */ interface IAccessor { /** + * The connection info was not available for this service. + * This happens when the user-supplied callback fails to produce + * valid connection info. + * Depending on the implementation of the callback, it might be helpful + * to retry. + */ + const int ERROR_CONNECTION_INFO_NOT_FOUND = 0; + /** + * Failed to create the socket. Often happens when the process trying to create + * the socket lacks the permissions to do so. + * This may be a temporary issue, so retrying the operation is OK. + */ + const int ERROR_FAILED_TO_CREATE_SOCKET = 1; + /** + * Failed to connect to the socket. This can happen for many reasons, so be sure + * log the error message and check it. + * This may be a temporary issue, so retrying the operation is OK. + */ + const int ERROR_FAILED_TO_CONNECT_TO_SOCKET = 2; + /** + * Failed to connect to the socket with EACCES because this process does not + * have perimssions to connect. + * There is no need to retry the connection as this access will not be granted + * upon retry. + */ + const int ERROR_FAILED_TO_CONNECT_EACCES = 3; + /** + * Unsupported socket family type returned. + * There is no need to retry the connection as this socket family is not + * supported. + */ + const int ERROR_UNSUPPORTED_SOCKET_FAMILY = 4; + + /** * Adds a connection to the RPC server of the service managed by the IAccessor. * * This method can be called multiple times to establish multiple distinct * connections to the same RPC server. * + * @throws ServiceSpecificError with message and one of the IAccessor::ERROR_ values. + * * @return A file descriptor connected to the RPC session of the service managed * by IAccessor. */ ParcelFileDescriptor addConnection(); - // TODO(b/350941051): Add API for debugging. + /** + * Get the instance name for the service this accessor is responsible for. + * + * This is used to verify the proxy binder is associated with the expected instance name. + */ + String getInstanceName(); } diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 5fb73079cb..2b23276d1c 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -17,14 +17,17 @@ #pragma once #include <binder/Common.h> #include <binder/IInterface.h> -#include <utils/Vector.h> +// Trusty has its own definition of socket APIs from trusty_ipc.h +#ifndef __TRUSTY__ +#include <sys/socket.h> +#endif // __TRUSTY__ #include <utils/String16.h> +#include <utils/Vector.h> #include <optional> +#include <set> namespace android { -// ---------------------------------------------------------------------- - /** * Service manager for C++ services. * @@ -216,6 +219,80 @@ LIBBINDER_EXPORTED bool checkCallingPermission(const String16& permission, int32 LIBBINDER_EXPORTED bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logPermissionFailure = true); +// ---------------------------------------------------------------------- +// Trusty's definition of the socket APIs does not include sockaddr types +#ifndef __TRUSTY__ +typedef std::function<status_t(const String16& name, sockaddr* outAddr, socklen_t addrSize)> + RpcSocketAddressProvider; + +/** + * This callback provides a way for clients to get access to remote services by + * providing an Accessor object from libbinder that can connect to the remote + * service over sockets. + * + * \param instance name of the service that the callback will provide an + * Accessor for. The provided accessor will be used to set up a client + * RPC connection in libbinder in order to return a binder for the + * associated remote service. + * + * \return IBinder of the Accessor object that libbinder implements. + * nullptr if the provider callback doesn't know how to reach the + * service or doesn't want to provide access for any other reason. + */ +typedef std::function<sp<IBinder>(const String16& instance)> RpcAccessorProvider; + +class AccessorProvider; + +/** + * Register a RpcAccessorProvider for the service manager APIs. + * + * \param instances that the RpcAccessorProvider knows about and can provide an + * Accessor for. + * \param provider callback that generates Accessors. + * + * \return A pointer used as a recept for the successful addition of the + * AccessorProvider. This is needed to unregister it later. + */ +[[nodiscard]] LIBBINDER_EXPORTED std::weak_ptr<AccessorProvider> addAccessorProvider( + std::set<std::string>&& instances, RpcAccessorProvider&& providerCallback); + +/** + * Remove an accessor provider using the pointer provided by addAccessorProvider + * along with the cookie pointer that was used. + * + * \param provider cookie that was returned by addAccessorProvider to keep track + * of this instance. + */ +[[nodiscard]] LIBBINDER_EXPORTED status_t +removeAccessorProvider(std::weak_ptr<AccessorProvider> provider); + +/** + * Create an Accessor associated with a service that can create a socket connection based + * on the connection info from the supplied RpcSocketAddressProvider. + * + * \param instance name of the service that this Accessor is associated with + * \param connectionInfoProvider a callback that returns connection info for + * connecting to the service. + * \return the binder of the IAccessor implementation from libbinder + */ +LIBBINDER_EXPORTED sp<IBinder> createAccessor(const String16& instance, + RpcSocketAddressProvider&& connectionInfoProvider); + +/** + * Check to make sure this binder is the expected binder that is an IAccessor + * associated with a specific instance. + * + * This helper function exists to avoid adding the IAccessor type to + * libbinder_ndk. + * + * \param instance name of the service that this Accessor should be associated with + * \param binder to validate + * + * \return OK if the binder is an IAccessor for `instance` + */ +LIBBINDER_EXPORTED status_t validateAccessor(const String16& instance, const sp<IBinder>& binder); +#endif // __TRUSTY__ + #ifndef __ANDROID__ // Create an IServiceManager that delegates the service manager on the device via adb. // This is can be set as the default service manager at program start, so that diff --git a/libs/binder/trusty/ndk/include/android/llndk-versioning.h b/libs/binder/include/binder/IServiceManagerUnitTestHelper.h index 3ae3d8f577..ff25163ddc 100644 --- a/libs/binder/trusty/ndk/include/android/llndk-versioning.h +++ b/libs/binder/include/binder/IServiceManagerUnitTestHelper.h @@ -13,6 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #pragma once -#define __INTRODUCED_IN_LLNDK(x) /* nothing on Trusty */ +#include <android/os/IServiceManager.h> +#include "IServiceManager.h" +namespace android { + +/** + * Encapsulate an AidlServiceManager in a CppBackendShim. Only used for testing. + */ +LIBBINDER_EXPORTED sp<IServiceManager> getServiceManagerShimFromAidlServiceManagerForTests( + const sp<os::IServiceManager>& sm); + +} // namespace android
\ No newline at end of file diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 4e02aceb86..a7423b3d2a 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -95,6 +95,7 @@ cc_library { "persistable_bundle.cpp", "process.cpp", "service_manager.cpp", + "binder_rpc.cpp", ], static_libs: [ @@ -230,12 +231,24 @@ cc_library_headers { }, apex_available: [ "//apex_available:platform", + "//apex_available:anyapex", "com.android.media", "com.android.media.swcodec", ], min_sdk_version: "29", } +// TODO: if you try to export libbinder_headers_platform_shared from libbinder_ndk.ndk, it will +// not select the NDK variant of libbinder_headers_platform_shared and instead, it will error +// that the NDK can't depend on glibc C++. +cc_library_headers { + name: "libbinder_headers_platform_shared_ndk", + export_include_dirs: ["include_cpp"], + sdk_version: "29", + min_sdk_version: "29", + visibility: [":__subpackages__"], +} + ndk_headers { name: "libbinder_ndk_headers", from: "include_ndk/android", @@ -246,26 +259,14 @@ ndk_headers { license: "NOTICE", } -// TODO(b/160624671): package with the aidl compiler -ndk_headers { - name: "libbinder_ndk_helper_headers", - from: "include_cpp/android", - to: "android", - srcs: [ - "include_cpp/android/*.h", - ], - license: "NOTICE", - // These are intentionally not C. It's a mistake that they're in the NDK. - // See the bug above. - skip_verification: true, -} +// include_cpp are packaged in development/build/sdk.atree with the AIDL compiler ndk_library { name: "libbinder_ndk", symbol_file: "libbinder_ndk.map.txt", first_version: "29", export_header_libs: [ - "libbinder_ndk_headers", - "libbinder_ndk_helper_headers", + // used to be part of the NDK, platform things depend on it + "libbinder_headers_platform_shared_ndk", ], } diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp new file mode 100644 index 0000000000..2cc5f8117e --- /dev/null +++ b/libs/binder/ndk/binder_rpc.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/binder_rpc.h> +#include <arpa/inet.h> +#include <binder/IServiceManager.h> +#include <linux/vm_sockets.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <variant> + +#include "ibinder_internal.h" +#include "status_internal.h" + +using ::android::defaultServiceManager; +using ::android::IBinder; +using ::android::IServiceManager; +using ::android::OK; +using ::android::sp; +using ::android::status_t; +using ::android::String16; +using ::android::String8; +using ::android::binder::Status; + +#define LOG_ACCESSOR_DEBUG(...) +// #define LOG_ACCESSOR_DEBUG(...) ALOGW(__VA_ARGS__) + +struct ABinderRpc_ConnectionInfo { + std::variant<sockaddr_vm, sockaddr_un, sockaddr_in> addr; +}; + +struct ABinderRpc_Accessor final : public ::android::RefBase { + static ABinderRpc_Accessor* make(const char* instance, const sp<IBinder>& binder) { + LOG_ALWAYS_FATAL_IF(binder == nullptr, "ABinderRpc_Accessor requires a non-null binder"); + status_t status = android::validateAccessor(String16(instance), binder); + if (status != OK) { + ALOGE("The given binder is not a valid IAccessor for %s. Status: %s", instance, + android::statusToString(status).c_str()); + return nullptr; + } + return new ABinderRpc_Accessor(binder); + } + + sp<IBinder> asBinder() { return mAccessorBinder; } + + ~ABinderRpc_Accessor() { LOG_ACCESSOR_DEBUG("ABinderRpc_Accessor dtor"); } + + private: + ABinderRpc_Accessor(sp<IBinder> accessor) : mAccessorBinder(accessor) {} + ABinderRpc_Accessor() = delete; + sp<IBinder> mAccessorBinder; +}; + +struct ABinderRpc_AccessorProvider { + public: + static ABinderRpc_AccessorProvider* make(std::weak_ptr<android::AccessorProvider> cookie) { + if (cookie.expired()) { + ALOGE("Null AccessorProvider cookie from libbinder"); + return nullptr; + } + return new ABinderRpc_AccessorProvider(cookie); + } + std::weak_ptr<android::AccessorProvider> mProviderCookie; + + private: + ABinderRpc_AccessorProvider() = delete; + + ABinderRpc_AccessorProvider(std::weak_ptr<android::AccessorProvider> provider) + : mProviderCookie(provider) {} +}; + +struct OnDeleteProviderHolder { + OnDeleteProviderHolder(void* data, ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) + : mData(data), mOnDelete(onDelete) {} + ~OnDeleteProviderHolder() { + if (mOnDelete) { + mOnDelete(mData); + } + } + void* mData; + ABinderRpc_AccessorProviderUserData_deleteCallback mOnDelete; + // needs to be copyable for std::function, but we will never copy it + OnDeleteProviderHolder(const OnDeleteProviderHolder&) { + LOG_ALWAYS_FATAL("This object can't be copied!"); + } + + private: + OnDeleteProviderHolder() = delete; +}; + +ABinderRpc_AccessorProvider* ABinderRpc_registerAccessorProvider( + ABinderRpc_AccessorProvider_getAccessorCallback provider, const char** instances, + size_t numInstances, void* data, + ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) { + if (provider == nullptr) { + ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider"); + return nullptr; + } + if (data && onDelete == nullptr) { + ALOGE("If a non-null data ptr is passed to ABinderRpc_registerAccessorProvider, then a " + "ABinderRpc_AccessorProviderUserData_deleteCallback must alse be passed to delete " + "the data object once the ABinderRpc_AccessorProvider is removed."); + return nullptr; + } + if (numInstances == 0 || instances == nullptr) { + ALOGE("No instances passed to ABinderRpc_registerAccessorProvider. numInstances: %zu", + numInstances); + return nullptr; + } + std::set<std::string> instanceStrings; + for (size_t i = 0; i < numInstances; i++) { + instanceStrings.emplace(instances[i]); + } + // call the onDelete when the last reference of this goes away (when the + // last reference to the generate std::function goes away). + std::shared_ptr<OnDeleteProviderHolder> onDeleteHolder = + std::make_shared<OnDeleteProviderHolder>(data, onDelete); + android::RpcAccessorProvider generate = [provider, + onDeleteHolder](const String16& name) -> sp<IBinder> { + ABinderRpc_Accessor* accessor = provider(String8(name).c_str(), onDeleteHolder->mData); + if (accessor == nullptr) { + ALOGE("The supplied ABinderRpc_AccessorProvider_getAccessorCallback returned nullptr"); + return nullptr; + } + sp<IBinder> binder = accessor->asBinder(); + ABinderRpc_Accessor_delete(accessor); + return binder; + }; + + std::weak_ptr<android::AccessorProvider> cookie = + android::addAccessorProvider(std::move(instanceStrings), std::move(generate)); + return ABinderRpc_AccessorProvider::make(cookie); +} + +void ABinderRpc_unregisterAccessorProvider(ABinderRpc_AccessorProvider* provider) { + if (provider == nullptr) { + LOG_ALWAYS_FATAL("Attempting to remove a null ABinderRpc_AccessorProvider"); + } + + status_t status = android::removeAccessorProvider(provider->mProviderCookie); + // There shouldn't be a way to get here because the caller won't have a + // ABinderRpc_AccessorProvider* without calling ABinderRpc_registerAccessorProvider + LOG_ALWAYS_FATAL_IF(status == android::BAD_VALUE, "Provider (%p) is not valid. Status: %s", + provider, android::statusToString(status).c_str()); + LOG_ALWAYS_FATAL_IF(status == android::NAME_NOT_FOUND, + "Provider (%p) was already unregistered. Status: %s", provider, + android::statusToString(status).c_str()); + LOG_ALWAYS_FATAL_IF(status != OK, + "Unknown error when attempting to unregister ABinderRpc_AccessorProvider " + "(%p). Status: %s", + provider, android::statusToString(status).c_str()); + + delete provider; +} + +struct OnDeleteConnectionInfoHolder { + OnDeleteConnectionInfoHolder(void* data, + ABinderRpc_ConnectionInfoProviderUserData_delete onDelete) + : mData(data), mOnDelete(onDelete) {} + ~OnDeleteConnectionInfoHolder() { + if (mOnDelete) { + mOnDelete(mData); + } + } + void* mData; + ABinderRpc_ConnectionInfoProviderUserData_delete mOnDelete; + // needs to be copyable for std::function, but we will never copy it + OnDeleteConnectionInfoHolder(const OnDeleteConnectionInfoHolder&) { + LOG_ALWAYS_FATAL("This object can't be copied!"); + } + + private: + OnDeleteConnectionInfoHolder() = delete; +}; + +ABinderRpc_Accessor* ABinderRpc_Accessor_new( + const char* instance, ABinderRpc_ConnectionInfoProvider provider, void* data, + ABinderRpc_ConnectionInfoProviderUserData_delete onDelete) { + if (instance == nullptr) { + ALOGE("Instance argument must be valid when calling ABinderRpc_Accessor_new"); + return nullptr; + } + if (data && onDelete == nullptr) { + ALOGE("If a non-null data ptr is passed to ABinderRpc_Accessor_new, then a " + "ABinderRpc_ConnectionInfoProviderUserData_delete callback must alse be passed to " + "delete " + "the data object once the ABinderRpc_Accessor is deleted."); + return nullptr; + } + std::shared_ptr<OnDeleteConnectionInfoHolder> onDeleteHolder = + std::make_shared<OnDeleteConnectionInfoHolder>(data, onDelete); + if (provider == nullptr) { + ALOGE("Can't create a new ABinderRpc_Accessor without a ABinderRpc_ConnectionInfoProvider " + "and it is " + "null"); + return nullptr; + } + android::RpcSocketAddressProvider generate = [provider, onDeleteHolder]( + const String16& name, sockaddr* outAddr, + size_t addrLen) -> status_t { + std::unique_ptr<ABinderRpc_ConnectionInfo> info( + provider(String8(name).c_str(), onDeleteHolder->mData)); + if (info == nullptr) { + ALOGE("The supplied ABinderRpc_ConnectionInfoProvider returned nullptr"); + return android::NAME_NOT_FOUND; + } + if (auto addr = std::get_if<sockaddr_vm>(&info->addr)) { + LOG_ALWAYS_FATAL_IF(addr->svm_family != AF_VSOCK, + "ABinderRpc_ConnectionInfo invalid family"); + if (addrLen < sizeof(sockaddr_vm)) { + ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_vm), + addrLen); + return android::BAD_VALUE; + } + LOG_ACCESSOR_DEBUG( + "Connection info provider found AF_VSOCK. family %d, port %d, cid %d", + addr->svm_family, addr->svm_port, addr->svm_cid); + *reinterpret_cast<sockaddr_vm*>(outAddr) = *addr; + } else if (auto addr = std::get_if<sockaddr_un>(&info->addr)) { + LOG_ALWAYS_FATAL_IF(addr->sun_family != AF_UNIX, + "ABinderRpc_ConnectionInfo invalid family"); + if (addrLen < sizeof(sockaddr_un)) { + ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_un), + addrLen); + return android::BAD_VALUE; + } + *reinterpret_cast<sockaddr_un*>(outAddr) = *addr; + } else if (auto addr = std::get_if<sockaddr_in>(&info->addr)) { + LOG_ALWAYS_FATAL_IF(addr->sin_family != AF_INET, + "ABinderRpc_ConnectionInfo invalid family"); + if (addrLen < sizeof(sockaddr_in)) { + ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_in), + addrLen); + return android::BAD_VALUE; + } + *reinterpret_cast<sockaddr_in*>(outAddr) = *addr; + } else { + LOG_ALWAYS_FATAL( + "Unsupported address family type when trying to get ARpcConnection info. A " + "new variant was added to the ABinderRpc_ConnectionInfo and this needs to be " + "updated."); + } + return OK; + }; + sp<IBinder> accessorBinder = android::createAccessor(String16(instance), std::move(generate)); + if (accessorBinder == nullptr) { + ALOGE("service manager did not get us an accessor"); + return nullptr; + } + LOG_ACCESSOR_DEBUG("service manager found an accessor, so returning one now from _new"); + return ABinderRpc_Accessor::make(instance, accessorBinder); +} + +void ABinderRpc_Accessor_delete(ABinderRpc_Accessor* accessor) { + delete accessor; +} + +AIBinder* ABinderRpc_Accessor_asBinder(ABinderRpc_Accessor* accessor) { + if (!accessor) { + ALOGE("ABinderRpc_Accessor argument is null."); + return nullptr; + } + + sp<IBinder> binder = accessor->asBinder(); + sp<AIBinder> aBinder = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder* ptr = aBinder.get(); + if (ptr == nullptr) { + LOG_ALWAYS_FATAL("Failed to lookupOrCreateFromBinder"); + } + ptr->incStrong(nullptr); + return ptr; +} + +ABinderRpc_Accessor* ABinderRpc_Accessor_fromBinder(const char* instance, AIBinder* binder) { + if (!binder) { + ALOGE("binder argument is null"); + return nullptr; + } + sp<IBinder> accessorBinder = binder->getBinder(); + if (accessorBinder) { + return ABinderRpc_Accessor::make(instance, accessorBinder); + } else { + ALOGE("Attempting to get an ABinderRpc_Accessor for %s but AIBinder::getBinder returned " + "null", + instance); + return nullptr; + } +} + +ABinderRpc_ConnectionInfo* ABinderRpc_ConnectionInfo_new(const sockaddr* addr, socklen_t len) { + if (addr == nullptr || len < 0 || static_cast<size_t>(len) < sizeof(sa_family_t)) { + ALOGE("Invalid arguments in Arpc_Connection_new"); + return nullptr; + } + // socklen_t was int32_t on 32-bit and uint32_t on 64 bit. + size_t socklen = len < 0 || static_cast<uintmax_t>(len) > SIZE_MAX ? 0 : len; + + if (addr->sa_family == AF_VSOCK) { + if (len != sizeof(sockaddr_vm)) { + ALOGE("Incorrect size of %zu for AF_VSOCK sockaddr_vm. Expecting %zu", socklen, + sizeof(sockaddr_vm)); + return nullptr; + } + sockaddr_vm vm = *reinterpret_cast<const sockaddr_vm*>(addr); + LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_VSOCK. family %d, port %d, cid %d", + vm.svm_family, vm.svm_port, vm.svm_cid); + return new ABinderRpc_ConnectionInfo(vm); + } else if (addr->sa_family == AF_UNIX) { + if (len != sizeof(sockaddr_un)) { + ALOGE("Incorrect size of %zu for AF_UNIX sockaddr_un. Expecting %zu", socklen, + sizeof(sockaddr_un)); + return nullptr; + } + sockaddr_un un = *reinterpret_cast<const sockaddr_un*>(addr); + LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_UNIX. family %d, path %s", + un.sun_family, un.sun_path); + return new ABinderRpc_ConnectionInfo(un); + } else if (addr->sa_family == AF_INET) { + if (len != sizeof(sockaddr_in)) { + ALOGE("Incorrect size of %zu for AF_INET sockaddr_in. Expecting %zu", socklen, + sizeof(sockaddr_in)); + return nullptr; + } + sockaddr_in in = *reinterpret_cast<const sockaddr_in*>(addr); + LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_INET. family %d, address %s, port %d", + in.sin_family, inet_ntoa(in.sin_addr), ntohs(in.sin_port)); + return new ABinderRpc_ConnectionInfo(in); + } + + ALOGE("ARpc APIs only support AF_VSOCK right now but the supplied sockadder::sa_family is: %hu", + addr->sa_family); + return nullptr; +} + +void ABinderRpc_ConnectionInfo_delete(ABinderRpc_ConnectionInfo* info) { + delete info; +} diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h index 62738041ba..af56bf0da1 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -225,6 +225,8 @@ class BpCInterface : public INTERFACE { SpAIBinder asBinder() override final; + const SpAIBinder& asBinderReference() { return mBinder; } + bool isRemote() override final { return AIBinder_isRemote(mBinder.get()); } binder_status_t dump(int fd, const char** args, uint32_t numArgs) override { diff --git a/libs/binder/ndk/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h index 5e0d4da97b..1d516aea9d 100644 --- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h +++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h @@ -17,13 +17,6 @@ #pragma once #include <android/binder_parcel.h> -#if defined(__ANDROID_VENDOR__) -#include <android/llndk-versioning.h> -#else -#if !defined(__INTRODUCED_IN_LLNDK) -#define __INTRODUCED_IN_LLNDK(level) __attribute__((annotate("introduced_in_llndk=" #level))) -#endif -#endif // __ANDROID_VENDOR__ #include <stdbool.h> #include <stdint.h> #include <sys/cdefs.h> @@ -83,8 +76,7 @@ typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t s * * \return Pointer to a new APersistableBundle */ -APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); +APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__); /** * Create a new APersistableBundle based off an existing APersistableBundle. @@ -98,7 +90,7 @@ APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID * \return Pointer to a new APersistableBundle */ APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _Nonnull pBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Delete an APersistableBundle. This must always be called when finished using @@ -109,7 +101,7 @@ APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _ * Available since API level 202404. */ void APersistableBundle_delete(APersistableBundle* _Nullable pBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Check for equality of APersistableBundles. @@ -123,7 +115,7 @@ void APersistableBundle_delete(APersistableBundle* _Nullable pBundle) */ bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs, const APersistableBundle* _Nonnull rhs) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Read an APersistableBundle from an AParcel. @@ -142,7 +134,7 @@ bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs, */ binder_status_t APersistableBundle_readFromParcel( const AParcel* _Nonnull parcel, APersistableBundle* _Nullable* _Nonnull outPBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Write an APersistableBundle to an AParcel. @@ -162,7 +154,7 @@ binder_status_t APersistableBundle_readFromParcel( */ binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonnull pBundle, AParcel* _Nonnull parcel) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get the size of an APersistableBundle. This is the number of mappings in the @@ -175,7 +167,7 @@ binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonn * \return number of mappings in the object */ int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Erase any entries added with the provided key. @@ -188,7 +180,7 @@ int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle) * \return number of entries erased. Either 0 or 1. */ int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a boolean associated with the provided key. @@ -201,8 +193,7 @@ int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const cha * Available since API level 202404. */ void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - bool val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + bool val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an int32_t associated with the provided key. @@ -215,8 +206,7 @@ void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const c * Available since API level 202404. */ void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int32_t val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an int64_t associated with the provided key. @@ -229,8 +219,7 @@ void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* * Available since API level 202404. */ void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int64_t val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int64_t val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a double associated with the provided key. @@ -243,8 +232,7 @@ void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char * Available since API level 202404. */ void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - double val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + double val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a string associated with the provided key. @@ -258,8 +246,7 @@ void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const ch * Available since API level 202404. */ void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a boolean vector associated with the provided key. @@ -275,8 +262,7 @@ void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const ch */ void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const bool* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an int32_t vector associated with the provided key. @@ -292,7 +278,7 @@ void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle, */ void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const int32_t* _Nonnull vec, int32_t num) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an int64_t vector associated with the provided key. @@ -308,8 +294,7 @@ void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const */ void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const int64_t* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a double vector associated with the provided key. @@ -325,8 +310,7 @@ void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle, */ void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const double* _Nonnull vec, - int32_t num) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t num) __INTRODUCED_IN(__ANDROID_API_V__); /** * Put a string vector associated with the provided key. @@ -343,7 +327,7 @@ void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle, void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const char* _Nullable const* _Nullable vec, int32_t num) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Put an APersistableBundle associated with the provided key. @@ -359,7 +343,7 @@ void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle, void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, const APersistableBundle* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a boolean associated with the provided key. @@ -374,7 +358,7 @@ void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundl */ bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, bool* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an int32_t associated with the provided key. @@ -388,8 +372,7 @@ bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle, * \return true if a value exists for the provided key */ bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, - int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an int64_t associated with the provided key. @@ -404,7 +387,7 @@ bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const */ bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int64_t* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a double associated with the provided key. @@ -419,7 +402,7 @@ bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle, */ bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, double* _Nonnull val) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a string associated with the provided key. @@ -440,8 +423,7 @@ bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle, int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, char* _Nullable* _Nonnull val, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a boolean vector associated with the provided key and place it in the @@ -468,7 +450,7 @@ int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle, int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, bool* _Nullable buffer, int32_t bufferSizeBytes) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an int32_t vector associated with the provided key and place it in the @@ -494,8 +476,7 @@ int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull p */ int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int32_t* _Nullable buffer, - int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an int64_t vector associated with the provided key and place it in the @@ -521,8 +502,8 @@ int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBund */ int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, int64_t* _Nullable buffer, - int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int32_t bufferSizeBytes) + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a double vector associated with the provided key and place it in the @@ -549,7 +530,7 @@ int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBun int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, double* _Nullable buffer, int32_t bufferSizeBytes) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get a string vector associated with the provided key and place it in the @@ -586,7 +567,7 @@ int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pB int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get an APersistableBundle* associated with the provided key. @@ -605,7 +586,7 @@ int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pB bool APersistableBundle_getPersistableBundle(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, APersistableBundle* _Nullable* _Nonnull outBundle) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -638,7 +619,7 @@ int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBu int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -669,8 +650,7 @@ int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBu int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -701,8 +681,7 @@ int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -734,8 +713,8 @@ int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBun char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -767,8 +746,8 @@ int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBun char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -801,7 +780,7 @@ int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnu int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -834,7 +813,7 @@ int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull p int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -867,7 +846,7 @@ int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -899,7 +878,7 @@ int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnul int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -932,7 +911,7 @@ int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnul int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, void* _Nullable context) - __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + __INTRODUCED_IN(__ANDROID_API_V__); /** * Get all of the keys associated with this specific type and place it in the @@ -963,6 +942,6 @@ int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnul int32_t APersistableBundle_getPersistableBundleKeys( const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator, - void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404); + void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__); __END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 41b30a0a0f..cc4943b9c3 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -18,7 +18,6 @@ #include <android/binder_ibinder.h> #include <android/binder_status.h> -#include <android/llndk-versioning.h> #include <sys/cdefs.h> __BEGIN_DECLS @@ -257,8 +256,7 @@ void AServiceManager_getUpdatableApexName(const char* instance, void* context, * \return the result of dlopen of the specified HAL */ void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance, - int flag) __INTRODUCED_IN(__ANDROID_API_V__) - __INTRODUCED_IN_LLNDK(202404); + int flag) __INTRODUCED_IN(__ANDROID_API_V__); /** * Prevent lazy services without client from shutting down their process diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h new file mode 100644 index 0000000000..4c5471ff70 --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_rpc.h @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_ibinder.h> +#include <sys/socket.h> + +__BEGIN_DECLS + +/** + * This represents an IAccessor implementation from libbinder that is + * responsible for providing a pre-connected socket file descriptor for a + * specific service. The service is an RpcServer and the pre-connected socket is + * used to set up a client RpcSession underneath libbinder's IServiceManager APIs + * to provide the client with the service's binder for remote communication. + */ +typedef struct ABinderRpc_Accessor ABinderRpc_Accessor; + +/** + * This represents an object that supplies ABinderRpc_Accessors to libbinder + * when they are requested. They are requested any time a client is attempting + * to get a service through IServiceManager APIs when the services aren't known by + * servicemanager. + */ +typedef struct ABinderRpc_AccessorProvider ABinderRpc_AccessorProvider; + +/** + * This represents information necessary for libbinder to be able to connect to a + * remote service. + * It supports connecting to linux sockets and is created using sockaddr + * types for sockets supported by libbinder like sockaddr_in, sockaddr_un, + * sockaddr_vm. + */ +typedef struct ABinderRpc_ConnectionInfo ABinderRpc_ConnectionInfo; + +/** + * These APIs provide a way for clients of binder services to be able to get a + * binder object of that service through the existing libbinder/libbinder_ndk + * Service Manager APIs when that service is using RPC Binder over sockets + * instead kernel binder. + * + * Some of these APIs are used on Android hosts when kernel binder is supported + * and the usual servicemanager process is available. Some of these APIs are + * only required when there is no kernel binder or extra servicemanager process + * such as the case of microdroid or similar VMs. + */ + +/** + * This callback is responsible for returning ABinderRpc_Accessor objects for a given + * service instance. These ABinderRpc_Accessor objects are implemented by + * libbinder_ndk and backed by implementations of android::os::IAccessor in + * libbinder. + * + * \param instance name of the service like + * `android.hardware.vibrator.IVibrator/default` + * \param data the data that was associated with this instance when the callback + * was registered. + * \return The ABinderRpc_Accessor associated with the service `instance`. This + * callback gives up ownership of the object once it returns it. The + * caller of this callback (libbinder_ndk) is responsible for deleting it + * with ABinderRpc_Accessor_delete. + */ +typedef ABinderRpc_Accessor* _Nullable (*ABinderRpc_AccessorProvider_getAccessorCallback)( + const char* _Nonnull instance, void* _Nullable data); + +/** + * This callback is responsible deleting the `void* data` object that is passed + * in to ABinderRpc_registerAccessorProvider for the ABinderRpc_AccessorProvider_getAccessorCallback + * to use. That object is owned by the ABinderRpc_AccessorProvider and must remain valid for the + * lifetime of the callback because it may be called and use the object. + * This _delete callback is called after the ABinderRpc_AccessorProvider is remove and + * is guaranteed never to be called again. + * + * \param data a pointer to data that the ABinderRpc_AccessorProvider_getAccessorCallback uses which + * is to be deleted by this call. + */ +typedef void (*ABinderRpc_AccessorProviderUserData_deleteCallback)(void* _Nullable data); + +/** + * Inject an ABinderRpc_AccessorProvider_getAccessorCallback into the process for + * the Service Manager APIs to use to retrieve ABinderRpc_Accessor objects associated + * with different RPC Binder services. + * + * \param provider callback that returns ABinderRpc_Accessors for libbinder to set up + * RPC clients with. + * \param instances array of instances that are supported by this provider. It + * will only be called if the client is looking for an instance that is + * in this list. These instances must be unique per-process. If an + * instance is being registered that was previously registered, this call + * will fail and the ABinderRpc_AccessorProviderUserData_deleteCallback + * will be called to clean up the data. + * \param number of instances in the instances array. + * \param data pointer that is passed to the ABinderRpc_AccessorProvider callback. + * IMPORTANT: The ABinderRpc_AccessorProvider now OWNS that object that data + * points to. It can be used as necessary in the callback. The data MUST + * remain valid for the lifetime of the provider callback. + * Do not attempt to give ownership of the same object to different + * providers throguh multiple calls to this function because the first + * one to be deleted will call the onDelete callback. + * \param onDelete callback used to delete the objects that `data` points to. + * This is called after ABinderRpc_AccessorProvider is guaranteed to never be + * called again. Before this callback is called, `data` must remain + * valid. + * \return nullptr on error if the data pointer is non-null and the onDelete + * callback is null or if an instance in the instances list was previously + * registered. In the error case of duplicate instances, if data was + * provided with a ABinderRpc_AccessorProviderUserData_deleteCallback, + * the callback will be called to delete the data. + * Otherwise returns a pointer to the ABinderRpc_AccessorProvider that + * can be used to remove with ABinderRpc_unregisterAccessorProvider. + */ +ABinderRpc_AccessorProvider* _Nullable ABinderRpc_registerAccessorProvider( + ABinderRpc_AccessorProvider_getAccessorCallback _Nonnull provider, + const char* _Nullable* _Nonnull instances, size_t numInstances, void* _Nullable data, + ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) __INTRODUCED_IN(36); + +/** + * Remove an ABinderRpc_AccessorProvider from libbinder. This will remove references + * from the ABinderRpc_AccessorProvider and will no longer call the + * ABinderRpc_AccessorProvider_getAccessorCallback. + * + * Note: The `data` object that was used when adding the accessor will be + * deleted by the ABinderRpc_AccessorProviderUserData_deleteCallback at some + * point after this call. Do not use the object and do not try to delete + * it through any other means. + * Note: This will abort when used incorrectly if this provider was never + * registered or if it were already unregistered. + * + * \param provider to be removed and deleted + * + */ +void ABinderRpc_unregisterAccessorProvider(ABinderRpc_AccessorProvider* _Nonnull provider) + __INTRODUCED_IN(36); + +/** + * Callback which returns the RPC connection information for libbinder to use to + * connect to a socket that a given service is listening on. This is needed to + * create an ABinderRpc_Accessor so it can connect to these services. + * + * \param instance name of the service to connect to + * \param data userdata for this callback. The pointer is provided in + * ABinderRpc_Accessor_new. + * \return ABinderRpc_ConnectionInfo with socket connection information for `instance` + */ +typedef ABinderRpc_ConnectionInfo* _Nullable (*ABinderRpc_ConnectionInfoProvider)( + const char* _Nonnull instance, void* _Nullable data) __INTRODUCED_IN(36); +/** + * This callback is responsible deleting the `void* data` object that is passed + * in to ABinderRpc_Accessor_new for the ABinderRpc_ConnectionInfoProvider to use. That + * object is owned by the ABinderRpc_Accessor and must remain valid for the + * lifetime the Accessor because it may be used by the connection info provider + * callback. + * This _delete callback is called after the ABinderRpc_Accessor is removed and + * is guaranteed never to be called again. + * + * \param data a pointer to data that the ABinderRpc_AccessorProvider uses which is to + * be deleted by this call. + */ +typedef void (*ABinderRpc_ConnectionInfoProviderUserData_delete)(void* _Nullable data); + +/** + * Create a new ABinderRpc_Accessor. This creates an IAccessor object in libbinder + * that can use the info from the ABinderRpc_ConnectionInfoProvider to connect to a + * socket that the service with `instance` name is listening to. + * + * \param instance name of the service that is listening on the socket + * \param provider callback that can get the socket connection information for the + * instance. This connection information may be dynamic, so the + * provider will be called any time a new connection is required. + * \param data pointer that is passed to the ABinderRpc_ConnectionInfoProvider callback. + * IMPORTANT: The ABinderRpc_ConnectionInfoProvider now OWNS that object that data + * points to. It can be used as necessary in the callback. The data MUST + * remain valid for the lifetime of the provider callback. + * Do not attempt to give ownership of the same object to different + * providers through multiple calls to this function because the first + * one to be deleted will call the onDelete callback. + * \param onDelete callback used to delete the objects that `data` points to. + * This is called after ABinderRpc_ConnectionInfoProvider is guaranteed to never be + * called again. Before this callback is called, `data` must remain + * valid. + * \return an ABinderRpc_Accessor instance. This is deleted by the caller once it is + * no longer needed. + */ +ABinderRpc_Accessor* _Nullable ABinderRpc_Accessor_new( + const char* _Nonnull instance, ABinderRpc_ConnectionInfoProvider _Nonnull provider, + void* _Nullable data, ABinderRpc_ConnectionInfoProviderUserData_delete _Nullable onDelete) + __INTRODUCED_IN(36); + +/** + * Delete an ABinderRpc_Accessor + * + * \param accessor to delete + */ +void ABinderRpc_Accessor_delete(ABinderRpc_Accessor* _Nonnull accessor) __INTRODUCED_IN(36); + +/** + * Return the AIBinder associated with an ABinderRpc_Accessor. This can be used to + * send the Accessor to another process or even register it with servicemanager. + * + * \param accessor to get the AIBinder for + * \return binder of the supplied accessor with one strong ref count + */ +AIBinder* _Nullable ABinderRpc_Accessor_asBinder(ABinderRpc_Accessor* _Nonnull accessor) + __INTRODUCED_IN(36); + +/** + * Return the ABinderRpc_Accessor associated with an AIBinder. The instance must match + * the ABinderRpc_Accessor implementation, and the AIBinder must a proxy binder for a + * remote service (ABpBinder). + * This can be used when receivng an AIBinder from another process that the + * other process obtained from ABinderRpc_Accessor_asBinder. + * + * \param instance name of the service that the Accessor is responsible for. + * \param accessorBinder proxy binder from another processes ABinderRpc_Accessor. + * \return ABinderRpc_Accessor representing the other processes ABinderRpc_Accessor + * implementation. This function does not take ownership of the + * ABinderRpc_Accessor (so the caller needs to delete with + * ABinderRpc_Accessor_delete), and it preserves the recount of the bidner + * object. + */ +ABinderRpc_Accessor* _Nullable ABinderRpc_Accessor_fromBinder(const char* _Nonnull instance, + AIBinder* _Nonnull accessorBinder) + __INTRODUCED_IN(36); + +/** + * Create a new ABinderRpc_ConnectionInfo with sockaddr. This can be supported socket + * types like sockaddr_vm (vsock) and sockaddr_un (Unix Domain Sockets). + * + * \param addr sockaddr pointer that can come from supported socket + * types like sockaddr_vm (vsock) and sockaddr_un (Unix Domain Sockets). + * \param len length of the concrete sockaddr type being used. Like + * sizeof(sockaddr_vm) when sockaddr_vm is used. + * \return the connection info based on the given sockaddr + */ +ABinderRpc_ConnectionInfo* _Nullable ABinderRpc_ConnectionInfo_new(const sockaddr* _Nonnull addr, + socklen_t len) + __INTRODUCED_IN(36); + +/** + * Delete an ABinderRpc_ConnectionInfo object that was created with + * ABinderRpc_ConnectionInfo_new. + * + * \param info object to be deleted + */ +void ABinderRpc_ConnectionInfo_delete(ABinderRpc_ConnectionInfo* _Nonnull info) __INTRODUCED_IN(36); + +__END_DECLS diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 826e199093..c9e669ed36 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -248,6 +248,18 @@ LIBBINDER_NDK35 { # introduced=VanillaIceCream AServiceManager_openDeclaredPassthroughHal; # systemapi llndk=202404 }; +LIBBINDER_NDK36 { # introduced=36 + global: + ABinderRpc_registerAccessorProvider; # systemapi + ABinderRpc_unregisterAccessorProvider; # systemapi + ABinderRpc_Accessor_new; # systemapi + ABinderRpc_Accessor_delete; # systemapi + ABinderRpc_Accessor_asBinder; # systemapi + ABinderRpc_Accessor_fromBinder; # systemapi + ABinderRpc_ConnectionInfo_new; # systemapi + ABinderRpc_ConnectionInfo_delete; # systemapi +}; + LIBBINDER_NDK_PLATFORM { global: AParcel_getAllowFds; diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index f518a22902..3cd2b9a891 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -46,6 +46,7 @@ #include "android/binder_ibinder.h" using namespace android; +using namespace std::chrono_literals; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest"; @@ -54,7 +55,7 @@ constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestServi constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService"; constexpr char kBinderNdkUnitTestServiceFlagged[] = "BinderNdkUnitTestFlagged"; -constexpr unsigned int kShutdownWaitTime = 11; +constexpr auto kShutdownWaitTime = 30s; constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715; class MyTestFoo : public IFoo { @@ -253,12 +254,22 @@ int lazyService(const char* instance) { } bool isServiceRunning(const char* serviceName) { - AIBinder* binder = AServiceManager_checkService(serviceName); - if (binder == nullptr) { - return false; + static const sp<android::IServiceManager> sm(android::defaultServiceManager()); + const Vector<String16> services = sm->listServices(); + for (const auto service : services) { + if (service == String16(serviceName)) return true; } - AIBinder_decStrong(binder); + return false; +} +bool isServiceShutdownWithWait(const char* serviceName) { + LOG(INFO) << "About to check and wait for shutdown of " << std::string(serviceName); + const auto before = std::chrono::steady_clock::now(); + while (isServiceRunning(serviceName)) { + sleep(1); + const auto after = std::chrono::steady_clock::now(); + if (after - before >= kShutdownWaitTime) return false; + } return true; } @@ -450,8 +461,8 @@ TEST(NdkBinder, CheckLazyServiceShutDown) { service = nullptr; IPCThreadState::self()->flushCommands(); // Make sure the service is dead after some time of no use - sleep(kShutdownWaitTime); - ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService)); + ASSERT_TRUE(isServiceShutdownWithWait(kLazyBinderNdkUnitTestService)) + << "Service failed to shut down"; } TEST(NdkBinder, ForcedPersistenceTest) { @@ -466,14 +477,12 @@ TEST(NdkBinder, ForcedPersistenceTest) { service = nullptr; IPCThreadState::self()->flushCommands(); - sleep(kShutdownWaitTime); - - bool isRunning = isServiceRunning(kForcePersistNdkUnitTestService); - if (i == 0) { - ASSERT_TRUE(isRunning) << "Service shut down when it shouldn't have."; + ASSERT_TRUE(isServiceRunning(kForcePersistNdkUnitTestService)) + << "Service shut down when it shouldn't have."; } else { - ASSERT_FALSE(isRunning) << "Service failed to shut down."; + ASSERT_TRUE(isServiceShutdownWithWait(kForcePersistNdkUnitTestService)) + << "Service failed to shut down"; } } } @@ -491,10 +500,7 @@ TEST(NdkBinder, ActiveServicesCallbackTest) { service = nullptr; IPCThreadState::self()->flushCommands(); - LOG(INFO) << "ActiveServicesCallbackTest about to sleep"; - sleep(kShutdownWaitTime); - - ASSERT_FALSE(isServiceRunning(kActiveServicesNdkUnitTestService)) + ASSERT_TRUE(isServiceShutdownWithWait(kActiveServicesNdkUnitTestService)) << "Service failed to shut down."; } diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 340014aeaa..04f1517556 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -195,7 +195,7 @@ impl PartialOrd for SpIBinder { impl PartialEq for SpIBinder { fn eq(&self, other: &Self) -> bool { - ptr::eq(self.0.as_ptr(), other.0.as_ptr()) + self.cmp(other) == Ordering::Equal } } diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 957871379b..8b0dda33dd 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -51,6 +51,30 @@ cc_test { ], } +cc_test { + name: "binderCacheUnitTest", + target: { + darwin: { + enabled: false, + }, + }, + srcs: [ + "binderCacheUnitTest.cpp", + ], + shared_libs: [ + "liblog", + "libbinder", + "libcutils", + "libutils", + ], + static_libs: [ + "libfakeservicemanager", + ], + defaults: ["libbinder_client_cache_flag"], + test_suites: ["general-tests"], + require_root: true, +} + // unit test only, which can run on host and doesn't use /dev/binder cc_test { name: "binderUnitTest", @@ -508,6 +532,9 @@ cc_test { static_libs: [ "libbinder_rpc_single_threaded", ], + shared_libs: [ + "libbinder_ndk", + ], } cc_test { @@ -888,6 +915,7 @@ cc_defaults { enabled: false, }, }, + corpus: ["corpus/*"], fuzz_config: { cc: [ "smoreland@google.com", diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp new file mode 100644 index 0000000000..482d197688 --- /dev/null +++ b/libs/binder/tests/binderCacheUnitTest.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <android-base/logging.h> +#include <android/os/IServiceManager.h> +#include <binder/IBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/IServiceManagerUnitTestHelper.h> +#include "fakeservicemanager/FakeServiceManager.h" + +#include <sys/prctl.h> +#include <thread> + +using namespace android; + +#ifdef LIBBINDER_CLIENT_CACHE +constexpr bool kUseLibbinderCache = true; +#else +constexpr bool kUseLibbinderCache = false; +#endif + +// A service name which is in the static list of cachable services +const String16 kCachedServiceName = String16("isub"); + +#define EXPECT_OK(status) \ + do { \ + binder::Status stat = (status); \ + EXPECT_TRUE(stat.isOk()) << stat; \ + } while (false) + +const String16 kServerName = String16("binderCacheUnitTest"); + +class FooBar : public BBinder { +public: + status_t onTransact(uint32_t, const Parcel&, Parcel*, uint32_t) { + // exit the server + std::thread([] { exit(EXIT_FAILURE); }).detach(); + return OK; + } + void killServer(sp<IBinder> binder) { + Parcel data, reply; + binder->transact(0, data, &reply, 0); + } +}; + +class MockAidlServiceManager : public os::IServiceManagerDefault { +public: + MockAidlServiceManager() : innerSm() {} + + binder::Status checkService(const ::std::string& name, os::Service* _out) override { + sp<IBinder> binder = innerSm.getService(String16(name.c_str())); + *_out = os::Service::make<os::Service::Tag::binder>(binder); + return binder::Status::ok(); + } + + binder::Status addService(const std::string& name, const sp<IBinder>& service, + bool allowIsolated, int32_t dumpPriority) override { + return binder::Status::fromStatusT( + innerSm.addService(String16(name.c_str()), service, allowIsolated, dumpPriority)); + } + + FakeServiceManager innerSm; +}; + +class LibbinderCacheTest : public ::testing::Test { +protected: + void SetUp() override { + sp<MockAidlServiceManager> sm = sp<MockAidlServiceManager>::make(); + mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(sm); + } + + void TearDown() override {} + +public: + void cacheAndConfirmCacheHit(const sp<IBinder>& binder1, const sp<IBinder>& binder2) { + // Add a service + EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder1)); + // Get the service. This caches it. + sp<IBinder> result = mServiceManager->checkService(kCachedServiceName); + ASSERT_EQ(binder1, result); + + // Add the different binder and replace the service. + // The cache should still hold the original binder. + EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder2)); + + result = mServiceManager->checkService(kCachedServiceName); + if (kUseLibbinderCache) { + // If cache is enabled, we should get the binder to Service Manager. + EXPECT_EQ(binder1, result); + } else { + // If cache is disabled, then we should get the newer binder + EXPECT_EQ(binder2, result); + } + } + + sp<android::IServiceManager> mServiceManager; +}; + +TEST_F(LibbinderCacheTest, AddLocalServiceAndConfirmCacheHit) { + sp<IBinder> binder1 = sp<BBinder>::make(); + sp<IBinder> binder2 = sp<BBinder>::make(); + + cacheAndConfirmCacheHit(binder1, binder2); +} + +TEST_F(LibbinderCacheTest, AddRemoteServiceAndConfirmCacheHit) { + sp<IBinder> binder1 = defaultServiceManager()->checkService(kServerName); + ASSERT_NE(binder1, nullptr); + sp<IBinder> binder2 = IInterface::asBinder(mServiceManager); + + cacheAndConfirmCacheHit(binder1, binder2); +} + +TEST_F(LibbinderCacheTest, RemoveFromCacheOnServerDeath) { + sp<IBinder> binder1 = defaultServiceManager()->checkService(kServerName); + FooBar foo = FooBar(); + + EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder1)); + + // Check Service, this caches the binder + sp<IBinder> result = mServiceManager->checkService(kCachedServiceName); + ASSERT_EQ(binder1, result); + + // Kill the server, this should remove from cache. + pid_t pid; + ASSERT_EQ(OK, binder1->getDebugPid(&pid)); + foo.killServer(binder1); + system(("kill -9 " + std::to_string(pid)).c_str()); + + sp<IBinder> binder2 = sp<BBinder>::make(); + + // Add new service with the same name. + // This will replace the service in FakeServiceManager. + EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder2)); + + // Confirm that new service is returned instead of old. + sp<IBinder> result2 = mServiceManager->checkService(kCachedServiceName); + ASSERT_EQ(binder2, result2); +} + +TEST_F(LibbinderCacheTest, NullBinderNotCached) { + sp<IBinder> binder1 = nullptr; + sp<IBinder> binder2 = sp<BBinder>::make(); + + // Check for a cacheble service which isn't registered. + // FakeServiceManager should return nullptr. + // This shouldn't be cached. + sp<IBinder> result = mServiceManager->checkService(kCachedServiceName); + ASSERT_EQ(binder1, result); + + // Add the same service + EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder2)); + + // This should return the newly added service. + result = mServiceManager->checkService(kCachedServiceName); + EXPECT_EQ(binder2, result); +} + +TEST_F(LibbinderCacheTest, DoNotCacheServiceNotInList) { + sp<IBinder> binder1 = sp<BBinder>::make(); + sp<IBinder> binder2 = sp<BBinder>::make(); + String16 serviceName = String16("NewLibbinderCacheTest"); + // Add a service + EXPECT_EQ(OK, mServiceManager->addService(serviceName, binder1)); + // Get the service. This shouldn't caches it. + sp<IBinder> result = mServiceManager->checkService(serviceName); + ASSERT_EQ(binder1, result); + + // Add the different binder and replace the service. + EXPECT_EQ(OK, mServiceManager->addService(serviceName, binder2)); + + // Confirm that we get the new service + result = mServiceManager->checkService(serviceName); + EXPECT_EQ(binder2, result); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + + // Start a FooBar service and add it to the servicemanager. + sp<IBinder> server = new FooBar(); + defaultServiceManager()->addService(kServerName, server); + + IPCThreadState::self()->joinThreadPool(true); + exit(1); // should not reach + } + + status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(3); + ProcessState::self()->startThreadPool(); + CHECK_EQ(ProcessState::self()->isThreadPoolStarted(), true); + CHECK_GT(ProcessState::self()->getThreadPoolMaxTotalThreadCount(), 0); + + auto binder = defaultServiceManager()->waitForService(kServerName); + CHECK_NE(nullptr, binder.get()); + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 3038de9a41..11150bcd77 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -46,6 +46,13 @@ #include "binderRpcTestCommon.h" #include "binderRpcTestFixture.h" +// TODO need to add IServiceManager.cpp/.h to libbinder_no_kernel +#ifdef BINDER_WITH_KERNEL_IPC +#include "android-base/logging.h" +#include "android/binder_manager.h" +#include "android/binder_rpc.h" +#endif // BINDER_WITH_KERNEL_IPC + using namespace std::chrono_literals; using namespace std::placeholders; using android::binder::borrowed_fd; @@ -365,26 +372,57 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( session->setMaxOutgoingConnections(options.numOutgoingConnections); session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); + sockaddr_storage addr{}; + socklen_t addrLen = 0; + switch (socketType) { - case SocketType::PRECONNECTED: + case SocketType::PRECONNECTED: { + sockaddr_un addr_un{}; + addr_un.sun_family = AF_UNIX; + strcpy(addr_un.sun_path, serverConfig.addr.c_str()); + addr = *reinterpret_cast<sockaddr_storage*>(&addr_un); + addrLen = sizeof(sockaddr_un); + status = session->setupPreconnectedClient({}, [=]() { return connectTo(UnixSocketAddress(serverConfig.addr.c_str())); }); - break; + } break; case SocketType::UNIX_RAW: - case SocketType::UNIX: + case SocketType::UNIX: { + sockaddr_un addr_un{}; + addr_un.sun_family = AF_UNIX; + strcpy(addr_un.sun_path, serverConfig.addr.c_str()); + addr = *reinterpret_cast<sockaddr_storage*>(&addr_un); + addrLen = sizeof(sockaddr_un); + status = session->setupUnixDomainClient(serverConfig.addr.c_str()); - break; + } break; case SocketType::UNIX_BOOTSTRAP: status = session->setupUnixDomainSocketBootstrapClient( unique_fd(dup(bootstrapClientFd.get()))); break; - case SocketType::VSOCK: + case SocketType::VSOCK: { + sockaddr_vm addr_vm{ + .svm_family = AF_VSOCK, + .svm_port = static_cast<unsigned int>(serverInfo.port), + .svm_cid = VMADDR_CID_LOCAL, + }; + addr = *reinterpret_cast<sockaddr_storage*>(&addr_vm); + addrLen = sizeof(sockaddr_vm); + status = session->setupVsockClient(VMADDR_CID_LOCAL, serverInfo.port); - break; - case SocketType::INET: - status = session->setupInetClient("127.0.0.1", serverInfo.port); - break; + } break; + case SocketType::INET: { + const std::string ip_addr = "127.0.0.1"; + sockaddr_in addr_in{}; + addr_in.sin_family = AF_INET; + addr_in.sin_port = htons(serverInfo.port); + inet_aton(ip_addr.c_str(), &addr_in.sin_addr); + addr = *reinterpret_cast<sockaddr_storage*>(&addr_in); + addrLen = sizeof(sockaddr_in); + + status = session->setupInetClient(ip_addr.c_str(), serverInfo.port); + } break; case SocketType::TIPC: status = session->setupPreconnectedClient({}, [=]() { #ifdef BINDER_RPC_TO_TRUSTY_TEST @@ -413,7 +451,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( break; } LOG_ALWAYS_FATAL_IF(status != OK, "Could not connect: %s", statusToString(status).c_str()); - ret->sessions.push_back({session, session->getRootObject()}); + ret->sessions.push_back({session, session->getRootObject(), addr, addrLen}); } return ret; } @@ -423,7 +461,7 @@ TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { GTEST_SKIP() << "This test requires multiple threads"; } - constexpr size_t kNumThreads = 10; + constexpr size_t kNumThreads = 5; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); @@ -468,11 +506,11 @@ static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCall EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs); - // Potential flake, but make sure calls are handled in parallel. Due - // to past flakes, this only checks that the amount of time taken has - // some parallelism. Other tests such as ThreadPoolGreaterThanEqualRequested - // check this more exactly. - EXPECT_LE(epochMsAfter, epochMsBefore + (numCalls - 1) * sleepMs); + // b/272429574, b/365294257 + // This flakes too much to test. Parallelization is tested + // in ThreadPoolGreaterThanEqualRequested and other tests. + // Test to make sure calls are handled in parallel. + // EXPECT_LE(epochMsAfter, epochMsBefore + (numCalls - 1) * sleepMs); } TEST_P(BinderRpc, ThreadPoolOverSaturated) { @@ -484,8 +522,7 @@ TEST_P(BinderRpc, ThreadPoolOverSaturated) { constexpr size_t kNumCalls = kNumThreads + 3; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); - // b/272429574 - below 500ms, the test fails - testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/); + testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 200 /*ms*/); } TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { @@ -499,8 +536,7 @@ TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { auto proc = createRpcTestSocketServerProcess( {.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections}); - // b/272429574 - below 500ms, the test fails - testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/); + testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 200 /*ms*/); } TEST_P(BinderRpc, ThreadingStressTest) { @@ -1127,6 +1163,434 @@ TEST_P(BinderRpc, Fds) { ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?"); } +// TODO need to add IServiceManager.cpp/.h to libbinder_no_kernel +#ifdef BINDER_WITH_KERNEL_IPC + +class BinderRpcAccessor : public BinderRpc { + void SetUp() override { + if (serverSingleThreaded()) { + // This blocks on android::FdTrigger::triggerablePoll when attempting to set + // up the client RpcSession + GTEST_SKIP() << "Accessors are not supported for single threaded libbinder"; + } + if (rpcSecurity() == RpcSecurity::TLS) { + GTEST_SKIP() << "Accessors are not supported with TLS"; + // ... for now + } + + if (socketType() == SocketType::UNIX_BOOTSTRAP) { + GTEST_SKIP() << "Accessors do not support UNIX_BOOTSTRAP because no connection " + "information is known"; + } + if (socketType() == SocketType::TIPC) { + GTEST_SKIP() << "Accessors do not support TIPC because the socket transport is not " + "known in libbinder"; + } + BinderRpc::SetUp(); + } +}; + +inline void waitForExtraSessionCleanup(const BinderRpcTestProcessSession& proc) { + // Need to give the server some time to delete its RpcSession after our last + // reference is dropped, closing the connection. Check for up to 1 second, + // every 10 ms. + for (size_t i = 0; i < 100; i++) { + std::vector<int32_t> remoteCounts; + EXPECT_OK(proc.rootIface->countBinders(&remoteCounts)); + // We exect the original binder to still be alive, we just want to wait + // for this extra session to be cleaned up. + if (remoteCounts.size() == proc.proc->sessions.size()) break; + usleep(10000); + } +} + +TEST_P(BinderRpcAccessor, InjectAndGetServiceHappyPath) { + constexpr size_t kNumThreads = 10; + const String16 kInstanceName("super.cool.service/better_than_default"); + + auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + auto receipt = addAccessorProvider( + {String8(kInstanceName).c_str()}, [&](const String16& name) -> sp<IBinder> { + return createAccessor(name, + [&](const String16& name, sockaddr* outAddr, + socklen_t addrSize) -> status_t { + if (outAddr == nullptr || + addrSize < proc.proc->sessions[0].addrLen) { + return BAD_VALUE; + } + if (name == kInstanceName) { + if (proc.proc->sessions[0].addr.ss_family == + AF_UNIX) { + sockaddr_un* un = reinterpret_cast<sockaddr_un*>( + &proc.proc->sessions[0].addr); + ALOGE("inside callback: %s", un->sun_path); + } + std::memcpy(outAddr, &proc.proc->sessions[0].addr, + proc.proc->sessions[0].addrLen); + return OK; + } + return NAME_NOT_FOUND; + }); + }); + + EXPECT_FALSE(receipt.expired()); + + sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName); + sp<IBinderRpcTest> service = checked_interface_cast<IBinderRpcTest>(binder); + EXPECT_NE(service, nullptr); + + sp<IBinder> out; + EXPECT_OK(service->repeatBinder(binder, &out)); + EXPECT_EQ(binder, out); + + out.clear(); + binder.clear(); + service.clear(); + + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); + + waitForExtraSessionCleanup(proc); +} + +TEST_P(BinderRpcAccessor, InjectNoAccessorProvided) { + const String16 kInstanceName("doesnt_matter_nothing_checks"); + + bool isProviderDeleted = false; + + auto receipt = addAccessorProvider({String8(kInstanceName).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + + sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName); + EXPECT_EQ(binder, nullptr); + + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + +TEST_P(BinderRpcAccessor, InjectDuplicateAccessorProvider) { + const String16 kInstanceName("super.cool.service/better_than_default"); + const String16 kInstanceName2("super.cool.service/better_than_default2"); + + auto receipt = + addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_FALSE(receipt.expired()); + // reject this because it's associated with an already used instance name + auto receipt2 = addAccessorProvider({String8(kInstanceName).c_str()}, + [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_TRUE(receipt2.expired()); + + // the first provider should still be usable + sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName); + EXPECT_EQ(binder, nullptr); + + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + +TEST_P(BinderRpcAccessor, InjectAccessorProviderNoInstance) { + auto receipt = addAccessorProvider({}, [&](const String16&) -> sp<IBinder> { return nullptr; }); + EXPECT_TRUE(receipt.expired()); +} + +TEST_P(BinderRpcAccessor, InjectNoSockaddrProvided) { + constexpr size_t kNumThreads = 10; + const String16 kInstanceName("super.cool.service/better_than_default"); + + auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + bool isProviderDeleted = false; + bool isAccessorDeleted = false; + + auto receipt = addAccessorProvider({String8(kInstanceName).c_str()}, + [&](const String16& name) -> sp<IBinder> { + return createAccessor(name, + [&](const String16&, sockaddr*, + socklen_t) -> status_t { + // don't fill in outAddr + return NAME_NOT_FOUND; + }); + }); + + EXPECT_FALSE(receipt.expired()); + + sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName); + EXPECT_EQ(binder, nullptr); + + status_t status = removeAccessorProvider(receipt); + EXPECT_EQ(status, OK); +} + +constexpr const char* kARpcInstance = "some.instance.name.IFoo/default"; +const char* kARpcSupportedServices[] = { + kARpcInstance, +}; +const uint32_t kARpcNumSupportedServices = 1; + +struct ConnectionInfoData { + sockaddr_storage addr; + socklen_t len; + bool* isDeleted; + ~ConnectionInfoData() { + if (isDeleted) *isDeleted = true; + } +}; + +struct AccessorProviderData { + sockaddr_storage addr; + socklen_t len; + bool* isDeleted; + ~AccessorProviderData() { + if (isDeleted) *isDeleted = true; + } +}; + +void accessorProviderDataOnDelete(void* data) { + delete reinterpret_cast<AccessorProviderData*>(data); +} +void infoProviderDataOnDelete(void* data) { + delete reinterpret_cast<ConnectionInfoData*>(data); +} + +ABinderRpc_ConnectionInfo* infoProvider(const char* instance, void* cookie) { + if (instance == nullptr || cookie == nullptr) return nullptr; + ConnectionInfoData* data = reinterpret_cast<ConnectionInfoData*>(cookie); + return ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&data->addr), data->len); +} + +ABinderRpc_Accessor* getAccessor(const char* instance, void* cookie) { + if (instance == nullptr || cookie == nullptr) return nullptr; + if (0 != strcmp(instance, kARpcInstance)) return nullptr; + + AccessorProviderData* data = reinterpret_cast<AccessorProviderData*>(cookie); + + ConnectionInfoData* info = new ConnectionInfoData{ + .addr = data->addr, + .len = data->len, + .isDeleted = nullptr, + }; + + return ABinderRpc_Accessor_new(instance, infoProvider, info, infoProviderDataOnDelete); +} + +class BinderARpcNdk : public ::testing::Test {}; + +TEST_F(BinderARpcNdk, ARpcProviderNewDelete) { + bool isDeleted = false; + + AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted}; + + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, + kARpcNumSupportedServices, data, + accessorProviderDataOnDelete); + + ASSERT_NE(provider, nullptr); + EXPECT_FALSE(isDeleted); + + ABinderRpc_unregisterAccessorProvider(provider); + + EXPECT_TRUE(isDeleted); +} + +TEST_F(BinderARpcNdk, ARpcProviderDuplicateInstance) { + const char* instance = "some.instance.name.IFoo/default"; + const uint32_t numInstances = 2; + const char* instances[numInstances] = { + instance, + "some.other.instance/default", + }; + + bool isDeleted = false; + + AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted}; + + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, instances, numInstances, data, + accessorProviderDataOnDelete); + + ASSERT_NE(provider, nullptr); + EXPECT_FALSE(isDeleted); + + const uint32_t numInstances2 = 1; + const char* instances2[numInstances2] = { + instance, + }; + bool isDeleted2 = false; + AccessorProviderData* data2 = new AccessorProviderData{{}, 0, &isDeleted2}; + ABinderRpc_AccessorProvider* provider2 = + ABinderRpc_registerAccessorProvider(getAccessor, instances2, numInstances2, data2, + accessorProviderDataOnDelete); + + EXPECT_EQ(provider2, nullptr); + // If it fails to be registered, the data is still cleaned up with + // accessorProviderDataOnDelete + EXPECT_TRUE(isDeleted2); + + ABinderRpc_unregisterAccessorProvider(provider); + + EXPECT_TRUE(isDeleted); +} + +TEST_F(BinderARpcNdk, ARpcProviderRegisterNoInstance) { + const uint32_t numInstances = 0; + const char* instances[numInstances] = {}; + + bool isDeleted = false; + AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted}; + + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, instances, numInstances, data, + accessorProviderDataOnDelete); + ASSERT_EQ(provider, nullptr); +} + +TEST_F(BinderARpcNdk, ARpcAccessorNewDelete) { + bool isDeleted = false; + + ConnectionInfoData* data = new ConnectionInfoData{{}, 0, &isDeleted}; + + ABinderRpc_Accessor* accessor = + ABinderRpc_Accessor_new("gshoe_service", infoProvider, data, infoProviderDataOnDelete); + ASSERT_NE(accessor, nullptr); + EXPECT_FALSE(isDeleted); + + ABinderRpc_Accessor_delete(accessor); + EXPECT_TRUE(isDeleted); +} + +TEST_F(BinderARpcNdk, ARpcConnectionInfoNewDelete) { + sockaddr_vm addr{ + .svm_family = AF_VSOCK, + .svm_port = VMADDR_PORT_ANY, + .svm_cid = VMADDR_CID_ANY, + }; + + ABinderRpc_ConnectionInfo* info = + ABinderRpc_ConnectionInfo_new(reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr_vm)); + EXPECT_NE(info, nullptr); + + ABinderRpc_ConnectionInfo_delete(info); +} + +TEST_F(BinderARpcNdk, ARpcAsFromBinderAsBinder) { + bool isDeleted = false; + + ConnectionInfoData* data = new ConnectionInfoData{{}, 0, &isDeleted}; + + ABinderRpc_Accessor* accessor = + ABinderRpc_Accessor_new("gshoe_service", infoProvider, data, infoProviderDataOnDelete); + ASSERT_NE(accessor, nullptr); + EXPECT_FALSE(isDeleted); + + { + ndk::SpAIBinder binder = ndk::SpAIBinder(ABinderRpc_Accessor_asBinder(accessor)); + EXPECT_NE(binder.get(), nullptr); + + ABinderRpc_Accessor* accessor2 = + ABinderRpc_Accessor_fromBinder("wrong_service_name", binder.get()); + // The API checks for the expected service name that is associated with + // the accessor! + EXPECT_EQ(accessor2, nullptr); + + accessor2 = ABinderRpc_Accessor_fromBinder("gshoe_service", binder.get()); + EXPECT_NE(accessor2, nullptr); + + // this is a new ABinderRpc_Accessor object that wraps the underlying + // libbinder object. + EXPECT_NE(accessor, accessor2); + + ndk::SpAIBinder binder2 = ndk::SpAIBinder(ABinderRpc_Accessor_asBinder(accessor2)); + EXPECT_EQ(binder.get(), binder2.get()); + + ABinderRpc_Accessor_delete(accessor2); + } + + EXPECT_FALSE(isDeleted); + ABinderRpc_Accessor_delete(accessor); + EXPECT_TRUE(isDeleted); +} + +TEST_F(BinderARpcNdk, ARpcRequireProviderOnDeleteCallback) { + EXPECT_EQ(nullptr, + ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, + kARpcNumSupportedServices, + reinterpret_cast<void*>(1), nullptr)); +} + +TEST_F(BinderARpcNdk, ARpcRequireInfoOnDeleteCallback) { + EXPECT_EQ(nullptr, + ABinderRpc_Accessor_new("the_best_service_name", infoProvider, + reinterpret_cast<void*>(1), nullptr)); +} + +TEST_F(BinderARpcNdk, ARpcNoDataNoProviderOnDeleteCallback) { + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, + kARpcNumSupportedServices, nullptr, nullptr); + ASSERT_NE(nullptr, provider); + ABinderRpc_unregisterAccessorProvider(provider); +} + +TEST_F(BinderARpcNdk, ARpcNoDataNoInfoOnDeleteCallback) { + ABinderRpc_Accessor* accessor = + ABinderRpc_Accessor_new("the_best_service_name", infoProvider, nullptr, nullptr); + ASSERT_NE(nullptr, accessor); + ABinderRpc_Accessor_delete(accessor); +} + +TEST_F(BinderARpcNdk, ARpcDoubleRemoveProvider) { + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, + kARpcNumSupportedServices, nullptr, nullptr); + ASSERT_NE(nullptr, provider); + ABinderRpc_unregisterAccessorProvider(provider); + EXPECT_DEATH(ABinderRpc_unregisterAccessorProvider(provider), " was already unregistered"); +} + +TEST_F(BinderARpcNdk, ARpcNullArgs_ConnectionInfo_new) { + sockaddr_storage addr; + EXPECT_EQ(nullptr, ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&addr), 0)); +} + +TEST_P(BinderRpcAccessor, ARpcGetService) { + constexpr size_t kNumThreads = 10; + bool isDeleted = false; + + auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); + EXPECT_EQ(OK, proc.rootBinder->pingBinder()); + + AccessorProviderData* data = + new AccessorProviderData{proc.proc->sessions[0].addr, proc.proc->sessions[0].addrLen, + &isDeleted}; + + ABinderRpc_AccessorProvider* provider = + ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices, + kARpcNumSupportedServices, data, + accessorProviderDataOnDelete); + + EXPECT_NE(provider, nullptr); + EXPECT_FALSE(isDeleted); + + { + ndk::SpAIBinder binder = ndk::SpAIBinder(AServiceManager_checkService(kARpcInstance)); + ASSERT_NE(binder.get(), nullptr); + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); + } + + ABinderRpc_unregisterAccessorProvider(provider); + EXPECT_TRUE(isDeleted); + + waitForExtraSessionCleanup(proc); +} + +#endif // BINDER_WITH_KERNEL_IPC + #ifdef BINDER_RPC_TO_TRUSTY_TEST static std::vector<BinderRpc::ParamType> getTrustyBinderRpcParams() { @@ -1315,6 +1779,11 @@ static std::vector<BinderRpc::ParamType> getBinderRpcParams() { INSTANTIATE_TEST_SUITE_P(PerSocket, BinderRpc, ::testing::ValuesIn(getBinderRpcParams()), BinderRpc::PrintParamInfo); +#ifdef BINDER_WITH_KERNEL_IPC +INSTANTIATE_TEST_SUITE_P(PerSocket, BinderRpcAccessor, ::testing::ValuesIn(getBinderRpcParams()), + BinderRpc::PrintParamInfo); +#endif // BINDER_WITH_KERNEL_IPC + class BinderRpcServerRootObject : public ::testing::TestWithParam<std::tuple<bool, bool, RpcSecurity>> {}; diff --git a/libs/binder/tests/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h index 2c9646b30e..c8a8acc0a0 100644 --- a/libs/binder/tests/binderRpcTestFixture.h +++ b/libs/binder/tests/binderRpcTestFixture.h @@ -35,6 +35,12 @@ public: struct SessionInfo { sp<RpcSession> session; sp<IBinder> root; +// Trusty defines its own socket APIs in trusty_ipc.h but doesn't include +// sockaddr types. +#ifndef __TRUSTY__ + sockaddr_storage addr; + socklen_t addrLen; +#endif }; // client session objects associated with other process diff --git a/libs/bufferstreams/rust/src/stream_config.rs b/libs/bufferstreams/rust/src/stream_config.rs index 454bdf144e..8288f9f3cf 100644 --- a/libs/bufferstreams/rust/src/stream_config.rs +++ b/libs/bufferstreams/rust/src/stream_config.rs @@ -32,10 +32,23 @@ pub struct StreamConfig { pub stride: u32, } +impl From<StreamConfig> for HardwareBufferDescription { + fn from(config: StreamConfig) -> Self { + HardwareBufferDescription::new( + config.width, + config.height, + config.layers, + config.format, + config.usage, + config.stride, + ) + } +} + impl StreamConfig { /// Tries to create a new HardwareBuffer from settings in a [StreamConfig]. pub fn create_hardware_buffer(&self) -> Option<HardwareBuffer> { - HardwareBuffer::new(self.width, self.height, self.layers, self.format, self.usage) + HardwareBuffer::new(&(*self).into()) } } @@ -59,9 +72,10 @@ mod test { assert!(maybe_buffer.is_some()); let buffer = maybe_buffer.unwrap(); - assert_eq!(config.width, buffer.width()); - assert_eq!(config.height, buffer.height()); - assert_eq!(config.format, buffer.format()); - assert_eq!(config.usage, buffer.usage()); + let description = buffer.description(); + assert_eq!(config.width, description.width()); + assert_eq!(config.height, description.height()); + assert_eq!(config.format, description.format()); + assert_eq!(config.usage, description.usage()); } } diff --git a/libs/debugstore/OWNERS b/libs/debugstore/OWNERS index 428a1a2215..c8e22b70ff 100644 --- a/libs/debugstore/OWNERS +++ b/libs/debugstore/OWNERS @@ -1,3 +1,2 @@ benmiles@google.com -gaillard@google.com mohamadmahmoud@google.com diff --git a/libs/debugstore/rust/Android.bp b/libs/debugstore/rust/Android.bp index 55ba3c32d1..9475333a72 100644 --- a/libs/debugstore/rust/Android.bp +++ b/libs/debugstore/rust/Android.bp @@ -23,7 +23,6 @@ rust_defaults { rustlibs: [ "libcrossbeam_queue", "libparking_lot", - "libonce_cell", "libcxx", ], shared_libs: ["libutils"], diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs index 1dfa512151..6bf79d4e57 100644 --- a/libs/debugstore/rust/src/core.rs +++ b/libs/debugstore/rust/src/core.rs @@ -17,12 +17,14 @@ use super::event::Event; use super::event_type::EventType; use super::storage::Storage; use crate::cxxffi::uptimeMillis; -use once_cell::sync::Lazy; use std::fmt; -use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{ + atomic::{AtomicU64, Ordering}, + LazyLock, +}; // Lazily initialized static instance of DebugStore. -static INSTANCE: Lazy<DebugStore> = Lazy::new(DebugStore::new); +static INSTANCE: LazyLock<DebugStore> = LazyLock::new(DebugStore::new); /// The `DebugStore` struct is responsible for managing debug events and data. pub struct DebugStore { diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index f8e3fd0a5c..25e6a52ed1 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -22,11 +22,14 @@ #include <com_android_graphics_libgui_flags.h> #include <cutils/atomic.h> +#include <ftl/fake_guard.h> #include <gui/BLASTBufferQueue.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> #include <gui/FrameRateUtils.h> #include <gui/GLConsumer.h> @@ -74,6 +77,12 @@ namespace android { std::unique_lock _lock{mutex}; \ base::ScopedLockAssertion assumeLocked(mutex); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) +static ReleaseBufferCallback EMPTY_RELEASE_CALLBACK = + [](const ReleaseCallbackId&, const sp<Fence>& /*releaseFence*/, + std::optional<uint32_t> /*currentMaxAcquiredBufferCount*/) {}; +#endif + void BLASTBufferItemConsumer::onDisconnect() { Mutex::Autolock lock(mMutex); mPreviouslyConnected = mCurrentlyConnected; @@ -215,6 +224,12 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinati }, this); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> bufferReleaseConsumer; + gui::BufferReleaseChannel::open(mName, bufferReleaseConsumer, mBufferReleaseProducer); + mBufferReleaseReader = std::make_shared<BufferReleaseReader>(std::move(bufferReleaseConsumer)); +#endif + BQA_LOGV("BLASTBufferQueue created"); } @@ -244,6 +259,9 @@ BLASTBufferQueue::~BLASTBufferQueue() { void BLASTBufferQueue::onFirstRef() { // safe default, most producers are expected to override this mProducer->setMaxDequeuedBufferCount(2); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + mBufferReleaseThread.start(sp<BLASTBufferQueue>::fromExisting(this)); +#endif } void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, @@ -269,6 +287,9 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, if (surfaceControlChanged) { t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer); +#endif applyTransaction = true; } mTransformHint = mSurfaceControl->getTransformHint(); @@ -386,6 +407,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.latchTime, stat.frameEventStats.dequeueReadyTime); } +#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) auto currFrameNumber = stat.frameEventStats.frameNumber; std::vector<ReleaseCallbackId> staleReleases; for (const auto& [key, value]: mSubmitted) { @@ -401,6 +423,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.currentMaxAcquiredBufferCount, true /* fakeRelease */); } +#endif } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } @@ -411,6 +434,15 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence } } +void BLASTBufferQueue::flushShadowQueue() { + BQA_LOGV("flushShadowQueue"); + int numFramesToFlush = mNumFrameAvailable; + while (numFramesToFlush > 0) { + acquireNextBufferLocked(std::nullopt); + numFramesToFlush--; + } +} + // Unlike transactionCallbackThunk the release buffer callback does not extend the life of the // BBQ. This is because if the BBQ is destroyed, then the buffers will be released by the client. // So we pass in a weak pointer to the BBQ and if it still alive, then we release the buffer. @@ -428,15 +460,6 @@ ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() { }; } -void BLASTBufferQueue::flushShadowQueue() { - BQA_LOGV("flushShadowQueue"); - int numFramesToFlush = mNumFrameAvailable; - while (numFramesToFlush > 0) { - acquireNextBufferLocked(std::nullopt); - numFramesToFlush--; - } -} - void BLASTBufferQueue::releaseBufferCallback( const ReleaseCallbackId& id, const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount) { @@ -504,13 +527,7 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, callbackId.to_string().c_str()); return; } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - if (!it->second.disconnectedAfterAcquired) { - mNumAcquired--; - } -#else mNumAcquired--; -#endif BBQ_TRACE("frame=%" PRIu64, callbackId.framenumber); BQA_LOGV("released %s", callbackId.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseFence); @@ -561,7 +578,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( applyTransaction = false; } - BLASTBufferItem bufferItem; + BufferItem bufferItem; status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); @@ -617,7 +634,12 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + ReleaseBufferCallback releaseBufferCallback = + applyTransaction ? EMPTY_RELEASE_CALLBACK : makeReleaseBufferCallbackThunk(); +#else auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); +#endif sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; @@ -767,9 +789,6 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { } // add to shadow queue -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - mNumDequeued--; -#endif mNumFrameAvailable++; if (waitForTransactionCallback && mNumFrameAvailable >= 2) { acquireAndReleaseBuffer(); @@ -824,17 +843,8 @@ void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) { }; void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { - { - std::lock_guard _lock{mTimestampMutex}; - mDequeueTimestamps.erase(bufferId); - } - -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - { - std::lock_guard lock{mMutex}; - mNumDequeued--; - } -#endif + std::lock_guard _lock{mTimestampMutex}; + mDequeueTimestamps.erase(bufferId); } bool BLASTBufferQueue::syncNextTransaction( @@ -1114,6 +1124,17 @@ public: AsyncWorker::getInstance().post( [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); }); } + + void onBufferDetached(int slot) override { + AsyncWorker::getInstance().post( + [listener = mListener, slot = slot]() { listener->onBufferDetached(slot); }); + } + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) + void onBufferAttached() override { + AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferAttached(); }); + } +#endif }; // Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls @@ -1134,116 +1155,6 @@ public: producerControlledByApp, output); } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - status_t disconnect(int api, DisconnectMode mode) override { - sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); - if (!bbq) { - return BufferQueueProducer::disconnect(api, mode); - } - - std::lock_guard lock{bbq->mMutex}; - if (status_t status = BufferQueueProducer::disconnect(api, mode); status != OK) { - return status; - } - - // We need to reset dequeued and acquired counts because BufferQueueProducer::disconnect - // calls BufferQueueCore::freeAllBuffersLocked which frees all dequeued and acquired - // buffers. We don't reset mNumFrameAvailable because these buffers are still available - // in BufferItemConsumer. - bbq->mNumDequeued = 0; - bbq->mNumAcquired = 0; - // SurfaceFlinger sends release callbacks for buffers that have been acquired after a - // disconnect. We set disconnectedAfterAcquired to true so that we can ignore any stale - // releases that come in after the producer is disconnected. Otherwise, releaseBuffer will - // decrement mNumAcquired for a buffer that was acquired before we reset mNumAcquired to - // zero. - for (auto& [releaseId, bufferItem] : bbq->mSubmitted) { - bufferItem.disconnectedAfterAcquired = true; - } - - return OK; - } - - status_t setAsyncMode(bool asyncMode) override { - if (status_t status = BufferQueueProducer::setAsyncMode(asyncMode); status != OK) { - return status; - } - - sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); - if (!bbq) { - return OK; - } - - { - std::lock_guard lock{bbq->mMutex}; - bbq->mAsyncMode = asyncMode; - } - - return OK; - } - - status_t setSharedBufferMode(bool sharedBufferMode) override { - if (status_t status = BufferQueueProducer::setSharedBufferMode(sharedBufferMode); - status != OK) { - return status; - } - - sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); - if (!bbq) { - return OK; - } - - { - std::lock_guard lock{bbq->mMutex}; - bbq->mSharedBufferMode = sharedBufferMode; - } - - return OK; - } - - status_t detachBuffer(int slot) override { - if (status_t status = BufferQueueProducer::detachBuffer(slot); status != OK) { - return status; - } - - sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); - if (!bbq) { - return OK; - } - - { - std::lock_guard lock{bbq->mMutex}; - bbq->mNumDequeued--; - } - - return OK; - } - - status_t dequeueBuffer(int* outSlot, sp<Fence>* outFence, uint32_t width, uint32_t height, - PixelFormat format, uint64_t usage, uint64_t* outBufferAge, - FrameEventHistoryDelta* outTimestamps) override { - sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); - if (!bbq) { - return BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format, - usage, outBufferAge, outTimestamps); - } - - { - std::lock_guard lock{bbq->mMutex}; - bbq->mNumDequeued++; - } - - status_t status = - BufferQueueProducer::dequeueBuffer(outSlot, outFence, width, height, format, usage, - outBufferAge, outTimestamps); - if (status < 0) { - std::lock_guard lock{bbq->mMutex}; - bbq->mNumDequeued--; - } - return status; - } -#endif - // We want to resize the frame history when changing the size of the buffer queue status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override { int maxBufferCount; @@ -1266,13 +1177,6 @@ public: bbq->resizeFrameEventHistory(newFrameHistorySize); } -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - { - std::lock_guard lock{bbq->mMutex}; - bbq->mMaxDequeuedBuffers = maxDequeuedBufferCount; - } -#endif - return OK; } @@ -1359,7 +1263,125 @@ bool BLASTBufferQueue::isSameSurfaceControl(const sp<SurfaceControl>& surfaceCon void BLASTBufferQueue::setTransactionHangCallback( std::function<void(const std::string&)> callback) { std::lock_guard _lock{mMutex}; - mTransactionHangCallback = callback; + mTransactionHangCallback = std::move(callback); +} + +void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { + std::lock_guard _lock{mMutex}; + mApplyToken = std::move(applyToken); +} + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + +BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader( + std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> endpoint) + : mEndpoint{std::move(endpoint)} { + mEpollFd = android::base::unique_fd{epoll_create1(0)}; + LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(), + "Failed to create buffer release epoll file descriptor. errno=%d " + "message='%s'", + errno, strerror(errno)); + + epoll_event registerEndpointFd{}; + registerEndpointFd.events = EPOLLIN; + registerEndpointFd.data.fd = mEndpoint->getFd(); + status_t status = + epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mEndpoint->getFd(), ®isterEndpointFd); + LOG_ALWAYS_FATAL_IF(status == -1, + "Failed to register buffer release consumer file descriptor with epoll. " + "errno=%d message='%s'", + errno, strerror(errno)); + + mEventFd = android::base::unique_fd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + LOG_ALWAYS_FATAL_IF(!mEventFd.ok(), + "Failed to create buffer release event file descriptor. errno=%d " + "message='%s'", + errno, strerror(errno)); + + epoll_event registerEventFd{}; + registerEventFd.events = EPOLLIN; + registerEventFd.data.fd = mEventFd.get(); + status = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mEventFd.get(), ®isterEventFd); + LOG_ALWAYS_FATAL_IF(status == -1, + "Failed to register buffer release event file descriptor with epoll. " + "errno=%d message='%s'", + errno, strerror(errno)); +} + +BLASTBufferQueue::BufferReleaseReader& BLASTBufferQueue::BufferReleaseReader::operator=( + BufferReleaseReader&& other) { + if (this != &other) { + ftl::FakeGuard guard{mMutex}; + ftl::FakeGuard otherGuard{other.mMutex}; + mEndpoint = std::move(other.mEndpoint); + mEpollFd = std::move(other.mEpollFd); + mEventFd = std::move(other.mEventFd); + } + return *this; +} + +status_t BLASTBufferQueue::BufferReleaseReader::readBlocking(ReleaseCallbackId& outId, + sp<Fence>& outFence, + uint32_t& outMaxAcquiredBufferCount) { + epoll_event event{}; + while (true) { + int eventCount = epoll_wait(mEpollFd.get(), &event, 1 /* maxevents */, -1 /* timeout */); + if (eventCount == 1) { + break; + } + if (eventCount == -1 && errno != EINTR) { + ALOGE("epoll_wait error while waiting for buffer release. errno=%d message='%s'", errno, + strerror(errno)); + } + } + + if (event.data.fd == mEventFd.get()) { + uint64_t value; + if (read(mEventFd.get(), &value, sizeof(uint64_t)) == -1 && errno != EWOULDBLOCK) { + ALOGE("error while reading from eventfd. errno=%d message='%s'", errno, + strerror(errno)); + } + return WOULD_BLOCK; + } + + std::lock_guard lock{mMutex}; + return mEndpoint->readReleaseFence(outId, outFence, outMaxAcquiredBufferCount); +} + +void BLASTBufferQueue::BufferReleaseReader::interruptBlockingRead() { + uint64_t value = 1; + if (write(mEventFd.get(), &value, sizeof(uint64_t)) == -1) { + ALOGE("failed to notify dequeue event. errno=%d message='%s'", errno, strerror(errno)); + } } +void BLASTBufferQueue::BufferReleaseThread::start(const sp<BLASTBufferQueue>& bbq) { + mRunning = std::make_shared<std::atomic_bool>(true); + mReader = bbq->mBufferReleaseReader; + std::thread([running = mRunning, reader = mReader, weakBbq = wp<BLASTBufferQueue>(bbq)]() { + pthread_setname_np(pthread_self(), "BufferReleaseThread"); + while (*running) { + ReleaseCallbackId id; + sp<Fence> fence; + uint32_t maxAcquiredBufferCount; + if (status_t status = reader->readBlocking(id, fence, maxAcquiredBufferCount); + status != OK) { + continue; + } + sp<BLASTBufferQueue> bbq = weakBbq.promote(); + if (!bbq) { + return; + } + bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount); + } + }).detach(); +} + +BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() { + *mRunning = false; + mReader->interruptBlockingRead(); +} + +#endif + } // namespace android diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index a4d105d320..da74e9cde6 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -45,7 +45,10 @@ #include <system/window.h> +#include <com_android_graphics_libgui_flags.h> + namespace android { +using namespace com::android::graphics::libgui; // Macros for include BufferQueueCore information in log messages #define BQ_LOGV(x, ...) \ @@ -924,6 +927,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, uint64_t currentFrameNumber = 0; BufferItem item; int connectedApi; + bool enableEglCpuThrottling = true; sp<Fence> lastQueuedFence; { // Autolock scope @@ -1097,6 +1101,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, VALIDATE_CONSISTENCY(); connectedApi = mCore->mConnectedApi; + if (flags::bq_producer_throttles_only_async_mode()) { + enableEglCpuThrottling = mCore->mAsyncMode || mCore->mDequeueBufferCannotBlock; + } lastQueuedFence = std::move(mLastQueueBufferFence); mLastQueueBufferFence = std::move(acquireFence); @@ -1142,7 +1149,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, } // Wait without lock held - if (connectedApi == NATIVE_WINDOW_API_EGL) { + if (connectedApi == NATIVE_WINDOW_API_EGL && enableEglCpuThrottling) { // Waiting here allows for two full buffers to be queued but not a // third. In the event that frames take varying time, this makes a // small trade-off in favor of latency rather than throughput. diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp index 7700795e1d..8b9b090496 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp @@ -184,4 +184,10 @@ bool BnProducerListener::needsReleaseNotify() { void BnProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*discardedSlots*/) { } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) +bool BnProducerListener::needsAttachNotify() { + return true; +} +#endif + } // namespace android diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp index 601a5f9b33..2de023e5b2 100644 --- a/libs/gui/ScreenCaptureResults.cpp +++ b/libs/gui/ScreenCaptureResults.cpp @@ -40,6 +40,13 @@ status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const { SAFE_PARCEL(parcel->writeBool, capturedSecureLayers); SAFE_PARCEL(parcel->writeBool, capturedHdrLayers); SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace)); + if (optionalGainMap != nullptr) { + SAFE_PARCEL(parcel->writeBool, true); + SAFE_PARCEL(parcel->write, *optionalGainMap); + } else { + SAFE_PARCEL(parcel->writeBool, false); + } + SAFE_PARCEL(parcel->writeFloat, hdrSdrRatio); return NO_ERROR; } @@ -68,6 +75,14 @@ status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { uint32_t dataspace = 0; SAFE_PARCEL(parcel->readUint32, &dataspace); capturedDataspace = static_cast<ui::Dataspace>(dataspace); + + bool hasGainmap; + SAFE_PARCEL(parcel->readBool, &hasGainmap); + if (hasGainmap) { + optionalGainMap = new GraphicBuffer(); + SAFE_PARCEL(parcel->read, *optionalGainMap); + } + SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio); return NO_ERROR; } diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index c5f9c38ca3..f126c0be2f 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -139,9 +139,9 @@ sp<Surface> SurfaceControl::generateSurfaceLocked() uint32_t ignore; auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow | ISurfaceComposerClient::eOpaque); - mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat, + mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat, flags, mHandle, {}, &ignore); - mBbq = sp<BLASTBufferQueue>::make("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat); + mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat); // This surface is always consumed by SurfaceFlinger, so the // producerControlledByApp value doesn't matter; using false. diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl index 2bbed2b9d6..4920344e0e 100644 --- a/libs/gui/aidl/android/gui/CaptureArgs.aidl +++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl @@ -69,5 +69,10 @@ parcelable CaptureArgs { // exact colorspace is not an appropriate intermediate result. // Note that if the caller is requesting a specific dataspace, this hint does nothing. boolean hintForSeamlessTransition = false; + + // Allows the screenshot to attach a gainmap, which allows for a per-pixel + // transformation of the screenshot to another luminance range, typically + // mapping an SDR base image into HDR. + boolean attachGainmap = false; } diff --git a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl index 97a903515b..f4ef16dc71 100644 --- a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl +++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl @@ -16,4 +16,4 @@ package android.gui; -parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults";
\ No newline at end of file +parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults"; diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 729d46a387..8592cffd15 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -142,7 +142,7 @@ public: * indicates the reason for the hang. */ void setTransactionHangCallback(std::function<void(const std::string&)> callback); - + void setApplyToken(sp<IBinder>); virtual ~BLASTBufferQueue(); void onFirstRef() override; @@ -187,15 +187,6 @@ private: // BufferQueue internally allows 1 more than // the max to be acquired int32_t mMaxAcquiredBuffers GUARDED_BY(mMutex) = 1; -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - int32_t mMaxDequeuedBuffers GUARDED_BY(mMutex) = 1; - static constexpr int32_t kMaxBufferCount = BufferQueueDefs::NUM_BUFFER_SLOTS; - - bool mAsyncMode GUARDED_BY(mMutex) = false; - bool mSharedBufferMode GUARDED_BY(mMutex) = false; - - int32_t mNumDequeued GUARDED_BY(mMutex) = 0; -#endif int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0; int32_t mNumAcquired GUARDED_BY(mMutex) = 0; @@ -204,16 +195,9 @@ private: // latch stale buffers and that we don't wait on barriers from an old producer. uint32_t mProducerId = 0; - class BLASTBufferItem : public BufferItem { - public: - // True if BBQBufferQueueProducer is disconnected after the buffer is acquried but - // before it is released. - bool disconnectedAfterAcquired{false}; - }; - // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the // buffer or the buffer has been presented and a new buffer is ready to be presented. - std::unordered_map<ReleaseCallbackId, BLASTBufferItem, ReleaseBufferCallbackIdHash> mSubmitted + std::unordered_map<ReleaseCallbackId, BufferItem, ReleaseBufferCallbackIdHash> mSubmitted GUARDED_BY(mMutex); // Keep a queue of the released buffers instead of immediately releasing @@ -287,7 +271,7 @@ private: // Queues up transactions using this token in SurfaceFlinger. This prevents queued up // transactions from other parts of the client from blocking this transaction. - const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make(); + sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make(); // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or // we will deadlock. @@ -331,6 +315,51 @@ private: std::function<void(const std::string&)> mTransactionHangCallback; std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); + +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + class BufferReleaseReader { + public: + BufferReleaseReader() = default; + BufferReleaseReader(std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint>); + BufferReleaseReader& operator=(BufferReleaseReader&&); + + // Block until we can read a buffer release message. + // + // Returns: + // * OK if a ReleaseCallbackId and Fence were successfully read. + // * WOULD_BLOCK if the blocking read was interrupted by interruptBlockingRead. + // * UNKNOWN_ERROR if something went wrong. + status_t readBlocking(ReleaseCallbackId& outId, sp<Fence>& outReleaseFence, + uint32_t& outMaxAcquiredBufferCount); + + // Signals the reader's eventfd to wake up any threads waiting on readBlocking. + void interruptBlockingRead(); + + private: + std::mutex mMutex; + std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mEndpoint GUARDED_BY(mMutex); + android::base::unique_fd mEpollFd; + android::base::unique_fd mEventFd; + }; + + // BufferReleaseChannel is used to communicate buffer releases from SurfaceFlinger to + // the client. See BBQBufferQueueProducer::dequeueBuffer for details. + std::shared_ptr<BufferReleaseReader> mBufferReleaseReader; + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; + + class BufferReleaseThread { + public: + BufferReleaseThread() = default; + ~BufferReleaseThread(); + void start(const sp<BLASTBufferQueue>&); + + private: + std::shared_ptr<std::atomic_bool> mRunning; + std::shared_ptr<BufferReleaseReader> mReader; + }; + + BufferReleaseThread mBufferReleaseThread; +#endif }; } // namespace android diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h new file mode 100644 index 0000000000..735375a1e7 --- /dev/null +++ b/libs/gui/include/gui/Flags.h @@ -0,0 +1,24 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <com_android_graphics_libgui_flags.h> + +#define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \ + (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \ + COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) && \ + COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
\ No newline at end of file diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h index 3dcc6b6670..43bf6a7d4b 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h @@ -90,6 +90,9 @@ public: Parcel* reply, uint32_t flags = 0); virtual bool needsReleaseNotify(); virtual void onBuffersDiscarded(const std::vector<int32_t>& slots); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) + virtual bool needsAttachNotify(); +#endif }; #else @@ -103,6 +106,9 @@ public: virtual ~StubProducerListener(); virtual void onBufferReleased() {} virtual bool needsReleaseNotify() { return false; } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK) + virtual bool needsAttachNotify() { return false; } +#endif }; } // namespace android diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h index 6e17791a29..f176f48fb4 100644 --- a/libs/gui/include/gui/ScreenCaptureResults.h +++ b/libs/gui/include/gui/ScreenCaptureResults.h @@ -36,6 +36,11 @@ public: bool capturedSecureLayers{false}; bool capturedHdrLayers{false}; ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB}; + // A gainmap that can be used to "lift" the screenshot into HDR + sp<GraphicBuffer> optionalGainMap; + // HDR/SDR ratio value that fully applies the gainmap. + // Note that we use 1/64 epsilon offsets to eliminate precision issues + float hdrSdrRatio{1.0f}; }; } // namespace android::gui diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index e74f9ad1dc..14a351316d 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -34,8 +34,6 @@ #include <shared_mutex> #include <unordered_set> -#include <com_android_graphics_libgui_flags.h> - namespace android { class GraphicBuffer; diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 7468401b7a..d3f2899ba3 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -98,4 +98,20 @@ flag { description: "Remove usage of IGBPs in the *Processor and Camera3*" bug: "342199002" is_fixed_read_only: true -} # wb_camera3_and_processors
\ No newline at end of file +} # wb_camera3_and_processors + +flag { + name: "wb_libcameraservice" + namespace: "core_graphics" + description: "Remove usage of IGBPs in the libcameraservice." + bug: "342197849" + is_fixed_read_only: true +} # wb_libcameraservice + +flag { + name: "bq_producer_throttles_only_async_mode" + namespace: "core_graphics" + description: "BufferQueueProducer only CPU throttle on queueBuffer() in async mode." + bug: "359252619" + is_fixed_read_only: true +} # bq_producer_throttles_only_async_mode diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index eb2a61d151..53f4a36c42 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -186,6 +186,10 @@ public: mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber); } + void setApplyToken(sp<IBinder> applyToken) { + mBlastBufferQueueAdapter->setApplyToken(std::move(applyToken)); + } + private: sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter; }; @@ -511,6 +515,69 @@ TEST_F(BLASTBufferQueueTest, TripleBuffering) { adapter.waitForCallbacks(); } +class WaitForCommittedCallback { +public: + WaitForCommittedCallback() = default; + ~WaitForCommittedCallback() = default; + + void wait() { + std::unique_lock lock(mMutex); + cv.wait(lock, [this] { return mCallbackReceived; }); + } + + void notify() { + std::unique_lock lock(mMutex); + mCallbackReceived = true; + cv.notify_one(); + mCallbackReceivedTimeStamp = std::chrono::system_clock::now(); + } + auto getCallback() { + return [this](void* /* unused context */, nsecs_t /* latchTime */, + const sp<Fence>& /* presentFence */, + const std::vector<SurfaceControlStats>& /* stats */) { notify(); }; + } + std::chrono::time_point<std::chrono::system_clock> mCallbackReceivedTimeStamp; + +private: + std::mutex mMutex; + std::condition_variable cv; + bool mCallbackReceived = false; +}; + +TEST_F(BLASTBufferQueueTest, setApplyToken) { + sp<IBinder> applyToken = sp<BBinder>::make(); + WaitForCommittedCallback firstTransaction; + WaitForCommittedCallback secondTransaction; + { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + adapter.setApplyToken(applyToken); + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction t; + t.addTransactionCommittedCallback(firstTransaction.getCallback(), nullptr); + adapter.mergeWithNextTransaction(&t, 1); + queueBuffer(igbProducer, 127, 127, 127, + /*presentTimeDelay*/ std::chrono::nanoseconds(500ms).count()); + } + { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + adapter.setApplyToken(applyToken); + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction t; + t.addTransactionCommittedCallback(secondTransaction.getCallback(), nullptr); + adapter.mergeWithNextTransaction(&t, 1); + queueBuffer(igbProducer, 127, 127, 127, /*presentTimeDelay*/ 0); + } + + firstTransaction.wait(); + secondTransaction.wait(); + EXPECT_GT(secondTransaction.mCallbackReceivedTimeStamp, + firstTransaction.mCallbackReceivedTimeStamp); +} + TEST_F(BLASTBufferQueueTest, SetCrop_Item) { uint8_t r = 255; uint8_t g = 0; diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp index 2ac2550f07..8db48d2eb0 100644 --- a/libs/gui/tests/Choreographer_test.cpp +++ b/libs/gui/tests/Choreographer_test.cpp @@ -52,25 +52,23 @@ TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) { sp<Looper> looper = Looper::prepare(0); Choreographer* choreographer = Choreographer::getForThread(); VsyncCallback animationCb; - VsyncCallback inputCb; - choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0, CALLBACK_ANIMATION); + VsyncCallback inputCb; choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0, CALLBACK_INPUT); - - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); - nsecs_t currTime; - int pollResult; + auto startTime = std::chrono::system_clock::now(); do { - pollResult = looper->pollOnce(16); - currTime = systemTime(SYSTEM_TIME_MONOTONIC); - } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) && - (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) && - (currTime - startTime < 3000)); - - ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback"; - ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback"; + static constexpr int32_t timeoutMs = 1000; + int pollResult = looper->pollOnce(timeoutMs); + ASSERT_TRUE((pollResult != Looper::POLL_TIMEOUT) && (pollResult != Looper::POLL_ERROR)) + << "Failed to poll looper. Poll result = " << pollResult; + auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::system_clock::now() - startTime); + ASSERT_LE(elapsedMs.count(), timeoutMs) + << "Timed out waiting for callbacks. inputCb=" << inputCb.callbackReceived() + << " animationCb=" << animationCb.callbackReceived(); + } while (!(inputCb.callbackReceived() && animationCb.callbackReceived())); ASSERT_EQ(inputCb.frameTime, animationCb.frameTime) << android::base::StringPrintf("input and animation callback frame times don't match. " diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index 99ffa683dd..cdbc1869c3 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "InputTransport" +#define LOG_TAG "InputConsumerNoResampling" #define ATRACE_TAG ATRACE_TAG_INPUT #include <chrono> @@ -33,12 +33,12 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> -namespace input_flags = com::android::input::flags; - namespace android { namespace { +using std::chrono::nanoseconds; + /** * Log debug messages relating to the consumer end of the transport channel. * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart) @@ -173,11 +173,9 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim bool isPointerEvent(const MotionEvent& motionEvent) { return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER; } - } // namespace using android::base::Result; -using android::base::StringPrintf; // --- InputConsumerNoResampling --- @@ -185,10 +183,10 @@ InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<Input sp<Looper> looper, InputConsumerCallbacks& callbacks, std::unique_ptr<Resampler> resampler) - : mChannel(channel), - mLooper(looper), - mCallbacks(callbacks), - mResampler(std::move(resampler)), + : mChannel{channel}, + mLooper{looper}, + mCallbacks{callbacks}, + mResampler{std::move(resampler)}, mFdEvents(0) { LOG_ALWAYS_FATAL_IF(mLooper == nullptr); mCallback = sp<LooperEventCallback>::make( @@ -201,7 +199,7 @@ InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<Input InputConsumerNoResampling::~InputConsumerNoResampling() { ensureCalledOnLooperThread(__func__); - consumeBatchedInputEvents(std::nullopt); + consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt); while (!mOutboundQueue.empty()) { processOutboundEvents(); // This is our last chance to ack the events. If we don't ack them here, we will get an ANR, @@ -227,8 +225,7 @@ int InputConsumerNoResampling::handleReceiveCallback(int events) { int handledEvents = 0; if (events & ALOOPER_EVENT_INPUT) { - std::vector<InputMessage> messages = readAllMessages(); - handleMessages(std::move(messages)); + handleMessages(readAllMessages()); handledEvents |= ALOOPER_EVENT_INPUT; } @@ -336,10 +333,8 @@ void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messa // add it to batch mBatches[deviceId].emplace(msg); } else { - // consume all pending batches for this event immediately - // TODO(b/329776327): figure out if this could be smarter by limiting the - // consumption only to the current device. - consumeBatchedInputEvents(std::nullopt); + // consume all pending batches for this device immediately + consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt); handleMessage(msg); } } else { @@ -457,11 +452,16 @@ void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const { } std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> -InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t frameTime, +InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages) { std::unique_ptr<MotionEvent> motionEvent; std::optional<uint32_t> firstSeqForBatch; - while (!messages.empty() && !(messages.front().body.motion.eventTime > frameTime)) { + const nanoseconds resampleLatency = + (mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0}; + const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency; + + while (!messages.empty() && + (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) { if (motionEvent == nullptr) { motionEvent = createMotionEvent(messages.front()); firstSeqForBatch = messages.front().header.seq; @@ -480,37 +480,52 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t frameTime, if (!messages.empty()) { futureSample = &messages.front(); } - mResampler->resampleMotionEvent(static_cast<std::chrono::nanoseconds>(frameTime), - *motionEvent, futureSample); + mResampler->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent, + futureSample); } return std::make_pair(std::move(motionEvent), firstSeqForBatch); } bool InputConsumerNoResampling::consumeBatchedInputEvents( - std::optional<nsecs_t> requestedFrameTime) { + std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) { ensureCalledOnLooperThread(__func__); // When batching is not enabled, we want to consume all events. That's equivalent to having an - // infinite frameTime. - const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max()); + // infinite requestedFrameTime. + requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max()); bool producedEvents = false; - for (auto& [_, messages] : mBatches) { - auto [motion, firstSeqForBatch] = createBatchedMotionEvent(frameTime, messages); + + for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId)) + : (mBatches.begin()); + deviceIdIter != mBatches.cend(); ++deviceIdIter) { + std::queue<InputMessage>& messages = deviceIdIter->second; + auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages); if (motion != nullptr) { LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value()); mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch); producedEvents = true; } else { - // This is OK, it just means that the frameTime is too old (all events that we have - // pending are in the future of the frametime). Maybe print a - // warning? If there are multiple devices active though, this might be normal and can - // just be ignored, unless none of them resulted in any consumption (in that case, this - // function would already return "false" so we could just leave it up to the caller). + // This is OK, it just means that the requestedFrameTime is too old (all events that we + // have pending are in the future of the requestedFrameTime). Maybe print a warning? If + // there are multiple devices active though, this might be normal and can just be + // ignored, unless none of them resulted in any consumption (in that case, this function + // would already return "false" so we could just leave it up to the caller). + } + + if (deviceId.has_value()) { + // We already consumed events for this device. Break here to prevent iterating over the + // other devices. + break; } } std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); }); return producedEvents; } +bool InputConsumerNoResampling::consumeBatchedInputEvents( + std::optional<nsecs_t> requestedFrameTime) { + return consumeBatchedInputEvents(/*deviceId=*/std::nullopt, requestedFrameTime); +} + void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const { sp<Looper> callingThreadLooper = Looper::getForThread(); if (callingThreadLooper != mLooper) { diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 9333ab83a6..c9030312f9 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -20,6 +20,7 @@ #include <unistd.h> #include <ctype.h> +#include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <ftl/enum.h> @@ -31,6 +32,9 @@ using android::base::StringPrintf; namespace android { +// Set to true to log detailed debugging messages about IDC file probing. +static constexpr bool DEBUG_PROBE = false; + static const char* CONFIGURATION_FILE_DIR[] = { "idc/", "keylayout/", @@ -114,15 +118,18 @@ std::string getInputDeviceConfigurationFilePathByName( for (const auto& prefix : pathPrefixes) { path = prefix; appendInputDeviceConfigurationFileRelativePath(path, name, type); -#if DEBUG_PROBE - ALOGD("Probing for system provided input device configuration file: path='%s'", - path.c_str()); -#endif if (!access(path.c_str(), R_OK)) { -#if DEBUG_PROBE - ALOGD("Found"); -#endif + LOG_IF(INFO, DEBUG_PROBE) + << "Found system-provided input device configuration file at " << path; return path; + } else if (errno != ENOENT) { + LOG(WARNING) << "Couldn't find a system-provided input device configuration file at " + << path << " due to error " << errno << " (" << strerror(errno) + << "); there may be an IDC file there that cannot be loaded."; + } else { + LOG_IF(ERROR, DEBUG_PROBE) + << "Didn't find system-provided input device configuration file at " << path + << ": " << strerror(errno); } } @@ -135,21 +142,22 @@ std::string getInputDeviceConfigurationFilePathByName( } path += "/system/devices/"; appendInputDeviceConfigurationFileRelativePath(path, name, type); -#if DEBUG_PROBE - ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str()); -#endif if (!access(path.c_str(), R_OK)) { -#if DEBUG_PROBE - ALOGD("Found"); -#endif + LOG_IF(INFO, DEBUG_PROBE) << "Found system user input device configuration file at " + << path; return path; + } else if (errno != ENOENT) { + LOG(WARNING) << "Couldn't find a system user input device configuration file at " << path + << " due to error " << errno << " (" << strerror(errno) + << "); there may be an IDC file there that cannot be loaded."; + } else { + LOG_IF(ERROR, DEBUG_PROBE) << "Didn't find system user input device configuration file at " + << path << ": " << strerror(errno); } // Not found. -#if DEBUG_PROBE - ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", - name.c_str(), type); -#endif + LOG_IF(INFO, DEBUG_PROBE) << "Probe failed to find input device configuration file with name '" + << name << "' and type " << ftl::enum_string(type); return ""; } diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 1cf5612d45..b0563abaf7 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -317,19 +317,8 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t return true; } -void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) { - if (fromKeyCode == toKeyCode) { - mKeyRemapping.erase(fromKeyCode); -#if DEBUG_MAPPING - ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode); -#endif - return; - } - mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode); -#if DEBUG_MAPPING - ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode, - toKeyCode); -#endif +void KeyCharacterMap::setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) { + mKeyRemapping = keyRemapping; } status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp index c663649091..51fadf8ec1 100644 --- a/libs/input/Resampler.cpp +++ b/libs/input/Resampler.cpp @@ -241,13 +241,19 @@ inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample, motionEvent.getId()); } -void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent, +nanoseconds LegacyResampler::getResampleLatency() const { + return RESAMPLE_LATENCY; +} + +void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent, const InputMessage* futureSample) { if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) { mLatestSamples.clear(); } mPreviousDeviceId = motionEvent.getDeviceId(); + const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY; + updateLatestSamples(motionEvent); const std::optional<Sample> sample = (futureSample != nullptr) diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index ab117b875e..60fb00e128 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -171,3 +171,39 @@ flag { description: "Include AXIS_RELATIVE_X and AXIS_RELATIVE_Y values when reporting touches from captured touchpads." bug: "330522990" } + +flag { + name: "enable_per_device_input_latency_metrics" + namespace: "input" + description: "Capture input latency metrics on a per device granular level using histograms." + bug: "270049345" +} + +flag { + name: "collect_palm_rejection_quality_metrics" + namespace: "input" + description: "Collect quality metrics on framework palm rejection." + bug: "341717757" +} + +flag { + name: "enable_touchpad_no_focus_change" + namespace: "input" + description: "Prevents touchpad gesture changing window focus." + bug: "364460018" +} + +flag { + name: "enable_input_policy_profile" + namespace: "input" + description: "Apply input policy profile for input threads." + bug: "347122505" + is_fixed_read_only: true +} + +flag { + name: "keyboard_repeat_keys" + namespace: "input" + description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates." + bug: "336585002" +} diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs index 008f675485..4f4ea8568b 100644 --- a/libs/input/rust/lib.rs +++ b/libs/input/rust/lib.rs @@ -24,12 +24,14 @@ mod keyboard_classifier; pub use data_store::{DataStore, DefaultFileReaderWriter}; pub use input::{ - DeviceClass, DeviceId, InputDevice, ModifierState, MotionAction, MotionFlags, Source, + DeviceClass, DeviceId, InputDevice, KeyboardType, ModifierState, MotionAction, MotionFlags, + Source, }; pub use input_verifier::InputVerifier; pub use keyboard_classifier::KeyboardClassifier; #[cxx::bridge(namespace = "android::input")] +#[allow(clippy::needless_maybe_sized)] #[allow(unsafe_op_in_unsafe_fn)] mod ffi { #[namespace = "android"] diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 132866bd99..81c6175805 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -16,6 +16,7 @@ cc_test { "BlockingQueue_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", + "InputConsumer_test.cpp", "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", @@ -25,6 +26,7 @@ cc_test { "MotionPredictorMetricsManager_test.cpp", "Resampler_test.cpp", "RingBuffer_test.cpp", + "TestInputChannel.cpp", "TfLiteMotionPredictor_test.cpp", "TouchResampling_test.cpp", "TouchVideoFrame_test.cpp", @@ -92,6 +94,7 @@ cc_test { }, }, }, + native_coverage: false, } // NOTE: This is a compile time test, and does not need to be diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp new file mode 100644 index 0000000000..d708316236 --- /dev/null +++ b/libs/input/tests/InputConsumer_test.cpp @@ -0,0 +1,247 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/InputConsumerNoResampling.h> + +#include <memory> +#include <optional> + +#include <TestEventMatchers.h> +#include <TestInputChannel.h> +#include <android-base/logging.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <input/BlockingQueue.h> +#include <input/InputEventBuilders.h> +#include <utils/Looper.h> +#include <utils/StrongPointer.h> + +namespace android { + +namespace { + +using std::chrono::nanoseconds; + +using ::testing::AllOf; +using ::testing::Matcher; +using ::testing::Not; + +} // namespace + +class InputConsumerTest : public testing::Test, public InputConsumerCallbacks { +protected: + InputConsumerTest() + : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")}, + mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} { + Looper::setForThread(mLooper); + mConsumer = + std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mLooper, *this, + std::make_unique<LegacyResampler>()); + } + + void invokeLooperCallback() const { + sp<LooperCallback> callback; + ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr, + /*events=*/nullptr, &callback, /*data=*/nullptr)); + callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr); + } + + void assertOnBatchedInputEventPendingWasCalled() { + ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL) + << "onBatchedInputEventPending has not been called."; + --mOnBatchedInputEventPendingInvocationCount; + } + + void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) { + std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop(); + ASSERT_NE(motionEvent, nullptr); + EXPECT_THAT(*motionEvent, matcher); + } + + std::shared_ptr<TestInputChannel> mClientTestChannel; + sp<Looper> mLooper; + std::unique_ptr<InputConsumerNoResampling> mConsumer; + + BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents; + BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents; + BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents; + BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents; + BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents; + BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents; + +private: + size_t mOnBatchedInputEventPendingInvocationCount{0}; + + // InputConsumerCallbacks interface + void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override { + mKeyEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override { + mMotionEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onBatchedInputEventPending(int32_t pendingBatchSource) override { + if (!mConsumer->probablyHasInput()) { + ADD_FAILURE() << "should deterministically have input because there is a batch"; + } + ++mOnBatchedInputEventPendingInvocationCount; + }; + void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override { + mFocusEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; + void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override { + mCaptureEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; + void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override { + mDragEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + } + void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override { + mTouchModeEvents.push(std::move(event)); + mConsumer->finishInputEvent(seq, true); + }; +}; + +TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) { + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} + .eventTime(nanoseconds{0ms}.count()) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1} + .eventTime(nanoseconds{5ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2} + .eventTime(nanoseconds{10ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + + mClientTestChannel->assertNoSentMessages(); + + invokeLooperCallback(); + + assertOnBatchedInputEventPendingWasCalled(); + + mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt); + + std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop(); + ASSERT_NE(downMotionEvent, nullptr); + + std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop(); + ASSERT_NE(moveMotionEvent, nullptr); + EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL); + + mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); +} + +TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) { + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} + .eventTime(nanoseconds{0ms}.count()) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1} + .eventTime(nanoseconds{5ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2} + .eventTime(nanoseconds{10ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3} + .eventTime(nanoseconds{15ms}.count()) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + + mClientTestChannel->assertNoSentMessages(); + + invokeLooperCallback(); + + assertOnBatchedInputEventPendingWasCalled(); + + mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/); + + std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop(); + ASSERT_NE(downMotionEvent, nullptr); + + std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop(); + ASSERT_NE(moveMotionEvent, nullptr); + const size_t numSamples = moveMotionEvent->getHistorySize() + 1; + EXPECT_LT(moveMotionEvent->getHistoricalEventTime(numSamples - 2), + moveMotionEvent->getEventTime()); + + // Consume all remaining events before ending the test. Otherwise, the smart pointer that owns + // consumer is set to null before destroying consumer. This leads to a member function call on a + // null object. + // TODO(b/332613662): Remove this workaround. + mConsumer->consumeBatchedInputEvents(std::nullopt); + + mClientTestChannel->assertFinishMessage(/*seq=*/0, true); + mClientTestChannel->assertFinishMessage(/*seq=*/1, true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, true); +} + +TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) { + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_MOVE) + .build()); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4} + .deviceId(1) + .action(AMOTION_EVENT_ACTION_DOWN) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN))); + + mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5} + .deviceId(0) + .action(AMOTION_EVENT_ACTION_UP) + .build()); + + invokeLooperCallback(); + assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE), + Not(MotionEventIsResampled()))); + + mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true); +} +} // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index 467c3b46ce..1210f711de 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -364,7 +364,7 @@ private: if (!mConsumer->probablyHasInput()) { ADD_FAILURE() << "should deterministically have input because there is a batch"; } - mConsumer->consumeBatchedInputEvents(std::nullopt); + mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt); }; void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override { mFocusEvents.push(std::move(event)); diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp index 7ae9a28664..26dee393c1 100644 --- a/libs/input/tests/Resampler_test.cpp +++ b/libs/input/tests/Resampler_test.cpp @@ -120,6 +120,47 @@ InputStream::operator MotionEvent() const { } // namespace +/** + * The testing setup assumes an input rate of 200 Hz and a display rate of 60 Hz. This implies that + * input events are received every 5 milliseconds, while the display consumes batched events every + * ~16 milliseconds. The resampler's RESAMPLE_LATENCY constant determines the resample time, which + * is calculated as frameTime - RESAMPLE_LATENCY. resampleTime specifies the time used for + * resampling. For example, if the desired frame time consumption is ~16 milliseconds, the resample + * time would be ~11 milliseconds. Consequenly, the last added sample to the motion event has an + * event time of ~11 milliseconds. Note that there are specific scenarios where resampleMotionEvent + * is not called with a multiple of ~16 milliseconds. These cases are primarily for data addition + * or to test other functionalities of the resampler. + * + * Coordinates are calculated using linear interpolation (lerp) based on the last two available + * samples. Linear interpolation is defined as (a + alpha*(b - a)). Let t_b and t_a be the + * timestamps of samples a and b, respectively. The interpolation factor alpha is calculated as + * (resampleTime - t_a) / (t_b - t_a). The value of alpha determines whether the resampled + * coordinates are interpolated or extrapolated. If alpha falls within the semi-closed interval [0, + * 1), the coordinates are interpolated. If alpha is greater than or equal to 1, the coordinates are + * extrapolated. + * + * The timeline below depics an interpolation scenario + * -----------------------------------|---------|---------|---------|---------- + * 10ms 11ms 15ms 16ms + * MOVE | MOVE | + * resampleTime frameTime + * Based on the timeline alpha is (11 - 10)/(15 - 10) = 1/5. Thus, coordinates are interpolated. + * + * The following timeline portrays an extrapolation scenario + * -------------------------|---------|---------|-------------------|---------- + * 5ms 10ms 11ms 16ms + * MOVE MOVE | | + * resampleTime frameTime + * Likewise, alpha = (11 - 5)/(10 - 5) = 6/5. Hence, coordinates are extrapolated. + * + * If a motion event was resampled, the tests will check that the following conditions are satisfied + * to guarantee resampling correctness: + * - The motion event metadata must not change. + * - The number of samples in the motion event must only increment by 1. + * - The resampled values must be at the end of motion event coordinates. + * - The rasamples values must be near the hand calculations. + * - The resampled time must be the most recent one in motion event. + */ class ResamplerTest : public testing::Test { protected: ResamplerTest() : mResampler(std::make_unique<LegacyResampler>()) {} @@ -225,7 +266,7 @@ TEST_F(ResamplerTest, NonResampledAxesArePreserved) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); EXPECT_EQ(motionEvent.getTouchMajor(0), TOUCH_MAJOR_VALUE); @@ -243,7 +284,7 @@ TEST_F(ResamplerTest, SinglePointerNotEnoughDataToResample) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } @@ -270,23 +311,6 @@ TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) { assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice); } -// Increments of 16 ms for display refresh rate -// Increments of 6 ms for input frequency -// Resampling latency is known to be 5 ms -// Therefore, first resampling time will be 11 ms - -/** - * Timeline - * ----+----------------------+---------+---------+---------+---------- - * 0ms 10ms 11ms 15ms 16ms - * DOWN MOVE | MSG | - * resample frame - * Resampling occurs at 11ms. It is possible to interpolate because there is a sample available - * after the resample time. It is assumed that the InputMessage frequency is 100Hz, and the frame - * frequency is 60Hz. This means the time between InputMessage samples is 10ms, and the time between - * frames is ~16ms. Resample time is frameTime - RESAMPLE_LATENCY. The resampled sample must be the - * last one in the batch to consume. - */ TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) { MotionEvent motionEvent = InputStream{{InputSample{10ms, @@ -297,7 +321,7 @@ TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, {Pointer{.id = 0, @@ -338,18 +362,13 @@ TEST_F(ResamplerTest, SinglePointerSingleSampleExtrapolation) { const MotionEvent originalMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(11ms, secondMotionEvent, nullptr); + mResampler->resampleMotionEvent(16ms, secondMotionEvent, nullptr); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent, {Pointer{.id = 0, .x = 2.2f, .y = 4.4f, .isResampled = true}}); - // Integrity of the whole motionEvent - // History size should increment by 1 - // Check if the resampled value is the last one - // Check if the resampleTime is correct - // Check if the PointerCoords are consistent with the other computations } TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) { @@ -364,7 +383,7 @@ TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, {Pointer{.id = 0, @@ -382,7 +401,7 @@ TEST_F(ResamplerTest, SinglePointerMultipleSampleExtrapolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); + mResampler->resampleMotionEvent(16ms, motionEvent, nullptr); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, {Pointer{.id = 0, @@ -400,7 +419,7 @@ TEST_F(ResamplerTest, SinglePointerDeltaTooSmallExtrapolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, nullptr); + mResampler->resampleMotionEvent(16ms, motionEvent, nullptr); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } @@ -414,7 +433,7 @@ TEST_F(ResamplerTest, SinglePointerDeltaTooLargeExtrapolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(27ms, motionEvent, nullptr); + mResampler->resampleMotionEvent(32ms, motionEvent, nullptr); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } @@ -428,7 +447,7 @@ TEST_F(ResamplerTest, SinglePointerResampleTimeTooFarExtrapolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(43ms, motionEvent, nullptr); + mResampler->resampleMotionEvent(48ms, motionEvent, nullptr); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, {Pointer{.id = 0, @@ -451,7 +470,7 @@ TEST_F(ResamplerTest, MultiplePointerSingleSampleInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, {Pointer{.x = 2.2f, .y = 2.2f, .isResampled = true}, @@ -475,7 +494,7 @@ TEST_F(ResamplerTest, MultiplePointerSingleSampleExtrapolation) { const MotionEvent originalMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent, {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, @@ -498,7 +517,7 @@ TEST_F(ResamplerTest, MultiplePointerMultipleSampleInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, @@ -517,7 +536,7 @@ TEST_F(ResamplerTest, MultiplePointerMultipleSampleExtrapolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, @@ -539,7 +558,7 @@ TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent, {Pointer{.x = 1.4f, .y = 1.4f, .isResampled = true}, @@ -560,7 +579,7 @@ TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersInterpolation) { const MotionEvent originalSecondMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(27ms, secondMotionEvent, &secondFutureSample); + mResampler->resampleMotionEvent(32ms, secondMotionEvent, &secondFutureSample); assertMotionEventIsResampledAndCoordsNear(originalSecondMotionEvent, secondMotionEvent, {Pointer{.x = 3.8f, .y = 3.8f, .isResampled = true}, @@ -586,7 +605,7 @@ TEST_F(ResamplerTest, MultiplePointerIncreaseNumPointersExtrapolation) { const MotionEvent secondOriginalMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); } @@ -606,7 +625,7 @@ TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } @@ -629,7 +648,7 @@ TEST_F(ResamplerTest, MultiplePointerDecreaseNumPointersExtrapolation) { const MotionEvent secondOriginalMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent, {Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true}, @@ -650,7 +669,7 @@ TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } @@ -672,7 +691,7 @@ TEST_F(ResamplerTest, MultiplePointerDifferentIdOrderExtrapolation) { const MotionEvent secondOriginalMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); } @@ -691,7 +710,7 @@ TEST_F(ResamplerTest, MultiplePointerDifferentIdsInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } @@ -713,7 +732,7 @@ TEST_F(ResamplerTest, MultiplePointerDifferentIdsExtrapolation) { const MotionEvent secondOriginalMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); } @@ -746,7 +765,7 @@ TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample); + mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } @@ -782,7 +801,7 @@ TEST_F(ResamplerTest, MultiplePointerDifferentToolTypeExtrapolation) { const MotionEvent secondOriginalMotionEvent = secondMotionEvent; - mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr); assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent); } @@ -815,7 +834,7 @@ TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeInterpolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } @@ -847,7 +866,7 @@ TEST_F(ResamplerTest, MultiplePointerShouldNotResampleToolTypeExtrapolation) { const MotionEvent originalMotionEvent = motionEvent; - mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr); + mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr); assertMotionEventIsNotResampled(originalMotionEvent, motionEvent); } diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h new file mode 100644 index 0000000000..dd2e40c025 --- /dev/null +++ b/libs/input/tests/TestEventMatchers.h @@ -0,0 +1,110 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ostream> + +#include <input/Input.h> + +namespace android { + +/** + * This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally, + * implementations must not be duplicated. + * TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput. + */ + +class WithDeviceIdMatcher { +public: + using is_gtest_matcher = void; + explicit WithDeviceIdMatcher(DeviceId deviceId) : mDeviceId(deviceId) {} + + bool MatchAndExplain(const InputEvent& event, std::ostream*) const { + return mDeviceId == event.getDeviceId(); + } + + void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; } + +private: + const DeviceId mDeviceId; +}; + +inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) { + return WithDeviceIdMatcher(deviceId); +} + +class WithMotionActionMatcher { +public: + using is_gtest_matcher = void; + explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} + + bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { + bool matches = mAction == event.getAction(); + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { + matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + } + return matches; + } + + void DescribeTo(std::ostream* os) const { + *os << "with motion action " << MotionEvent::actionToString(mAction); + if (mAction == AMOTION_EVENT_ACTION_CANCEL) { + *os << " and FLAG_CANCELED"; + } + } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; } + +private: + const int32_t mAction; +}; + +inline WithMotionActionMatcher WithMotionAction(int32_t action) { + return WithMotionActionMatcher(action); +} + +class MotionEventIsResampledMatcher { +public: + using is_gtest_matcher = void; + + bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const { + const size_t numSamples = motionEvent.getHistorySize() + 1; + const size_t numPointers = motionEvent.getPointerCount(); + if (numPointers <= 0 || numSamples <= 0) { + return false; + } + for (size_t i = 0; i < numPointers; ++i) { + const PointerCoords& pointerCoords = + motionEvent.getSamplePointerCoords()[numSamples * numPointers + i]; + if (!pointerCoords.isResampled) { + return false; + } + } + return true; + } + + void DescribeTo(std::ostream* os) const { *os << "MotionEvent is resampled."; } + + void DescribeNegationTo(std::ostream* os) const { *os << "MotionEvent is not resampled."; } +}; + +inline MotionEventIsResampledMatcher MotionEventIsResampled() { + return MotionEventIsResampledMatcher(); +} +} // namespace android diff --git a/libs/input/tests/TestInputChannel.cpp b/libs/input/tests/TestInputChannel.cpp new file mode 100644 index 0000000000..26a0ca2106 --- /dev/null +++ b/libs/input/tests/TestInputChannel.cpp @@ -0,0 +1,102 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "TestInputChannel" +#define ATRACE_TAG ATRACE_TAG_INPUT + +#include <TestInputChannel.h> + +#include <sys/socket.h> +#include <unistd.h> + +#include <array> + +#include <android-base/logging.h> +#include <android-base/unique_fd.h> +#include <binder/IBinder.h> +#include <utils/StrongPointer.h> + +namespace android { + +namespace { + +/** + * Returns a stub file descriptor by opening a socket pair and closing one of the fds. The returned + * fd can be used to construct an InputChannel. + */ +base::unique_fd generateFileDescriptor() { + std::array<int, 2> kFileDescriptors; + LOG_IF(FATAL, ::socketpair(AF_UNIX, SOCK_SEQPACKET, 0, kFileDescriptors.data()) != 0) + << "TestInputChannel. Failed to create socket pair."; + LOG_IF(FATAL, ::close(kFileDescriptors[1]) != 0) + << "TestInputChannel. Failed to close file descriptor."; + return base::unique_fd{kFileDescriptors[0]}; +} +} // namespace + +// --- TestInputChannel --- + +TestInputChannel::TestInputChannel(const std::string& name) + : InputChannel{name, generateFileDescriptor(), sp<BBinder>::make()} {} + +void TestInputChannel::enqueueMessage(const InputMessage& message) { + mReceivedMessages.push(message); +} + +status_t TestInputChannel::sendMessage(const InputMessage* message) { + LOG_IF(FATAL, message == nullptr) + << "TestInputChannel " << getName() << ". No message was passed to sendMessage."; + + mSentMessages.push(*message); + return OK; +} + +base::Result<InputMessage> TestInputChannel::receiveMessage() { + if (mReceivedMessages.empty()) { + return base::Error(WOULD_BLOCK); + } + InputMessage message = mReceivedMessages.front(); + mReceivedMessages.pop(); + return message; +} + +bool TestInputChannel::probablyHasInput() const { + return !mReceivedMessages.empty(); +} + +void TestInputChannel::assertFinishMessage(uint32_t seq, bool handled) { + ASSERT_FALSE(mSentMessages.empty()) + << "TestInputChannel " << getName() << ". Cannot assert. mSentMessages is empty."; + + const InputMessage& finishMessage = mSentMessages.front(); + + EXPECT_EQ(finishMessage.header.seq, seq) + << "TestInputChannel " << getName() + << ". Sequence mismatch. Message seq: " << finishMessage.header.seq + << " Expected seq: " << seq; + + EXPECT_EQ(finishMessage.body.finished.handled, handled) + << "TestInputChannel " << getName() + << ". Handled value mismatch. Message val: " << std::boolalpha + << finishMessage.body.finished.handled << "Expected val: " << handled + << std::noboolalpha; + mSentMessages.pop(); +} + +void TestInputChannel::assertNoSentMessages() const { + ASSERT_TRUE(mSentMessages.empty()); +} +} // namespace android
\ No newline at end of file diff --git a/libs/input/tests/TestInputChannel.h b/libs/input/tests/TestInputChannel.h new file mode 100644 index 0000000000..43253ec0ef --- /dev/null +++ b/libs/input/tests/TestInputChannel.h @@ -0,0 +1,66 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <queue> +#include <string> + +#include <android-base/result.h> +#include <gtest/gtest.h> +#include <input/InputTransport.h> +#include <utils/Errors.h> + +namespace android { + +class TestInputChannel final : public InputChannel { +public: + explicit TestInputChannel(const std::string& name); + + /** + * Enqueues a message in mReceivedMessages. + */ + void enqueueMessage(const InputMessage& message); + + /** + * Pushes message to mSentMessages. In the default implementation, InputChannel sends messages + * through a file descriptor. TestInputChannel, on the contrary, stores sent messages in + * mSentMessages for assertion reasons. + */ + status_t sendMessage(const InputMessage* message) override; + + /** + * Returns an InputMessage from mReceivedMessages. This is done instead of retrieving data + * directly from fd. + */ + base::Result<InputMessage> receiveMessage() override; + + /** + * Returns if mReceivedMessages is not empty. + */ + bool probablyHasInput() const override; + + void assertFinishMessage(uint32_t seq, bool handled); + + void assertNoSentMessages() const; + +private: + // InputMessages received by the endpoint. + std::queue<InputMessage> mReceivedMessages; + // InputMessages sent by the endpoint. + std::queue<InputMessage> mSentMessages; +}; +} // namespace android diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp index e3be3bc8f8..d0ca78e658 100644 --- a/libs/nativedisplay/ADisplay.cpp +++ b/libs/nativedisplay/ADisplay.cpp @@ -129,7 +129,7 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { std::vector<DisplayConfigImpl> modesPerDisplay[size]; ui::DisplayConnectionType displayConnectionTypes[size]; int numModes = 0; - for (int i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) { ui::StaticDisplayInfo staticInfo; if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo); @@ -151,7 +151,7 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { numModes += modes.size(); modesPerDisplay[i].reserve(modes.size()); - for (int j = 0; j < modes.size(); ++j) { + for (size_t j = 0; j < modes.size(); ++j) { const ui::DisplayMode& mode = modes[j]; modesPerDisplay[i].emplace_back( DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(), @@ -224,7 +224,7 @@ float ADisplay_getMaxSupportedFps(ADisplay* display) { CHECK_NOT_NULL(display); DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); float maxFps = 0.0; - for (int i = 0; i < impl->numConfigs; ++i) { + for (size_t i = 0; i < impl->numConfigs; ++i) { maxFps = std::max(maxFps, impl->configs[i].fps); } return maxFps; @@ -261,7 +261,7 @@ int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) { for (size_t i = 0; i < impl->numConfigs; i++) { auto* config = impl->configs + i; - if (config->id == info.activeDisplayModeId) { + if (info.activeDisplayModeId >= 0 && config->id == (size_t)info.activeDisplayModeId) { *outConfig = reinterpret_cast<ADisplayConfig*>(config); return OK; } diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 855807472e..a8a86ba1a9 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -67,9 +67,6 @@ ndk_library { // Android O first_version: "26", - export_header_libs: [ - "libnativewindow_ndk_headers", - ], } cc_library { diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp index 97740dbcd8..d68d6ba1af 100644 --- a/libs/nativewindow/rust/Android.bp +++ b/libs/nativewindow/rust/Android.bp @@ -29,6 +29,8 @@ rust_bindgen { "--bitfield-enum=AHardwareBuffer_UsageFlags", "--allowlist-file=.*/nativewindow/include/.*\\.h", + "--allowlist-file=.*/include/cutils/.*\\.h", + "--allowlist-file=.*/include_outside_system/cutils/.*\\.h", "--blocklist-type", "AParcel", "--raw-line", @@ -39,6 +41,7 @@ rust_bindgen { ], shared_libs: [ "libbinder_ndk", + "libcutils", "libnativewindow", ], rustlibs: [ @@ -66,6 +69,7 @@ rust_library { srcs: [":libnativewindow_bindgen_internal"], shared_libs: [ "libbinder_ndk", + "libcutils", "libnativewindow", ], rustlibs: [ diff --git a/libs/nativewindow/rust/src/handle.rs b/libs/nativewindow/rust/src/handle.rs new file mode 100644 index 0000000000..c41ab8d1b8 --- /dev/null +++ b/libs/nativewindow/rust/src/handle.rs @@ -0,0 +1,243 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT 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 std::{ + ffi::c_int, + mem::forget, + os::fd::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}, + ptr::NonNull, +}; + +/// Rust wrapper around `native_handle_t`. +/// +/// This owns the `native_handle_t` and its file descriptors, and will close them and free it when +/// it is dropped. +#[derive(Debug)] +pub struct NativeHandle(NonNull<ffi::native_handle_t>); + +impl NativeHandle { + /// Creates a new `NativeHandle` with the given file descriptors and integer values. + /// + /// The `NativeHandle` will take ownership of the file descriptors and close them when it is + /// dropped. + pub fn new(fds: Vec<OwnedFd>, ints: &[c_int]) -> Option<Self> { + let fd_count = fds.len(); + // SAFETY: native_handle_create doesn't have any safety requirements. + let handle = unsafe { + ffi::native_handle_create(fd_count.try_into().unwrap(), ints.len().try_into().unwrap()) + }; + let handle = NonNull::new(handle)?; + for (i, fd) in fds.into_iter().enumerate() { + // SAFETY: `handle` must be valid because it was just created, and the array offset is + // within the bounds of what we allocated above. + unsafe { + *(*handle.as_ptr()).data.as_mut_ptr().add(i) = fd.into_raw_fd(); + } + } + for (i, value) in ints.iter().enumerate() { + // SAFETY: `handle` must be valid because it was just created, and the array offset is + // within the bounds of what we allocated above. Note that `data` is uninitialized + // until after this so we can't use `slice::from_raw_parts_mut` or similar to create a + // reference to it so we use raw pointers arithmetic instead. + unsafe { + *(*handle.as_ptr()).data.as_mut_ptr().add(fd_count + i) = *value; + } + } + // SAFETY: `handle` must be valid because it was just created. + unsafe { + ffi::native_handle_set_fdsan_tag(handle.as_ptr()); + } + Some(Self(handle)) + } + + /// Returns a borrowed view of all the file descriptors in this native handle. + pub fn fds(&self) -> Vec<BorrowedFd> { + self.data()[..self.fd_count()] + .iter() + .map(|fd| { + // SAFETY: The `native_handle_t` maintains ownership of the file descriptor so it + // won't be closed until this `NativeHandle` is destroyed. The `BorrowedFd` will + // have a lifetime constrained to that of `&self`, so it can't outlive it. + unsafe { BorrowedFd::borrow_raw(*fd) } + }) + .collect() + } + + /// Returns the integer values in this native handle. + pub fn ints(&self) -> &[c_int] { + &self.data()[self.fd_count()..] + } + + /// Destroys the `NativeHandle`, taking ownership of the file descriptors it contained. + pub fn into_fds(self) -> Vec<OwnedFd> { + let fds = self.data()[..self.fd_count()] + .iter() + .map(|fd| { + // SAFETY: The `native_handle_t` has ownership of the file descriptor, and + // after this we destroy it without closing the file descriptor so we can take over + // ownership of it. + unsafe { OwnedFd::from_raw_fd(*fd) } + }) + .collect(); + + // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed + // after this because we own it and forget it. + unsafe { + assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0); + } + // Don't drop self, as that would cause `native_handle_close` to be called and close the + // file descriptors. + forget(self); + fds + } + + /// Returns a reference to the underlying `native_handle_t`. + fn as_ref(&self) -> &ffi::native_handle_t { + // SAFETY: All the ways of creating a `NativeHandle` ensure that the `native_handle_t` is + // valid and initialised, and lives as long as the `NativeHandle`. We enforce Rust's + // aliasing rules by giving the reference a lifetime matching that of `&self`. + unsafe { self.0.as_ref() } + } + + /// Returns the number of file descriptors included in the native handle. + fn fd_count(&self) -> usize { + self.as_ref().numFds.try_into().unwrap() + } + + /// Returns the number of integer values included in the native handle. + fn int_count(&self) -> usize { + self.as_ref().numInts.try_into().unwrap() + } + + /// Returns a slice reference for all the used `data` field of the native handle, including both + /// file descriptors and integers. + fn data(&self) -> &[c_int] { + let total_count = self.fd_count() + self.int_count(); + // SAFETY: The data must have been initialised with this number of elements when the + // `NativeHandle` was created. + unsafe { self.as_ref().data.as_slice(total_count) } + } + + /// Wraps a raw `native_handle_t` pointer, taking ownership of it. + /// + /// # Safety + /// + /// `native_handle` must be a valid pointer to a `native_handle_t`, and must not be used + /// anywhere else after calling this method. + pub unsafe fn from_raw(native_handle: NonNull<ffi::native_handle_t>) -> Self { + Self(native_handle) + } + + /// Creates a new `NativeHandle` wrapping a clone of the given `native_handle_t` pointer. + /// + /// Unlike [`from_raw`](Self::from_raw) this doesn't take ownership of the pointer passed in, so + /// the caller remains responsible for closing and freeing it. + /// + /// # Safety + /// + /// `native_handle` must be a valid pointer to a `native_handle_t`. + pub unsafe fn clone_from_raw(native_handle: NonNull<ffi::native_handle_t>) -> Option<Self> { + // SAFETY: The caller promised that `native_handle` was valid. + let cloned = unsafe { ffi::native_handle_clone(native_handle.as_ptr()) }; + NonNull::new(cloned).map(Self) + } + + /// Returns a raw pointer to the wrapped `native_handle_t`. + /// + /// This is only valid as long as this `NativeHandle` exists, so shouldn't be stored. It mustn't + /// be closed or deleted. + pub fn as_raw(&self) -> NonNull<ffi::native_handle_t> { + self.0 + } + + /// Turns the `NativeHandle` into a raw `native_handle_t`. + /// + /// The caller takes ownership of the `native_handle_t` and its file descriptors, so is + /// responsible for closing and freeing it. + pub fn into_raw(self) -> NonNull<ffi::native_handle_t> { + let raw = self.0; + forget(self); + raw + } +} + +impl Clone for NativeHandle { + fn clone(&self) -> Self { + // SAFETY: Our wrapped `native_handle_t` pointer is always valid. + unsafe { Self::clone_from_raw(self.0) }.expect("native_handle_clone returned null") + } +} + +impl Drop for NativeHandle { + fn drop(&mut self) { + // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed + // after this because we own it and are being dropped. + unsafe { + assert_eq!(ffi::native_handle_close(self.0.as_ptr()), 0); + assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0); + } + } +} + +// SAFETY: `NativeHandle` owns the `native_handle_t`, which just contains some integers and file +// descriptors, which aren't tied to any particular thread. +unsafe impl Send for NativeHandle {} + +// SAFETY: A `NativeHandle` can be used from different threads simultaneously, as is is just +// integers and file descriptors. +unsafe impl Sync for NativeHandle {} + +#[cfg(test)] +mod test { + use super::*; + use std::fs::File; + + #[test] + fn create_empty() { + let handle = NativeHandle::new(vec![], &[]).unwrap(); + assert_eq!(handle.fds().len(), 0); + assert_eq!(handle.ints(), &[]); + } + + #[test] + fn create_with_ints() { + let handle = NativeHandle::new(vec![], &[1, 2, 42]).unwrap(); + assert_eq!(handle.fds().len(), 0); + assert_eq!(handle.ints(), &[1, 2, 42]); + } + + #[test] + fn create_with_fd() { + let file = File::open("/dev/null").unwrap(); + let handle = NativeHandle::new(vec![file.into()], &[]).unwrap(); + assert_eq!(handle.fds().len(), 1); + assert_eq!(handle.ints(), &[]); + } + + #[test] + fn clone() { + let file = File::open("/dev/null").unwrap(); + let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); + assert_eq!(original.ints(), &[42]); + assert_eq!(original.fds().len(), 1); + + let cloned = original.clone(); + drop(original); + + assert_eq!(cloned.ints(), &[42]); + assert_eq!(cloned.fds().len(), 1); + + drop(cloned); + } +} diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index dc3f51f7fd..931c311e65 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -16,7 +16,10 @@ extern crate nativewindow_bindgen as ffi; +mod handle; mod surface; + +pub use handle::NativeHandle; pub use surface::Surface; pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags}; @@ -27,31 +30,29 @@ use binder::{ unstable_api::{status_result, AsNative}, StatusCode, }; -use ffi::{AHardwareBuffer, AHardwareBuffer_readFromParcel, AHardwareBuffer_writeToParcel}; +use ffi::{ + AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_readFromParcel, + AHardwareBuffer_writeToParcel, +}; use std::fmt::{self, Debug, Formatter}; use std::mem::ManuallyDrop; use std::ptr::{self, null_mut, NonNull}; -/// Wrapper around an opaque C `AHardwareBuffer`. -#[derive(PartialEq, Eq)] -pub struct HardwareBuffer(NonNull<AHardwareBuffer>); +/// Wrapper around a C `AHardwareBuffer_Desc`. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HardwareBufferDescription(AHardwareBuffer_Desc); -impl HardwareBuffer { - /// 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. - /// - /// Available since API 29 - pub fn is_supported( +impl HardwareBufferDescription { + /// Creates a new `HardwareBufferDescription` with the given parameters. + pub fn new( width: u32, height: u32, layers: u32, format: AHardwareBuffer_Format::Type, usage: AHardwareBuffer_UsageFlags, stride: u32, - ) -> bool { - let buffer_desc = ffi::AHardwareBuffer_Desc { + ) -> Self { + Self(AHardwareBuffer_Desc { width, height, layers, @@ -60,9 +61,69 @@ impl HardwareBuffer { stride, rfu0: 0, rfu1: 0, - }; - // SAFETY: *buffer_desc will never be null. - let status = unsafe { ffi::AHardwareBuffer_isSupported(&buffer_desc) }; + }) + } + + /// Returns the width from the buffer description. + pub fn width(&self) -> u32 { + self.0.width + } + + /// Returns the height from the buffer description. + pub fn height(&self) -> u32 { + self.0.height + } + + /// Returns the number from layers from the buffer description. + pub fn layers(&self) -> u32 { + self.0.layers + } + + /// Returns the format from the buffer description. + pub fn format(&self) -> AHardwareBuffer_Format::Type { + self.0.format + } + + /// Returns the usage bitvector from the buffer description. + pub fn usage(&self) -> AHardwareBuffer_UsageFlags { + AHardwareBuffer_UsageFlags(self.0.usage) + } + + /// Returns the stride from the buffer description. + pub fn stride(&self) -> u32 { + self.0.stride + } +} + +impl Default for HardwareBufferDescription { + fn default() -> Self { + Self(AHardwareBuffer_Desc { + width: 0, + height: 0, + layers: 0, + format: 0, + usage: 0, + stride: 0, + rfu0: 0, + rfu1: 0, + }) + } +} + +/// Wrapper around an opaque C `AHardwareBuffer`. +#[derive(PartialEq, Eq)] +pub struct HardwareBuffer(NonNull<AHardwareBuffer>); + +impl HardwareBuffer { + /// 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. + /// + /// Available since API 29 + pub fn is_supported(buffer_description: &HardwareBufferDescription) -> bool { + // SAFETY: The pointer comes from a reference so must be valid. + let status = unsafe { ffi::AHardwareBuffer_isSupported(&buffer_description.0) }; status == 1 } @@ -74,27 +135,11 @@ impl HardwareBuffer { /// /// Available since API level 26. #[inline] - pub fn new( - width: u32, - height: u32, - layers: u32, - format: AHardwareBuffer_Format::Type, - usage: AHardwareBuffer_UsageFlags, - ) -> Option<Self> { - let buffer_desc = ffi::AHardwareBuffer_Desc { - width, - height, - layers, - format, - usage: usage.0, - stride: 0, - rfu0: 0, - rfu1: 0, - }; + pub fn new(buffer_description: &HardwareBufferDescription) -> Option<Self> { let mut ptr = ptr::null_mut(); // SAFETY: The returned pointer is valid until we drop/deallocate it. The function may fail // and return a status, but we check it later. - let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut ptr) }; + let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_description.0, &mut ptr) }; if status == 0 { Some(Self(NonNull::new(ptr).expect("Allocated AHardwareBuffer was null"))) @@ -103,6 +148,50 @@ impl HardwareBuffer { } } + /// Creates a `HardwareBuffer` from a native handle. + /// + /// The native handle is cloned, so this doesn't take ownership of the original handle passed + /// in. + pub fn create_from_handle( + handle: &NativeHandle, + buffer_description: &HardwareBufferDescription, + ) -> Result<Self, StatusCode> { + let mut buffer = ptr::null_mut(); + // SAFETY: The caller guarantees that `handle` is valid, and the buffer pointer is valid + // because it comes from a reference. The method we pass means that + // `AHardwareBuffer_createFromHandle` will clone the handle rather than taking ownership of + // it. + let status = unsafe { + ffi::AHardwareBuffer_createFromHandle( + &buffer_description.0, + handle.as_raw().as_ptr(), + ffi::CreateFromHandleMethod_AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE + .try_into() + .unwrap(), + &mut buffer, + ) + }; + status_result(status)?; + Ok(Self(NonNull::new(buffer).expect("Allocated AHardwareBuffer was null"))) + } + + /// Returns a clone of the native handle of the buffer. + /// + /// Returns `None` if the operation fails for any reason. + pub fn cloned_native_handle(&self) -> Option<NativeHandle> { + // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid + // because it must have been allocated by `AHardwareBuffer_allocate`, + // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet + // released it. + let native_handle = unsafe { ffi::AHardwareBuffer_getNativeHandle(self.0.as_ptr()) }; + NonNull::new(native_handle.cast_mut()).and_then(|native_handle| { + // SAFETY: `AHardwareBuffer_getNativeHandle` should have returned a valid pointer which + // is valid at least as long as the buffer is, and `clone_from_raw` clones it rather + // than taking ownership of it so the original `native_handle` isn't stored. + unsafe { NativeHandle::clone_from_raw(native_handle) } + }) + } + /// Adopts the given raw pointer and wraps it in a Rust HardwareBuffer. /// /// # Safety @@ -155,37 +244,8 @@ impl HardwareBuffer { out_id } - /// Get the width of this buffer - pub fn width(&self) -> u32 { - self.description().width - } - - /// Get the height of this buffer - pub fn height(&self) -> u32 { - self.description().height - } - - /// Get the number of layers of this buffer - pub fn layers(&self) -> u32 { - self.description().layers - } - - /// Get the format of this buffer - pub fn format(&self) -> AHardwareBuffer_Format::Type { - self.description().format - } - - /// Get the usage bitvector of this buffer - pub fn usage(&self) -> AHardwareBuffer_UsageFlags { - AHardwareBuffer_UsageFlags(self.description().usage) - } - - /// Get the stride of this buffer - pub fn stride(&self) -> u32 { - self.description().stride - } - - fn description(&self) -> ffi::AHardwareBuffer_Desc { + /// Returns the description of this buffer. + pub fn description(&self) -> HardwareBufferDescription { let mut buffer_desc = ffi::AHardwareBuffer_Desc { width: 0, height: 0, @@ -198,7 +258,7 @@ impl HardwareBuffer { }; // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null. unsafe { ffi::AHardwareBuffer_describe(self.0.as_ref(), &mut buffer_desc) }; - buffer_desc + HardwareBufferDescription(buffer_desc) } } @@ -281,19 +341,27 @@ mod test { #[test] fn create_valid_buffer_returns_ok() { - let buffer = HardwareBuffer::new( + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( 512, 512, 1, AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, - ); + 0, + )); assert!(buffer.is_some()); } #[test] fn create_invalid_buffer_returns_err() { - let buffer = HardwareBuffer::new(512, 512, 1, 0, AHardwareBuffer_UsageFlags(0)); + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + 512, + 512, + 1, + 0, + AHardwareBuffer_UsageFlags(0), + 0, + )); assert!(buffer.is_none()); } @@ -319,39 +387,45 @@ mod test { // SAFETY: The pointer must be valid because it was just allocated successfully, and we // don't use it after calling this. let buffer = unsafe { HardwareBuffer::from_raw(NonNull::new(raw_buffer_ptr).unwrap()) }; - assert_eq!(buffer.width(), 1024); + assert_eq!(buffer.description().width(), 1024); } #[test] fn basic_getters() { - let buffer = HardwareBuffer::new( + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( 1024, 512, 1, AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, - ) + 0, + )) .expect("Buffer with some basic parameters was not created successfully"); - assert_eq!(buffer.width(), 1024); - assert_eq!(buffer.height(), 512); - assert_eq!(buffer.layers(), 1); - assert_eq!(buffer.format(), AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM); + let description = buffer.description(); + assert_eq!(description.width(), 1024); + assert_eq!(description.height(), 512); + assert_eq!(description.layers(), 1); + assert_eq!( + description.format(), + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM + ); assert_eq!( - buffer.usage(), + description.usage(), AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN ); } #[test] fn id_getter() { - let buffer = HardwareBuffer::new( + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( 1024, 512, 1, AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, - ) + 0, + )) .expect("Buffer with some basic parameters was not created successfully"); assert_ne!(0, buffer.id()); @@ -359,13 +433,14 @@ mod test { #[test] fn clone() { - let buffer = HardwareBuffer::new( + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( 1024, 512, 1, AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, - ) + 0, + )) .expect("Buffer with some basic parameters was not created successfully"); let buffer2 = buffer.clone(); @@ -374,13 +449,14 @@ mod test { #[test] fn into_raw() { - let buffer = HardwareBuffer::new( + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( 1024, 512, 1, AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, - ) + 0, + )) .expect("Buffer with some basic parameters was not created successfully"); let buffer2 = buffer.clone(); @@ -390,4 +466,26 @@ mod test { assert_eq!(remade_buffer, buffer2); } + + #[test] + fn native_handle_and_back() { + let buffer_description = HardwareBufferDescription::new( + 1024, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 1024, + ); + let buffer = HardwareBuffer::new(&buffer_description) + .expect("Buffer with some basic parameters was not created successfully"); + + let native_handle = + buffer.cloned_native_handle().expect("Failed to get native handle for buffer"); + let buffer2 = HardwareBuffer::create_from_handle(&native_handle, &buffer_description) + .expect("Failed to create buffer from native handle"); + + assert_eq!(buffer.description(), buffer_description); + assert_eq!(buffer2.description(), buffer_description); + } } diff --git a/libs/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h index 5689f7df94..5046a80095 100644 --- a/libs/nativewindow/rust/sys/nativewindow_bindings.h +++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h @@ -20,3 +20,5 @@ #include <android/hdr_metadata.h> #include <android/native_window.h> #include <android/native_window_aidl.h> +#include <cutils/native_handle.h> +#include <vndk/hardware_buffer.h> diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs index 876f6c8e26..73a7e95149 100644 --- a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs +++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs @@ -22,13 +22,14 @@ use nativewindow::*; #[inline] fn create_720p_buffer() -> HardwareBuffer { - HardwareBuffer::new( + HardwareBuffer::new(&HardwareBufferDescription::new( 1280, 720, 1, AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, - ) + 0, + )) .unwrap() } @@ -51,7 +52,7 @@ fn criterion_benchmark(c: &mut Criterion) { // underlying call to AHardwareBuffer_describe. c.bench_with_input(BenchmarkId::new("desc", "buffer"), &buffer, |b, buffer| { b.iter(|| { - buffer.width(); + buffer.description().width(); }) }); } diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index 7639fab3a5..d248ea0b84 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -100,6 +100,7 @@ filegroup { "skia/debug/SkiaCapture.cpp", "skia/debug/SkiaMemoryReporter.cpp", "skia/filters/BlurFilter.cpp", + "skia/filters/GainmapFactory.cpp", "skia/filters/GaussianBlurFilter.cpp", "skia/filters/KawaseBlurDualFilter.cpp", "skia/filters/KawaseBlurFilter.cpp", diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index bc3976d9f1..907590a236 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -21,6 +21,7 @@ #include "skia/GraphiteVkRenderEngine.h" #include "skia/SkiaGLRenderEngine.h" #include "threaded/RenderEngineThreaded.h" +#include "ui/GraphicTypes.h" #include <com_android_graphics_surfaceflinger_flags.h> #include <cutils/properties.h> @@ -101,17 +102,34 @@ ftl::Future<FenceResult> RenderEngine::drawLayers(const DisplaySettings& display base::unique_fd&& bufferFence) { const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); std::future<FenceResult> resultFuture = resultPromise->get_future(); - updateProtectedContext(layers, buffer); + updateProtectedContext(layers, {buffer.get()}); drawLayersInternal(std::move(resultPromise), display, layers, buffer, std::move(bufferFence)); return resultFuture; } +ftl::Future<FenceResult> RenderEngine::drawGainmap( + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) { + const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); + std::future<FenceResult> resultFuture = resultPromise->get_future(); + updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()}); + drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr, + std::move(hdrFence), hdrSdrRatio, dataspace, gainmap); + return resultFuture; +} + void RenderEngine::updateProtectedContext(const std::vector<LayerSettings>& layers, - const std::shared_ptr<ExternalTexture>& buffer) { + vector<const ExternalTexture*> buffers) { const bool needsProtectedContext = - (buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED)) || - std::any_of(layers.begin(), layers.end(), [](const LayerSettings& layer) { - const std::shared_ptr<ExternalTexture>& buffer = layer.source.buffer.buffer; + std::any_of(layers.begin(), layers.end(), + [](const LayerSettings& layer) { + const std::shared_ptr<ExternalTexture>& buffer = + layer.source.buffer.buffer; + return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); + }) || + std::any_of(buffers.begin(), buffers.end(), [](const ExternalTexture* buffer) { return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); }); useProtectedContext(needsProtectedContext); diff --git a/libs/renderengine/benchmark/AndroidTest.xml b/libs/renderengine/benchmark/AndroidTest.xml new file mode 100644 index 0000000000..3b923cb3a8 --- /dev/null +++ b/libs/renderengine/benchmark/AndroidTest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for librenderengine_bench."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native-metric" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="librenderengine_bench->/data/local/tmp/librenderengine_bench" /> + </target_preparer> + <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" > + <option name="native-benchmark-device-path" value="/data/local/tmp" /> + <option name="benchmark-module-name" value="librenderengine_bench" /> + <option name="file-exclusion-filter-regex" value=".*\.config$" /> + <option name="file-exclusion-filter-regex" value=".*/resources/.*" /> + </test> +</configuration> diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp index 326d1cec1b..a9264b3914 100644 --- a/libs/renderengine/benchmark/RenderEngineBench.cpp +++ b/libs/renderengine/benchmark/RenderEngineBench.cpp @@ -29,6 +29,16 @@ using namespace android; using namespace android::renderengine; +// To run tests: +/** + * mmm frameworks/native/libs/renderengine/benchmark;\ + * adb push $OUT/data/benchmarktest/librenderengine_bench/librenderengine_bench + * /data/benchmarktest/librenderengine_bench/librenderengine_bench;\ + * adb shell /data/benchmarktest/librenderengine_bench/librenderengine_bench + * + * (64-bit devices: out directory contains benchmarktest64 instead of benchmarktest) + */ + /////////////////////////////////////////////////////////////////////////////// // Helpers for calling drawLayers /////////////////////////////////////////////////////////////////////////////// @@ -173,29 +183,67 @@ static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& } } +/** + * Return a buffer with the image in the provided path, relative to the executable directory + */ +static std::shared_ptr<ExternalTexture> createTexture(RenderEngine& re, const char* relPathImg) { + // Initially use cpu access so we can decode into it with AImageDecoder. + auto [width, height] = getDisplaySize(); + auto srcBuffer = + allocateBuffer(re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source"); + std::string fileName = base::GetExecutableDirectory().append(relPathImg); + renderenginebench::decode(fileName.c_str(), srcBuffer->getBuffer()); + // Now copy into GPU-only buffer for more realistic timing. + srcBuffer = copyBuffer(re, srcBuffer, 0, "source"); + return srcBuffer; +} + /////////////////////////////////////////////////////////////////////////////// // Benchmarks /////////////////////////////////////////////////////////////////////////////// +constexpr char kHomescreenPath[] = "/resources/homescreen.png"; + +/** + * Draw a layer with texture and no additional shaders as a baseline to evaluate a shader's impact + * on performance + */ template <class... Args> -void BM_blur(benchmark::State& benchState, Args&&... args) { +void BM_homescreen(benchmark::State& benchState, Args&&... args) { auto args_tuple = std::make_tuple(std::move(args)...); auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), - static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)), - static_cast<RenderEngine::BlurAlgorithm>(std::get<2>(args_tuple))); + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); - // Initially use cpu access so we can decode into it with AImageDecoder. auto [width, height] = getDisplaySize(); - auto srcBuffer = - allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source"); - { - std::string srcImage = base::GetExecutableDirectory(); - srcImage.append("/resources/homescreen.png"); - renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer()); - - // Now copy into GPU-only buffer for more realistic timing. - srcBuffer = copyBuffer(*re, srcBuffer, 0, "source"); - } + auto srcBuffer = createTexture(*re, kHomescreenPath); + + const FloatRect layerRect(0, 0, width, height); + LayerSettings layer{ + .geometry = + Geometry{ + .boundaries = layerRect, + }, + .source = + PixelSource{ + .buffer = + Buffer{ + .buffer = srcBuffer, + }, + }, + .alpha = half(1.0f), + }; + auto layers = std::vector<LayerSettings>{layer}; + benchDrawLayers(*re, layers, benchState, "homescreen"); +} + +template <class... Args> +void BM_homescreen_blur(benchmark::State& benchState, Args&&... args) { + auto args_tuple = std::make_tuple(std::move(args)...); + auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); + + auto [width, height] = getDisplaySize(); + auto srcBuffer = createTexture(*re, kHomescreenPath); const FloatRect layerRect(0, 0, width, height); LayerSettings layer{ @@ -223,14 +271,55 @@ void BM_blur(benchmark::State& benchState, Args&&... args) { }; auto layers = std::vector<LayerSettings>{layer, blurLayer}; - benchDrawLayers(*re, layers, benchState, "blurred"); + benchDrawLayers(*re, layers, benchState, "homescreen_blurred"); +} + +template <class... Args> +void BM_homescreen_edgeExtension(benchmark::State& benchState, Args&&... args) { + auto args_tuple = std::make_tuple(std::move(args)...); + auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)), + static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple))); + + auto [width, height] = getDisplaySize(); + auto srcBuffer = createTexture(*re, kHomescreenPath); + + LayerSettings layer{ + .geometry = + Geometry{ + .boundaries = FloatRect(0, 0, width, height), + }, + .source = + PixelSource{ + .buffer = + Buffer{ + .buffer = srcBuffer, + // Part of the screen is not covered by the texture but + // will be filled in by the shader + .textureTransform = + mat4(mat3(), + vec3(width * 0.3f, height * 0.3f, 0.0f)), + }, + }, + .alpha = half(1.0f), + .edgeExtensionEffect = + EdgeExtensionEffect(/* left */ true, + /* right */ false, /* top */ true, /* bottom */ false), + }; + auto layers = std::vector<LayerSettings>{layer}; + benchDrawLayers(*re, layers, benchState, "homescreen_edge_extension"); } -BENCHMARK_CAPTURE(BM_blur, gaussian, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL, - RenderEngine::BlurAlgorithm::GAUSSIAN); +BENCHMARK_CAPTURE(BM_homescreen_blur, gaussian, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::GAUSSIAN); -BENCHMARK_CAPTURE(BM_blur, kawase, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL, - RenderEngine::BlurAlgorithm::KAWASE); +BENCHMARK_CAPTURE(BM_homescreen_blur, kawase, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE); -BENCHMARK_CAPTURE(BM_blur, kawase_dual_filter, RenderEngine::Threaded::YES, +BENCHMARK_CAPTURE(BM_homescreen_blur, kawase_dual_filter, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER); + +BENCHMARK_CAPTURE(BM_homescreen, SkiaGLThreaded, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL); + +BENCHMARK_CAPTURE(BM_homescreen_edgeExtension, SkiaGLThreaded, RenderEngine::Threaded::YES, + RenderEngine::GraphicsApi::GL); diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index b640983a55..280ec19a4c 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -102,6 +102,9 @@ struct DisplaySettings { Local, }; TonemapStrategy tonemapStrategy = TonemapStrategy::Libtonemap; + + // For now, meaningful primarily when the TonemappingStrategy is Local + float targetHdrSdrRatio = 1.f; }; static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) { diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 9bc2c48a8a..0fd982e812 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -97,6 +97,7 @@ struct PrimeCacheConfig { bool cacheImageDimmedLayers = true; bool cacheClippedLayers = true; bool cacheShadowLayers = true; + bool cacheEdgeExtension = true; bool cachePIPImageLayers = true; bool cacheTransparentImageDimmedLayers = true; bool cacheClippedDimmedImageLayers = true; @@ -208,6 +209,13 @@ public: const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence); + virtual ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr, + base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, + base::borrowed_fd&& hdrFence, float hdrSdrRatio, + ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap); + // Clean-up method that should be called on the main thread after the // drawFence returned by drawLayers fires. This method will free up // resources used by the most recently drawn frame. If the frame is still @@ -285,8 +293,7 @@ protected: // Update protectedContext mode depending on whether or not any layer has a protected buffer. void updateProtectedContext(const std::vector<LayerSettings>&, - const std::shared_ptr<ExternalTexture>&); - + std::vector<const ExternalTexture*>); // Attempt to switch RenderEngine into and out of protectedContext mode virtual void useProtectedContext(bool useProtectedContext) = 0; @@ -294,6 +301,13 @@ protected: const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0; + + virtual void drawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) = 0; }; struct RenderEngineCreationArgs { diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index a8c242a86f..fb8331d870 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -46,6 +46,17 @@ public: ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, base::unique_fd&&)); + MOCK_METHOD7(drawGainmap, + ftl::Future<FenceResult>(const std::shared_ptr<ExternalTexture>&, + base::borrowed_fd&&, + const std::shared_ptr<ExternalTexture>&, + base::borrowed_fd&&, float, ui::Dataspace, + const std::shared_ptr<ExternalTexture>&)); + MOCK_METHOD8(drawGainmapInternal, + void(const std::shared_ptr<std::promise<FenceResult>>&&, + const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, + const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float, + ui::Dataspace, const std::shared_ptr<ExternalTexture>&)); MOCK_METHOD5(drawLayersInternal, void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&, const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&, diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h index 74daf471fa..a570ad01a2 100644 --- a/libs/renderengine/skia/AutoBackendTexture.h +++ b/libs/renderengine/skia/AutoBackendTexture.h @@ -16,9 +16,9 @@ #pragma once -#include <GrDirectContext.h> #include <SkImage.h> #include <SkSurface.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <sys/types.h> #include <ui/GraphicTypes.h> diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp index 59b06568a0..57041ee6a1 100644 --- a/libs/renderengine/skia/Cache.cpp +++ b/libs/renderengine/skia/Cache.cpp @@ -27,6 +27,8 @@ #include "ui/Rect.h" #include "utils/Timers.h" +#include <com_android_graphics_libgui_flags.h> + namespace android::renderengine::skia { namespace { @@ -619,6 +621,32 @@ static void drawP3ImageLayers(SkiaRenderEngine* renderengine, const DisplaySetti } } +static void drawEdgeExtensionLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, + const std::shared_ptr<ExternalTexture>& dstTexture, + const std::shared_ptr<ExternalTexture>& srcTexture) { + const Rect& displayRect = display.physicalDisplay; + // Make the layer + LayerSettings layer{ + // Make the layer bigger than the texture + .geometry = Geometry{.boundaries = FloatRect(0, 0, displayRect.width(), + displayRect.height())}, + .source = PixelSource{.buffer = + Buffer{ + .buffer = srcTexture, + .isOpaque = 1, + }}, + // The type of effect does not affect the shader's uniforms, but the layer must have a + // valid EdgeExtensionEffect to apply the shader + .edgeExtensionEffect = + EdgeExtensionEffect(true /* left */, false, false, true /* bottom */), + }; + for (float alpha : {0.5, 0.0, 1.0}) { + layer.alpha = alpha; + auto layers = std::vector<LayerSettings>{layer}; + renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); + } +} + // // The collection of shaders cached here were found by using perfetto to record shader compiles // during actions that involve RenderEngine, logging the layer settings, and the shader code @@ -761,6 +789,12 @@ void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig co // Draw layers for b/185569240. drawClippedLayers(renderengine, display, dstTexture, texture); } + + if (com::android::graphics::libgui::flags::edge_extension_shader() && + config.cacheEdgeExtension) { + drawEdgeExtensionLayers(renderengine, display, dstTexture, texture); + drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture); + } } if (config.cachePIPImageLayers) { diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index af24600ade..4ef7d5bccb 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -21,16 +21,15 @@ #include "SkiaGLRenderEngine.h" -#include "compat/SkiaGpuContext.h" - #include <EGL/egl.h> #include <EGL/eglext.h> -#include <GrContextOptions.h> -#include <GrTypes.h> #include <android-base/stringprintf.h> #include <common/trace.h> -#include <gl/GrGLInterface.h> +#include <include/gpu/ganesh/GrContextOptions.h> +#include <include/gpu/ganesh/GrTypes.h> #include <include/gpu/ganesh/gl/GrGLDirectContext.h> +#include <include/gpu/ganesh/gl/GrGLInterface.h> +#include <log/log_main.h> #include <sync/sync.h> #include <ui/DebugUtils.h> @@ -40,7 +39,7 @@ #include <numeric> #include "GLExtensions.h" -#include "log/log_main.h" +#include "compat/SkiaGpuContext.h" namespace android { namespace renderengine { diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index bd177e60ba..765103889e 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -20,9 +20,10 @@ #include <EGL/egl.h> #include <EGL/eglext.h> #include <GLES2/gl2.h> -#include <GrDirectContext.h> #include <SkSurface.h> #include <android-base/thread_annotations.h> +#include <include/gpu/ganesh/GrContextOptions.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <renderengine/ExternalTexture.h> #include <renderengine/RenderEngine.h> #include <sys/types.h> @@ -32,7 +33,6 @@ #include "AutoBackendTexture.h" #include "EGL/egl.h" -#include "GrContextOptions.h" #include "SkImageInfo.h" #include "SkiaRenderEngine.h" #include "android-base/macros.h" diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index d58f303e70..ec9d3efb88 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -20,9 +20,6 @@ #include "SkiaRenderEngine.h" -#include <GrBackendSemaphore.h> -#include <GrContextOptions.h> -#include <GrTypes.h> #include <SkBlendMode.h> #include <SkCanvas.h> #include <SkColor.h> @@ -56,6 +53,9 @@ #include <common/FlagManager.h> #include <common/trace.h> #include <gui/FenceMonitor.h> +#include <include/gpu/ganesh/GrBackendSemaphore.h> +#include <include/gpu/ganesh/GrContextOptions.h> +#include <include/gpu/ganesh/GrTypes.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <pthread.h> #include <src/core/SkTraceEventCommon.h> @@ -75,6 +75,7 @@ #include "ColorSpaces.h" #include "compat/SkiaGpuContext.h" #include "filters/BlurFilter.h" +#include "filters/GainmapFactory.h" #include "filters/GaussianBlurFilter.h" #include "filters/KawaseBlurDualFilter.h" #include "filters/KawaseBlurFilter.h" @@ -238,12 +239,22 @@ static inline SkM44 getSkM44(const android::mat4& matrix) { static inline SkPoint3 getSkPoint3(const android::vec3& vector) { return SkPoint3::Make(vector.x, vector.y, vector.z); } + } // namespace namespace android { namespace renderengine { namespace skia { +namespace { +void trace(sp<Fence> fence) { + if (SFTRACE_ENABLED()) { + static gui::FenceMonitor sMonitor("RE Completion"); + sMonitor.queueFence(std::move(fence)); + } +} +} // namespace + using base::StringAppendF; std::future<void> SkiaRenderEngine::primeCache(PrimeCacheConfig config) { @@ -544,13 +555,15 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( const auto usingLocalTonemap = parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local && hdrType != HdrRenderType::SDR && - shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); - + shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr) && + (hdrType != HdrRenderType::DISPLAY_HDR || + parameters.display.targetHdrSdrRatio < parameters.layerDimmingRatio); if (usingLocalTonemap) { - static MouriMap kMapper; - const float ratio = + const float inputRatio = hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio; - shader = kMapper.mouriMap(getActiveContext(), shader, ratio); + static MouriMap kMapper; + shader = kMapper.mouriMap(getActiveContext(), shader, inputRatio, + parameters.display.targetHdrSdrRatio); } // disable tonemapping if we already locally tonemapped @@ -1187,11 +1200,48 @@ void SkiaRenderEngine::drawLayersInternal( LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); + trace(drawFence); + resultPromise->set_value(std::move(drawFence)); +} - if (SFTRACE_ENABLED()) { - static gui::FenceMonitor sMonitor("RE Completion"); - sMonitor.queueFence(drawFence); - } +void SkiaRenderEngine::drawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + auto context = getActiveContext(); + auto surfaceTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true); + sk_sp<SkSurface> dstSurface = + surfaceTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR); + + waitFence(context, sdrFence); + const auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), false); + const auto sdrImage = sdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType); + const auto sdrShader = + sdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}), + nullptr); + waitFence(context, hdrFence); + const auto hdrTextureRef = getOrCreateBackendTexture(hdr->getBuffer(), false); + const auto hdrImage = hdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType); + const auto hdrShader = + hdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}), + nullptr); + + static GainmapFactory kGainmapFactory; + const auto gainmapShader = kGainmapFactory.createSkShader(sdrShader, hdrShader, hdrSdrRatio); + + const auto canvas = dstSurface->getCanvas(); + SkPaint paint; + paint.setShader(gainmapShader); + paint.setBlendMode(SkBlendMode::kSrc); + canvas->drawPaint(paint); + + auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface)); + trace(drawFence); resultPromise->set_value(std::move(drawFence)); } diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index 224a1cad92..b5f8898263 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -18,11 +18,12 @@ #define SF_SKIARENDERENGINE_H_ #include <renderengine/RenderEngine.h> -#include <sys/types.h> -#include <GrBackendSemaphore.h> -#include <SkSurface.h> #include <android-base/thread_annotations.h> +#include <include/core/SkImageInfo.h> +#include <include/core/SkSurface.h> +#include <include/gpu/ganesh/GrBackendSemaphore.h> +#include <include/gpu/ganesh/GrContextOptions.h> #include <renderengine/ExternalTexture.h> #include <renderengine/RenderEngine.h> #include <sys/types.h> @@ -32,8 +33,6 @@ #include <unordered_map> #include "AutoBackendTexture.h" -#include "GrContextOptions.h" -#include "SkImageInfo.h" #include "android-base/macros.h" #include "compat/SkiaGpuContext.h" #include "debug/SkiaCapture.h" @@ -143,6 +142,13 @@ private: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override final; + void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, + base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, + base::borrowed_fd&& hdrFence, float hdrSdrRatio, + ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) override final; void dump(std::string& result) override final; diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index d89e818cdc..677a2b63b2 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -24,12 +24,12 @@ #include "GaneshVkRenderEngine.h" #include "compat/SkiaGpuContext.h" -#include <GrBackendSemaphore.h> -#include <GrContextOptions.h> -#include <GrDirectContext.h> +#include <include/gpu/ganesh/GrBackendSemaphore.h> +#include <include/gpu/ganesh/GrContextOptions.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> #include <include/gpu/ganesh/vk/GrVkDirectContext.h> -#include <vk/GrVkTypes.h> +#include <include/gpu/ganesh/vk/GrVkTypes.h> #include <android-base/stringprintf.h> #include <common/trace.h> diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp index 3fbc6ca22e..88282e7f5a 100644 --- a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp +++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp @@ -21,12 +21,12 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <include/core/SkImage.h> -#include <include/gpu/GrDirectContext.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <include/gpu/ganesh/SkImageGanesh.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <include/gpu/ganesh/gl/GrGLBackendSurface.h> #include <include/gpu/ganesh/vk/GrVkBackendSurface.h> -#include <include/gpu/vk/GrVkTypes.h> +#include <include/gpu/ganesh/vk/GrVkTypes.h> #include "skia/ColorSpaces.h" #include "skia/compat/SkiaBackendTexture.h" diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.h b/libs/renderengine/skia/compat/GaneshBackendTexture.h index 5cf8647801..4337df18d4 100644 --- a/libs/renderengine/skia/compat/GaneshBackendTexture.h +++ b/libs/renderengine/skia/compat/GaneshBackendTexture.h @@ -21,7 +21,7 @@ #include <include/android/GrAHardwareBufferUtils.h> #include <include/core/SkColorSpace.h> -#include <include/gpu/GrDirectContext.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <android-base/macros.h> diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp index b121fe83eb..931f8433da 100644 --- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp +++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp @@ -19,12 +19,12 @@ #include <include/core/SkImageInfo.h> #include <include/core/SkSurface.h> #include <include/core/SkTraceMemoryDump.h> -#include <include/gpu/GrDirectContext.h> -#include <include/gpu/GrTypes.h> +#include <include/gpu/ganesh/GrDirectContext.h> +#include <include/gpu/ganesh/GrTypes.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <include/gpu/ganesh/gl/GrGLDirectContext.h> +#include <include/gpu/ganesh/gl/GrGLInterface.h> #include <include/gpu/ganesh/vk/GrVkDirectContext.h> -#include <include/gpu/gl/GrGLInterface.h> #include <include/gpu/vk/VulkanBackendContext.h> #include "../AutoBackendTexture.h" diff --git a/libs/renderengine/skia/compat/SkiaBackendTexture.h b/libs/renderengine/skia/compat/SkiaBackendTexture.h index 09877a5ede..fa12624ff7 100644 --- a/libs/renderengine/skia/compat/SkiaBackendTexture.h +++ b/libs/renderengine/skia/compat/SkiaBackendTexture.h @@ -18,7 +18,7 @@ #include <include/android/GrAHardwareBufferUtils.h> #include <include/core/SkColorSpace.h> -#include <include/gpu/GrDirectContext.h> +#include <include/gpu/ganesh/GrDirectContext.h> #include <android/hardware_buffer.h> #include <ui/GraphicTypes.h> diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h index 9fa6fb8521..0bd8283987 100644 --- a/libs/renderengine/skia/compat/SkiaGpuContext.h +++ b/libs/renderengine/skia/compat/SkiaGpuContext.h @@ -20,10 +20,10 @@ #define LOG_TAG "RenderEngine" #include <include/core/SkSurface.h> -#include <include/gpu/GrDirectContext.h> -#include <include/gpu/gl/GrGLInterface.h> +#include <include/gpu/ganesh/GrDirectContext.h> +#include <include/gpu/ganesh/gl/GrGLInterface.h> #include <include/gpu/graphite/Context.h> -#include "include/gpu/vk/VulkanBackendContext.h" +#include <include/gpu/vk/VulkanBackendContext.h> #include "SkiaBackendTexture.h" diff --git a/libs/renderengine/skia/filters/GainmapFactory.cpp b/libs/renderengine/skia/filters/GainmapFactory.cpp new file mode 100644 index 0000000000..e4d4fe96f8 --- /dev/null +++ b/libs/renderengine/skia/filters/GainmapFactory.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "GainmapFactory.h" + +#include <log/log.h> + +namespace android { +namespace renderengine { +namespace skia { +namespace { + +sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl) { + auto [effect, error] = SkRuntimeEffect::MakeForShader(sksl); + LOG_ALWAYS_FATAL_IF(!effect, "RuntimeShader error: %s", error.c_str()); + return effect; +} + +// Please refer to https://developer.android.com/media/platform/hdr-image-format#gain_map-generation +static const SkString kGainmapShader = SkString(R"( + uniform shader sdr; + uniform shader hdr; + uniform float mapMaxLog2; + + const float mapMinLog2 = 0.0; + const float mapGamma = 1.0; + const float offsetSdr = 0.015625; + const float offsetHdr = 0.015625; + + float luminance(vec3 linearColor) { + return 0.2126 * linearColor.r + 0.7152 * linearColor.g + 0.0722 * linearColor.b; + } + + vec4 main(vec2 xy) { + float sdrY = luminance(toLinearSrgb(sdr.eval(xy).rgb)); + float hdrY = luminance(toLinearSrgb(hdr.eval(xy).rgb)); + float pixelGain = (hdrY + offsetHdr) / (sdrY + offsetSdr); + float logRecovery = (log2(pixelGain) - mapMinLog2) / (mapMaxLog2 - mapMinLog2); + return vec4(pow(clamp(logRecovery, 0.0, 1.0), mapGamma)); + } +)"); +} // namespace + +const float INTERPOLATION_STRENGTH_VALUE = 0.7f; + +GainmapFactory::GainmapFactory() : mEffect(makeEffect(kGainmapShader)) {} + +sk_sp<SkShader> GainmapFactory::createSkShader(const sk_sp<SkShader>& sdr, + const sk_sp<SkShader>& hdr, float hdrSdrRatio) { + SkRuntimeShaderBuilder shaderBuilder(mEffect); + shaderBuilder.child("sdr") = sdr; + shaderBuilder.child("hdr") = hdr; + shaderBuilder.uniform("mapMaxLog2") = std::log2(hdrSdrRatio); + return shaderBuilder.makeShader(); +} + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/GainmapFactory.h b/libs/renderengine/skia/filters/GainmapFactory.h new file mode 100644 index 0000000000..7aea5e2195 --- /dev/null +++ b/libs/renderengine/skia/filters/GainmapFactory.h @@ -0,0 +1,44 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <SkRuntimeEffect.h> +#include <SkShader.h> + +namespace android { +namespace renderengine { +namespace skia { + +/** + * Generates a shader for computing a gainmap, given an SDR base image and its idealized HDR + * rendition. The shader follows the procedure in the UltraHDR spec: + * https://developer.android.com/media/platform/hdr-image-format#gain_map-generation, but makes some + * simplifying assumptions about metadata typical for RenderEngine's usage. + */ +class GainmapFactory { +public: + GainmapFactory(); + // Generates the gainmap shader. The hdrSdrRatio is the max_content_boost in the UltraHDR + // specification. + sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& sdr, const sk_sp<SkShader>& hdr, + float hdrSdrRatio); + +private: + sk_sp<SkRuntimeEffect> mEffect; +}; +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp index b45893919c..b099bcf3d7 100644 --- a/libs/renderengine/skia/filters/MouriMap.cpp +++ b/libs/renderengine/skia/filters/MouriMap.cpp @@ -67,7 +67,7 @@ const SkString kBlur(R"( float result = 0.0; for (int y = -2; y <= 2; y++) { for (int x = -2; x <= 2; x++) { - result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r; + result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r; } } return float4(float3(exp2(result)), 1.0); @@ -78,18 +78,20 @@ const SkString kTonemap(R"( uniform shader lux; uniform float scaleFactor; uniform float hdrSdrRatio; + uniform float targetHdrSdrRatio; vec4 main(vec2 xy) { float localMax = lux.eval(xy * scaleFactor).r; float4 rgba = image.eval(xy); float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio; - if (localMax <= 1.0) { + if (localMax <= targetHdrSdrRatio) { return float4(fromLinearSrgb(linear), rgba.a); } float maxRGB = max(linear.r, max(linear.g, linear.b)); localMax = max(localMax, maxRGB); - float gain = (1 + maxRGB / (localMax * localMax)) / (1 + maxRGB); + float gain = (1 + maxRGB * (targetHdrSdrRatio / (localMax * localMax))) + / (1 + maxRGB / targetHdrSdrRatio); return float4(fromLinearSrgb(linear * gain), rgba.a); } )"); @@ -114,10 +116,10 @@ MouriMap::MouriMap() mTonemap(makeEffect(kTonemap)) {} sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, - float hdrSdrRatio) { + float hdrSdrRatio, float targetHdrSdrRatio) { auto downchunked = downchunk(context, input, hdrSdrRatio); auto localLux = blur(context, downchunked.get()); - return tonemap(input, localLux.get(), hdrSdrRatio); + return tonemap(input, localLux.get(), hdrSdrRatio, targetHdrSdrRatio); } sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input, @@ -166,8 +168,8 @@ sk_sp<SkImage> MouriMap::blur(SkiaGpuContext* context, SkImage* input) const { LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__); return makeImage(blurSurface.get(), blurBuilder); } -sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, - float hdrSdrRatio) const { +sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio, + float targetHdrSdrRatio) const { static constexpr float kScaleFactor = 1.0f / 128.0f; SkRuntimeShaderBuilder tonemapBuilder(mTonemap); tonemapBuilder.child("image") = input; @@ -176,8 +178,9 @@ sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone)); tonemapBuilder.uniform("scaleFactor") = kScaleFactor; tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio; + tonemapBuilder.uniform("targetHdrSdrRatio") = targetHdrSdrRatio; return tonemapBuilder.makeShader(); } } // namespace skia } // namespace renderengine -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h index 3c0df8abf0..9ba2b6ff9d 100644 --- a/libs/renderengine/skia/filters/MouriMap.h +++ b/libs/renderengine/skia/filters/MouriMap.h @@ -64,13 +64,16 @@ public: // Apply the MouriMap tonemmaping operator to the input. // The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger // then 1.0 means that there is headroom above the SDR region. - sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio); + // Similarly, the target HDR/SDR ratio describes the luminance range of the output. + sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputHdrSdrRatio, + float targetHdrSdrRatio); private: sk_sp<SkImage> downchunk(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio) const; sk_sp<SkImage> blur(SkiaGpuContext* context, SkImage* input) const; - sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio) const; + sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio, + float targetHdrSdrRatio) const; const sk_sp<SkRuntimeEffect> mCrosstalkAndChunk16x16; const sk_sp<SkRuntimeEffect> mChunk8x8; const sk_sp<SkRuntimeEffect> mBlur; @@ -78,4 +81,4 @@ private: }; } // namespace skia } // namespace renderengine -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index f5a90fdcfd..c187f93089 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -249,6 +249,16 @@ void RenderEngineThreaded::drawLayersInternal( return; } +void RenderEngineThreaded::drawGainmapInternal( + const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) { + resultPromise->set_value(Fence::NO_FENCE); + return; +} + ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( const DisplaySettings& display, const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) { @@ -262,7 +272,7 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( mFunctionCalls.push( [resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) { SFTRACE_NAME("REThreaded::drawLayers"); - instance.updateProtectedContext(layers, buffer); + instance.updateProtectedContext(layers, {buffer.get()}); instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer, base::unique_fd(fd)); }); @@ -271,6 +281,30 @@ ftl::Future<FenceResult> RenderEngineThreaded::drawLayers( return resultFuture; } +ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap( + const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence, + float hdrSdrRatio, ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) { + SFTRACE_CALL(); + const auto resultPromise = std::make_shared<std::promise<FenceResult>>(); + std::future<FenceResult> resultFuture = resultPromise->get_future(); + { + std::lock_guard lock(mThreadMutex); + mNeedsPostRenderCleanup = true; + mFunctionCalls.push([resultPromise, sdr, sdrFence = std::move(sdrFence), hdr, + hdrFence = std::move(hdrFence), hdrSdrRatio, dataspace, + gainmap](renderengine::RenderEngine& instance) mutable { + SFTRACE_NAME("REThreaded::drawGainmap"); + instance.updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()}); + instance.drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr, + std::move(hdrFence), hdrSdrRatio, dataspace, gainmap); + }); + } + mCondition.notify_one(); + return resultFuture; +} + int RenderEngineThreaded::getContextPriority() { std::promise<int> resultPromise; std::future<int> resultFuture = resultPromise.get_future(); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index d4997d6c93..cb6e924d81 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -55,6 +55,12 @@ public: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override; + ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr, + base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, + base::borrowed_fd&& hdrFence, float hdrSdrRatio, + ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) override; int getContextPriority() override; bool supportsBackgroundBlur() override; @@ -71,6 +77,13 @@ protected: const std::vector<LayerSettings>& layers, const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) override; + void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, + const std::shared_ptr<ExternalTexture>& sdr, + base::borrowed_fd&& sdrFence, + const std::shared_ptr<ExternalTexture>& hdr, + base::borrowed_fd&& hdrFence, float hdrSdrRatio, + ui::Dataspace dataspace, + const std::shared_ptr<ExternalTexture>& gainmap) override; private: void threadMain(CreateInstanceFactory factory); diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index e5af7406ed..8b13d78840 100644 --- a/libs/ui/DisplayIdentification.cpp +++ b/libs/ui/DisplayIdentification.cpp @@ -26,6 +26,7 @@ #include <ftl/hash.h> #include <log/log.h> #include <ui/DisplayIdentification.h> +#include <ui/Size.h> namespace android { namespace { @@ -46,6 +47,10 @@ std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) { return view[3]; } +bool isDetailedTimingDescriptor(const byte_view& view) { + return view[0] != 0 && view[1] != 0; +} + 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')); @@ -219,6 +224,8 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { std::string_view displayName; std::string_view serialNumber; std::string_view asciiText; + ui::Size preferredDTDPixelSize; + ui::Size preferredDTDPhysicalSize; constexpr size_t kDescriptorCount = 4; constexpr size_t kDescriptorLength = 18; @@ -243,6 +250,35 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { serialNumber = parseEdidText(descriptor); break; } + } else if (isDetailedTimingDescriptor(view)) { + static constexpr size_t kHorizontalPhysicalLsbOffset = 12; + static constexpr size_t kHorizontalPhysicalMsbOffset = 14; + static constexpr size_t kVerticalPhysicalLsbOffset = 13; + static constexpr size_t kVerticalPhysicalMsbOffset = 14; + const uint32_t hSize = + static_cast<uint32_t>(view[kHorizontalPhysicalLsbOffset] | + ((view[kHorizontalPhysicalMsbOffset] >> 4) << 8)); + const uint32_t vSize = + static_cast<uint32_t>(view[kVerticalPhysicalLsbOffset] | + ((view[kVerticalPhysicalMsbOffset] & 0b1111) << 8)); + + static constexpr size_t kHorizontalPixelLsbOffset = 2; + static constexpr size_t kHorizontalPixelMsbOffset = 4; + static constexpr size_t kVerticalPixelLsbOffset = 5; + static constexpr size_t kVerticalPixelMsbOffset = 7; + + const uint8_t hLsb = view[kHorizontalPixelLsbOffset]; + const uint8_t hMsb = view[kHorizontalPixelMsbOffset]; + const int32_t hPixel = hLsb + ((hMsb & 0xF0) << 4); + + const uint8_t vLsb = view[kVerticalPixelLsbOffset]; + const uint8_t vMsb = view[kVerticalPixelMsbOffset]; + const int32_t vPixel = vLsb + ((vMsb & 0xF0) << 4); + + preferredDTDPixelSize.setWidth(hPixel); + preferredDTDPixelSize.setHeight(vPixel); + preferredDTDPhysicalSize.setWidth(hSize); + preferredDTDPhysicalSize.setHeight(vSize); } view = view.subspan(kDescriptorLength); @@ -297,14 +333,22 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { } } - return Edid{.manufacturerId = manufacturerId, - .productId = productId, - .pnpId = *pnpId, - .modelHash = modelHash, - .displayName = displayName, - .manufactureOrModelYear = manufactureOrModelYear, - .manufactureWeek = manufactureWeek, - .cea861Block = cea861Block}; + DetailedTimingDescriptor preferredDetailedTimingDescriptor{ + .pixelSizeCount = preferredDTDPixelSize, + .physicalSizeInMm = preferredDTDPhysicalSize, + }; + + return Edid{ + .manufacturerId = manufacturerId, + .productId = productId, + .pnpId = *pnpId, + .modelHash = modelHash, + .displayName = displayName, + .manufactureOrModelYear = manufactureOrModelYear, + .manufactureWeek = manufactureWeek, + .cea861Block = cea861Block, + .preferredDetailedTimingDescriptor = preferredDetailedTimingDescriptor, + }; } std::optional<PnpId> getPnpId(uint16_t manufacturerId) { @@ -336,9 +380,12 @@ std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( } const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash); - return DisplayIdentificationInfo{.id = displayId, - .name = std::string(edid->displayName), - .deviceProductInfo = buildDeviceProductInfo(*edid)}; + return DisplayIdentificationInfo{ + .id = displayId, + .name = std::string(edid->displayName), + .deviceProductInfo = buildDeviceProductInfo(*edid), + .preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor, + }; } PhysicalDisplayId getVirtualDisplayId(uint32_t id) { diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index ffb6cdb6ef..b0c6e44b2b 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -388,8 +388,8 @@ status_t GraphicBuffer::lockAsync(uint64_t inProducerUsage, uint64_t inConsumerU } } - const uint64_t usage = static_cast<uint64_t>( - android_convertGralloc1To0Usage(inProducerUsage, inConsumerUsage)); + const uint64_t usage = static_cast<uint64_t>(ANDROID_NATIVE_UNSIGNED_CAST( + android_convertGralloc1To0Usage(inProducerUsage, inConsumerUsage))); auto result = getBufferMapper().lock(handle, usage, rect, base::unique_fd{fenceFd}); diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index b6ab2f5a47..7b5a27d9e1 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -208,8 +208,10 @@ status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint32_t usage, status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage, uint64_t consumerUsage, const Rect& bounds, void** vaddr, int fenceFd) { - return lockAsync(handle, android_convertGralloc1To0Usage(producerUsage, consumerUsage), bounds, - vaddr, fenceFd); + return lockAsync(handle, + ANDROID_NATIVE_UNSIGNED_CAST( + android_convertGralloc1To0Usage(producerUsage, consumerUsage)), + bounds, vaddr, fenceFd); } status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage, diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index 8bc2017b55..648e024d86 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -25,6 +25,7 @@ #include <ui/DeviceProductInfo.h> #include <ui/DisplayId.h> +#include <ui/Size.h> #define LEGACY_DISPLAY_TYPE_PRIMARY 0 #define LEGACY_DISPLAY_TYPE_EXTERNAL 1 @@ -33,10 +34,16 @@ namespace android { using DisplayIdentificationData = std::vector<uint8_t>; +struct DetailedTimingDescriptor { + ui::Size pixelSizeCount; + ui::Size physicalSizeInMm; +}; + struct DisplayIdentificationInfo { PhysicalDisplayId id; std::string name; std::optional<DeviceProductInfo> deviceProductInfo; + std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor; }; struct ExtensionBlock { @@ -68,6 +75,7 @@ struct Edid { uint8_t manufactureOrModelYear; uint8_t manufactureWeek; std::optional<Cea861ExtensionBlock> cea861Block; + std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor; }; bool isEdid(const DisplayIdentificationData&); diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp index 721b46688e..76e3f66e1b 100644 --- a/libs/ui/tests/DisplayIdentification_test.cpp +++ b/libs/ui/tests/DisplayIdentification_test.cpp @@ -194,6 +194,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(21, edid->manufactureOrModelYear); EXPECT_EQ(0, edid->manufactureWeek); EXPECT_FALSE(edid->cea861Block); + EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(261, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(163, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getExternalEdid()); ASSERT_TRUE(edid); @@ -206,6 +210,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(22, edid->manufactureOrModelYear); EXPECT_EQ(2, edid->manufactureWeek); EXPECT_FALSE(edid->cea861Block); + EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(641, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(400, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getExternalEedid()); ASSERT_TRUE(edid); @@ -224,6 +232,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(0, physicalAddress.b); EXPECT_EQ(0, physicalAddress.c); EXPECT_EQ(0, physicalAddress.d); + EXPECT_EQ(1366, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(768, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(160, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(90, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getPanasonicTvEdid()); ASSERT_TRUE(edid); @@ -242,6 +254,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(0, physicalAddress.b); EXPECT_EQ(0, physicalAddress.c); EXPECT_EQ(0, physicalAddress.d); + EXPECT_EQ(1920, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(1080, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(698, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(392, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getHisenseTvEdid()); ASSERT_TRUE(edid); @@ -260,6 +276,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(2, physicalAddress.b); EXPECT_EQ(3, physicalAddress.c); EXPECT_EQ(4, physicalAddress.d); + EXPECT_EQ(1920, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(1080, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(575, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(323, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); edid = parseEdid(getCtlDisplayEdid()); ASSERT_TRUE(edid); @@ -273,6 +293,10 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(0xff, edid->manufactureWeek); ASSERT_TRUE(edid->cea861Block); EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock); + EXPECT_EQ(1360, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); + EXPECT_EQ(768, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); + EXPECT_EQ(521, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width); + EXPECT_EQ(293, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height); } TEST(DisplayIdentificationTest, parseInvalidEdid) { diff --git a/opengl/OWNERS b/opengl/OWNERS index 3d60a1dad6..645a578c76 100644 --- a/opengl/OWNERS +++ b/opengl/OWNERS @@ -2,5 +2,4 @@ chrisforbes@google.com cnorthrop@google.com ianelliott@google.com jessehall@google.com -lpy@google.com -vantablack@google.com +tomnom@google.com diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index 16de3908f8..5159ffe86b 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -40,9 +40,6 @@ ndk_library { symbol_file: "libEGL.map.txt", first_version: "9", unversioned_until: "current", - export_header_libs: [ - "libEGL_headers", - ], } ndk_library { @@ -50,9 +47,6 @@ ndk_library { symbol_file: "libGLESv1_CM.map.txt", first_version: "9", unversioned_until: "current", - export_header_libs: [ - "libGLESv1_CM_headers", - ], } ndk_library { @@ -60,9 +54,6 @@ ndk_library { symbol_file: "libGLESv2.map.txt", first_version: "9", unversioned_until: "current", - export_header_libs: [ - "libGLESv2_headers", - ], } ndk_library { @@ -70,9 +61,6 @@ ndk_library { symbol_file: "libGLESv3.map.txt", first_version: "18", unversioned_until: "current", - export_header_libs: [ - "libGLESv3_headers", - ], } cc_defaults { diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp index 00161e6f4c..7628745537 100644 --- a/services/gpuservice/gpuwork/GpuWork.cpp +++ b/services/gpuservice/gpuwork/GpuWork.cpp @@ -44,7 +44,7 @@ #include "gpuwork/gpuWork.h" -#define ONE_MS_IN_NS (10000000) +#define MSEC_PER_NSEC (1000LU * 1000LU) namespace android { namespace gpuwork { @@ -385,10 +385,11 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* dat ALOGI("pullWorkAtoms: after random selection: uids.size() == %zu", uids.size()); auto now = std::chrono::steady_clock::now(); - long long duration = - std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint) - .count(); - if (duration > std::numeric_limits<int32_t>::max() || duration < 0) { + int32_t duration = + static_cast<int32_t>( + std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint) + .count()); + if (duration < 0) { // This is essentially impossible. If it does somehow happen, give up, // but still clear the map. clearMap(); @@ -404,13 +405,14 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* dat } const UidTrackingInfo& info = it->second; - uint64_t total_active_duration_ms = info.total_active_duration_ns / ONE_MS_IN_NS; - uint64_t total_inactive_duration_ms = info.total_inactive_duration_ns / ONE_MS_IN_NS; + int32_t total_active_duration_ms = + static_cast<int32_t>(info.total_active_duration_ns / MSEC_PER_NSEC); + int32_t total_inactive_duration_ms = + static_cast<int32_t>(info.total_inactive_duration_ns / MSEC_PER_NSEC); // Skip this atom if any numbers are out of range. |duration| is // already checked above. - if (total_active_duration_ms > std::numeric_limits<int32_t>::max() || - total_inactive_duration_ms > std::numeric_limits<int32_t>::max()) { + if (total_active_duration_ms < 0 || total_inactive_duration_ms < 0) { continue; } @@ -421,11 +423,11 @@ AStatsManager_PullAtomCallbackReturn GpuWork::pullWorkAtoms(AStatsEventList* dat // gpu_id bitcast_int32(gpuId), // time_duration_seconds - static_cast<int32_t>(duration), + duration, // total_active_duration_millis - static_cast<int32_t>(total_active_duration_ms), + total_active_duration_ms, // total_inactive_duration_millis - static_cast<int32_t>(total_inactive_duration_ms)); + total_inactive_duration_ms); } } clearMap(); diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h index e70da540b9..60cd2c703e 100644 --- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h +++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h @@ -125,7 +125,7 @@ private: static constexpr size_t kNumGpusHardLimit = 32; // The minimum GPU time needed to actually log stats for a UID. - static constexpr uint64_t kMinGpuTimeNanoseconds = 30U * 1000000000U; // 30 seconds. + static constexpr uint64_t kMinGpuTimeNanoseconds = 10LLU * 1000000000LLU; // 10 seconds. // The previous time point at which |mGpuWorkMap| was cleared. std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex); diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-0 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-0 Binary files differnew file mode 100644 index 0000000000..120a34dc01 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-0 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-1 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-1 Binary files differnew file mode 100644 index 0000000000..92d5bdf268 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-1 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-10 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-10 Binary files differnew file mode 100644 index 0000000000..c044c844be --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-10 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-11 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-11 Binary files differnew file mode 100644 index 0000000000..430552e625 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-11 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-12 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-12 Binary files differnew file mode 100644 index 0000000000..f7849bbe02 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-12 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-13 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-13 Binary files differnew file mode 100644 index 0000000000..2f0a655224 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-13 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-14 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-14 Binary files differnew file mode 100644 index 0000000000..bd8fb017db --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-14 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-15 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-15 Binary files differnew file mode 100644 index 0000000000..29aa2b1a40 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-15 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-16 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-16 Binary files differnew file mode 100644 index 0000000000..e00100f5c2 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-16 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-17 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-17 Binary files differnew file mode 100644 index 0000000000..db281e13fa --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-17 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-18 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-18 Binary files differnew file mode 100644 index 0000000000..5592c32a54 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-18 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-2 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-2 Binary files differnew file mode 100644 index 0000000000..5bd2b993dc --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-2 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-3 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-3 Binary files differnew file mode 100644 index 0000000000..2d89289137 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-3 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-4 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-4 Binary files differnew file mode 100644 index 0000000000..8f3f1c212a --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-4 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-5 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-5 Binary files differnew file mode 100644 index 0000000000..95b05e79ae --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-5 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-6 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-6 Binary files differnew file mode 100644 index 0000000000..497a5019a9 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-6 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-7 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-7 Binary files differnew file mode 100644 index 0000000000..9902bdec7a --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-7 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-8 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-8 Binary files differnew file mode 100644 index 0000000000..fdaaf311b5 --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-8 diff --git a/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-9 b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-9 Binary files differnew file mode 100644 index 0000000000..a197c6ad7b --- /dev/null +++ b/services/gpuservice/tests/fuzzers/corpus/seed-2024-08-29-9 diff --git a/services/gpuservice/vts/TEST_MAPPING b/services/gpuservice/vts/TEST_MAPPING index b33e9624e2..a809be18e1 100644 --- a/services/gpuservice/vts/TEST_MAPPING +++ b/services/gpuservice/vts/TEST_MAPPING @@ -1,7 +1,13 @@ { "presubmit": [ { - "name": "GpuServiceVendorTests" + "name": "GpuServiceVendorTests", + "options": [ + { + // Exclude test methods that require a physical device to run. + "exclude-annotation": "android.platform.test.annotations.RequiresDevice" + } + ] } ] } diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java index 6c16335359..5c12323633 100644 --- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java +++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java @@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; -import android.platform.test.annotations.RestrictedBuildTest; +import android.platform.test.annotations.RequiresDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -63,7 +63,7 @@ public class GpuWorkTracepointTest extends BaseHostJUnit4Test { } @VsrTest(requirements={"VSR-3.3-004"}) - @RestrictedBuildTest + @RequiresDevice @Test public void testGpuWorkPeriodTracepointFormat() throws Exception { CommandResult commandResult = getDevice().executeShellV2Command( diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index cb220abd80..ca92ab5aca 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -217,6 +217,7 @@ cc_defaults { "libcutils", "libinput", "liblog", + "libprocessgroup", "libstatslog", "libutils", ], diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp index e4d73fc974..2ef94fbb07 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -60,6 +60,7 @@ void InputFilter::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& AidlDeviceInfo& aidlInfo = mDeviceInfos.emplace_back(); aidlInfo.deviceId = info.getId(); aidlInfo.external = info.isExternal(); + aidlInfo.keyboardType = info.getKeyboardType(); } if (isFilterEnabled()) { LOG_ALWAYS_FATAL_IF(!mInputFilterRust->notifyInputDevicesChanged(mDeviceInfos).isOk()); @@ -145,6 +146,12 @@ void InputFilter::notifyConfigurationChangedLocked() { void InputFilter::dump(std::string& dump) { dump += "InputFilter:\n"; + if (isFilterEnabled()) { + std::string result; + LOG_ALWAYS_FATAL_IF(!mInputFilterRust->dumpFilter(&result).isOk()); + dump += result; + dump += "\n"; + } } } // namespace android diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 41e5247b50..b155122749 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -250,6 +250,10 @@ void InputManager::dump(std::string& dump) { mCollector->dump(dump); dump += '\n'; } + if (ENABLE_INPUT_FILTER_RUST) { + mInputFilter->dump(dump); + dump += '\n'; + } mDispatcher->dump(dump); dump += '\n'; } diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp index e74f258168..449eb45b4b 100644 --- a/services/inputflinger/InputThread.cpp +++ b/services/inputflinger/InputThread.cpp @@ -16,8 +16,14 @@ #include "InputThread.h" +#include <android-base/logging.h> +#include <com_android_input_flags.h> +#include <processgroup/processgroup.h> + namespace android { +namespace input_flags = com::android::input::flags; + namespace { // Implementation of Thread from libutils. @@ -43,6 +49,11 @@ InputThread::InputThread(std::string name, std::function<void()> loop, std::func : mName(name), mThreadWake(wake) { mThread = sp<InputThreadImpl>::make(loop); mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY); + if (input_flags::enable_input_policy_profile()) { + if (!applyInputEventProfile()) { + LOG(ERROR) << "Couldn't apply input policy profile for " << name; + } + } } InputThread::~InputThread() { @@ -63,4 +74,14 @@ bool InputThread::isCallingThread() { #endif } +bool InputThread::applyInputEventProfile() { +#if defined(__ANDROID__) + return SetTaskProfiles(mThread->getTid(), {"InputPolicy"}); +#else + // Since thread information is not available and there's no benefit of + // applying the task profile on host, return directly. + return true; +#endif +} + } // namespace android
\ No newline at end of file diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 397fedac4c..006d507a40 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -407,7 +407,8 @@ void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& // TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed // immediately by a DOWN event. pc.fade(PointerControllerInterface::Transition::IMMEDIATE); - pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED); + pc.updatePointerIcon(mShowTouchesEnabled ? PointerIconStyle::TYPE_SPOT_HOVER + : PointerIconStyle::TYPE_NOT_SPECIFIED); } else if (canUnfadeOnDisplay(args.displayId)) { pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); } @@ -792,6 +793,13 @@ bool PointerChoreographer::setPointerIcon( if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) { auto it = mStylusPointersByDevice.find(deviceId); if (it != mStylusPointersByDevice.end()) { + if (mShowTouchesEnabled) { + // If an app doesn't override the icon for the hovering stylus, show the hover icon. + auto* style = std::get_if<PointerIconStyle>(&icon); + if (style != nullptr && *style == PointerIconStyle::TYPE_NOT_SPECIFIED) { + *style = PointerIconStyle::TYPE_SPOT_HOVER; + } + } setIconForController(icon, *it->second); return true; } diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index af9d2ebeda..10fec7480d 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -298,9 +298,14 @@ "name": "CtsInputRootTestCases" } ], - "staged-platinum-postsubmit": [ + "platinum-postsubmit": [ { "name": "inputflinger_tests" } + ], + "staged-platinum-postsubmit": [ + { + "name": "libinput_tests" + } ] } diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl index b9e6a03560..5b0b9ac5ef 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/DeviceInfo.aidl @@ -23,4 +23,5 @@ package com.android.server.inputflinger; parcelable DeviceInfo { int deviceId; boolean external; + int keyboardType; }
\ No newline at end of file diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl index 994d1c4b1a..31b72319ac 100644 --- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl +++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl @@ -54,5 +54,7 @@ interface IInputFilter { /** Notifies when configuration changes */ void notifyConfigurationChanged(in InputFilterConfiguration config); + + String dumpFilter(); } diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 1a0ec48525..8b2b8432c3 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -46,6 +46,7 @@ filegroup { "InputState.cpp", "InputTarget.cpp", "LatencyAggregator.cpp", + "LatencyAggregatorWithHistograms.cpp", "LatencyTracker.cpp", "Monitor.cpp", "TouchedWindow.cpp", diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h index 4a0889f596..568d348ba9 100644 --- a/services/inputflinger/dispatcher/CancelationOptions.h +++ b/services/inputflinger/dispatcher/CancelationOptions.h @@ -32,7 +32,8 @@ struct CancelationOptions { CANCEL_POINTER_EVENTS = 1, CANCEL_NON_POINTER_EVENTS = 2, CANCEL_FALLBACK_EVENTS = 3, - ftl_last = CANCEL_FALLBACK_EVENTS, + CANCEL_HOVER_EVENTS = 4, + ftl_last = CANCEL_HOVER_EVENTS }; // The criterion to use to determine which events should be canceled. diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index bcef35021e..b0beeca43c 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -52,6 +52,7 @@ #include "Connection.h" #include "DebugConfig.h" #include "InputDispatcher.h" +#include "InputEventTimeline.h" #include "trace/InputTracer.h" #include "trace/InputTracingPerfettoBackend.h" #include "trace/ThreadedBackend.h" @@ -154,6 +155,10 @@ 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; +// Interval at which we should push the atom gathering input event latencies in +// LatencyAggregatorWithHistograms +constexpr nsecs_t LATENCY_STATISTICS_PUSH_INTERVAL = 6 * 3600 * 1000000000LL; // 6 hours + // Event log tags. See EventLogTags.logtags for reference. constexpr int LOGTAG_INPUT_INTERACTION = 62000; constexpr int LOGTAG_INPUT_FOCUS = 62001; @@ -747,7 +752,8 @@ std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState, } touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS; } - touchedWindow.addHoveringPointer(entry.deviceId, pointer); + const auto [x, y] = resolveTouchedPosition(entry); + touchedWindow.addHoveringPointer(entry.deviceId, pointer, x, y); if (canReceiveForegroundTouches(*newWindow->getInfo())) { touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND; } @@ -874,6 +880,8 @@ std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellatio return {false, true}; case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS: return {false, true}; + case CancelationOptions::Mode::CANCEL_HOVER_EVENTS: + return {true, false}; } } @@ -943,8 +951,13 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mFocusedDisplayId(ui::LogicalDisplayId::DEFAULT), mWindowTokenWithPointerCapture(nullptr), mAwaitedApplicationDisplayId(ui::LogicalDisplayId::INVALID), - mLatencyAggregator(), - mLatencyTracker(&mLatencyAggregator) { + mInputEventTimelineProcessor( + input_flags::enable_per_device_input_latency_metrics() + ? std::move(std::unique_ptr<InputEventTimelineProcessor>( + new LatencyAggregatorWithHistograms())) + : std::move(std::unique_ptr<InputEventTimelineProcessor>( + new LatencyAggregator()))), + mLatencyTracker(*mInputEventTimelineProcessor) { mLooper = sp<Looper>::make(false); mReporter = createInputReporter(); @@ -1016,6 +1029,11 @@ void InputDispatcher::dispatchOnce() { const nsecs_t nextAnrCheck = processAnrsLocked(); nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck); + if (input_flags::enable_per_device_input_latency_metrics()) { + const nsecs_t nextStatisticsPush = processLatencyStatisticsLocked(); + nextWakeupTime = std::min(nextWakeupTime, nextStatisticsPush); + } + // We are about to enter an infinitely long sleep, because we have no commands or // pending or queued events if (nextWakeupTime == LLONG_MAX) { @@ -1096,6 +1114,21 @@ nsecs_t InputDispatcher::processAnrsLocked() { return LLONG_MIN; } +/** + * Check if enough time has passed since the last latency statistics push. + * Return the time at which we should wake up next. + */ +nsecs_t InputDispatcher::processLatencyStatisticsLocked() { + const nsecs_t currentTime = now(); + // Log the atom recording latency statistics if more than 6 hours passed from the last + // push + if (currentTime - mLastStatisticPushTime >= LATENCY_STATISTICS_PUSH_INTERVAL) { + mInputEventTimelineProcessor->pushLatencyStatistics(); + mLastStatisticPushTime = currentTime; + } + return mLastStatisticPushTime + LATENCY_STATISTICS_PUSH_INTERVAL; +} + std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( const std::shared_ptr<Connection>& connection) { if (connection->monitor) { @@ -2510,7 +2543,8 @@ InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, const Motio if (isHoverAction) { // The "windowHandle" is the target of this hovering pointer. - tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer); + tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer, x, + y); } // Set target flags. @@ -4490,6 +4524,10 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { { // acquire lock mLock.lock(); + if (input_flags::keyboard_repeat_keys() && !mConfig.keyRepeatEnabled) { + policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; + } + if (shouldSendKeyToInputFilterLocked(args)) { mLock.unlock(); @@ -4510,6 +4548,14 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry); } + if (input_flags::enable_per_device_input_latency_metrics()) { + if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && + IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER && + !mInputFilterEnabled) { + mLatencyTracker.trackNotifyKey(args); + } + } + needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); } // release lock @@ -4642,10 +4688,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER && !mInputFilterEnabled) { - const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN; - std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args); - mLatencyTracker.trackListener(args.id, isDown, args.eventTime, args.readTime, - args.deviceId, sources); + mLatencyTracker.trackNotifyMotion(args); } needWake = enqueueInboundEventLocked(std::move(newEntry)); @@ -5415,6 +5458,32 @@ void InputDispatcher::setInputWindowsLocked( } } + // Check if the hovering should stop because the window is no longer eligible to receive it + // (for example, if the touchable region changed) + if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) { + TouchState& state = it->second; + for (TouchedWindow& touchedWindow : state.windows) { + std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf( + [this, displayId, &touchedWindow](const PointerProperties& properties, float x, + float y) REQUIRES(mLock) { + const bool isStylus = properties.toolType == ToolType::STYLUS; + const ui::Transform displayTransform = getTransformLocked(displayId); + const bool stillAcceptsTouch = + windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(), + displayId, x, y, isStylus, displayTransform); + return !stillAcceptsTouch; + }); + + for (DeviceId deviceId : erasedDevices) { + CancelationOptions options(CancelationOptions::Mode::CANCEL_HOVER_EVENTS, + "WindowInfo changed", + traceContext.getTracker()); + options.deviceId = deviceId; + synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); + } + } + } + // Release information for windows that are no longer present. // This ensures that unused input channels are released promptly. // Otherwise, they might stick around until the window handle is destroyed @@ -6055,7 +6124,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n", ns2ms(mConfig.keyRepeatTimeout)); dump += mLatencyTracker.dump(INDENT2); - dump += mLatencyAggregator.dump(INDENT2); + dump += mInputEventTimelineProcessor->dump(INDENT2); dump += INDENT "InputTracer: "; dump += mTracer == nullptr ? "Disabled" : "Enabled"; } @@ -7209,11 +7278,13 @@ sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow( } void InputDispatcher::setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, - std::chrono::nanoseconds delay) { + std::chrono::nanoseconds delay, + bool keyRepeatEnabled) { std::scoped_lock _l(mLock); mConfig.keyRepeatTimeout = timeout.count(); mConfig.keyRepeatDelay = delay.count(); + mConfig.keyRepeatEnabled = keyRepeatEnabled; } bool InputDispatcher::isPointerInWindow(const sp<android::IBinder>& token, diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 87dfd1d8a4..d90b9de80e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -28,6 +28,7 @@ #include "InputTarget.h" #include "InputThread.h" #include "LatencyAggregator.h" +#include "LatencyAggregatorWithHistograms.h" #include "LatencyTracker.h" #include "Monitor.h" #include "TouchState.h" @@ -153,8 +154,8 @@ public: // Public to allow tests to verify that a Monitor can get ANR. void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout); - void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, - std::chrono::nanoseconds delay) override; + void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, std::chrono::nanoseconds delay, + bool keyRepeatEnabled) override; bool isPointerInWindow(const sp<IBinder>& token, ui::LogicalDisplayId displayId, DeviceId deviceId, int32_t pointerId) override; @@ -326,6 +327,7 @@ private: std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock); nsecs_t processAnrsLocked() REQUIRES(mLock); + nsecs_t processLatencyStatisticsLocked() REQUIRES(mLock); std::chrono::nanoseconds getDispatchingTimeoutLocked( const std::shared_ptr<Connection>& connection) REQUIRES(mLock); @@ -697,7 +699,8 @@ private: DeviceId deviceId) const REQUIRES(mLock); // Statistics gathering. - LatencyAggregator mLatencyAggregator GUARDED_BY(mLock); + nsecs_t mLastStatisticPushTime = 0; + std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock); LatencyTracker mLatencyTracker GUARDED_BY(mLock); void traceInboundQueueLengthLocked() REQUIRES(mLock); void traceOutboundQueueLength(const Connection& connection); diff --git a/services/inputflinger/dispatcher/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp index a3650030ac..688196461a 100644 --- a/services/inputflinger/dispatcher/InputEventTimeline.cpp +++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp @@ -66,15 +66,16 @@ bool ConnectionTimeline::operator!=(const ConnectionTimeline& rhs) const { return !operator==(rhs); } -InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, - uint16_t vendorId, uint16_t productId, - const std::set<InputDeviceUsageSource>& sources) - : isDown(isDown), - eventTime(eventTime), +InputEventTimeline::InputEventTimeline(nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, + uint16_t productId, + const std::set<InputDeviceUsageSource>& sources, + InputEventActionType inputEventActionType) + : eventTime(eventTime), readTime(readTime), vendorId(vendorId), productId(productId), - sources(sources) {} + sources(sources), + inputEventActionType(inputEventActionType) {} bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const { if (connectionTimelines.size() != rhs.connectionTimelines.size()) { @@ -89,8 +90,9 @@ bool InputEventTimeline::operator==(const InputEventTimeline& rhs) const { return false; } } - return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime && - vendorId == rhs.vendorId && productId == rhs.productId && sources == rhs.sources; + return eventTime == rhs.eventTime && readTime == rhs.readTime && vendorId == rhs.vendorId && + productId == rhs.productId && sources == rhs.sources && + inputEventActionType == rhs.inputEventActionType; } } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h index 1756944d0e..4552708adc 100644 --- a/services/inputflinger/dispatcher/InputEventTimeline.h +++ b/services/inputflinger/dispatcher/InputEventTimeline.h @@ -74,15 +74,38 @@ private: bool mHasGraphicsTimeline = false; }; +enum class InputEventActionType : int32_t { + UNKNOWN_INPUT_EVENT = 0, + MOTION_ACTION_DOWN = 1, + // Motion events for ACTION_MOVE (characterizes scrolling motion) + MOTION_ACTION_MOVE = 2, + // Motion events for ACTION_UP (when the pointer first goes up) + MOTION_ACTION_UP = 3, + // Motion events for ACTION_HOVER_MOVE (pointer position on screen changes but pointer is not + // down) + MOTION_ACTION_HOVER_MOVE = 4, + // Motion events for ACTION_SCROLL (moving the mouse wheel) + MOTION_ACTION_SCROLL = 5, + // Key events for both ACTION_DOWN and ACTION_UP (key press and key release) + KEY = 6, + + ftl_first = UNKNOWN_INPUT_EVENT, + ftl_last = KEY, + // Used by latency fuzzer + kMaxValue = ftl_last + +}; + struct InputEventTimeline { - InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, - uint16_t productId, const std::set<InputDeviceUsageSource>& sources); - const bool isDown; // True if this is an ACTION_DOWN event + InputEventTimeline(nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, uint16_t productId, + const std::set<InputDeviceUsageSource>& sources, + InputEventActionType inputEventActionType); const nsecs_t eventTime; const nsecs_t readTime; const uint16_t vendorId; const uint16_t productId; const std::set<InputDeviceUsageSource> sources; + const InputEventActionType inputEventActionType; struct IBinderHash { std::size_t operator()(const sp<IBinder>& b) const { @@ -98,13 +121,21 @@ struct InputEventTimeline { class InputEventTimelineProcessor { protected: InputEventTimelineProcessor() {} - virtual ~InputEventTimelineProcessor() {} public: + virtual ~InputEventTimelineProcessor() {} + /** * Process the provided timeline */ virtual void processTimeline(const InputEventTimeline& timeline) = 0; + + /** + * Trigger a push for the input event latency statistics + */ + virtual void pushLatencyStatistics() = 0; + + virtual std::string dump(const char* prefix) const = 0; }; } // namespace inputdispatcher diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 4df23c54d6..9b5a79b24d 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -638,6 +638,8 @@ bool InputState::shouldCancelMotion(const MotionMemento& memento, return memento.source & AINPUT_SOURCE_CLASS_POINTER; case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); + case CancelationOptions::Mode::CANCEL_HOVER_EVENTS: + return memento.hovering; default: return false; } diff --git a/services/inputflinger/dispatcher/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp index e09d97a13b..d0e9d7c983 100644 --- a/services/inputflinger/dispatcher/LatencyAggregator.cpp +++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp @@ -28,6 +28,8 @@ using android::base::StringPrintf; using dist_proc::aggregation::KllQuantile; using std::chrono_literals::operator""ms; +namespace { + // Convert the provided nanoseconds into hundreds of microseconds. // Use hundreds of microseconds (as opposed to microseconds) to preserve space. static inline int64_t ns2hus(nsecs_t nanos) { @@ -74,6 +76,8 @@ static std::chrono::milliseconds getSlowEventMinReportingInterval() { return std::chrono::milliseconds(std::stoi(millis)); } +} // namespace + namespace android::inputdispatcher { /** @@ -125,6 +129,9 @@ void LatencyAggregator::processTimeline(const InputEventTimeline& timeline) { processSlowEvent(timeline); } +// This version of LatencyAggregator doesn't push any atoms +void LatencyAggregator::pushLatencyStatistics() {} + void LatencyAggregator::processStatistics(const InputEventTimeline& timeline) { std::scoped_lock lock(mLock); // Before we do any processing, check that we have not yet exceeded MAX_SIZE @@ -134,7 +141,9 @@ void LatencyAggregator::processStatistics(const InputEventTimeline& timeline) { mNumSketchEventsProcessed++; std::array<std::unique_ptr<KllQuantile>, SketchIndex::SIZE>& sketches = - timeline.isDown ? mDownSketches : mMoveSketches; + timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN + ? mDownSketches + : mMoveSketches; // Process common ones first const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; @@ -242,7 +251,9 @@ void LatencyAggregator::processSlowEvent(const InputEventTimeline& timeline) { const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; - android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, timeline.isDown, + android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, + timeline.inputEventActionType == + InputEventActionType::MOTION_ACTION_DOWN, static_cast<int32_t>(ns2us(eventToRead)), static_cast<int32_t>(ns2us(readToDeliver)), static_cast<int32_t>(ns2us(deliverToConsume)), diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h index d6d168602a..468add1435 100644 --- a/services/inputflinger/dispatcher/LatencyAggregator.h +++ b/services/inputflinger/dispatcher/LatencyAggregator.h @@ -57,6 +57,8 @@ public: */ void processTimeline(const InputEventTimeline& timeline) override; + void pushLatencyStatistics() override; + std::string dump(const char* prefix) const; ~LatencyAggregator(); diff --git a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp new file mode 100644 index 0000000000..881a96b28e --- /dev/null +++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp @@ -0,0 +1,345 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "LatencyAggregatorWithHistograms" +#include "../InputDeviceMetricsSource.h" +#include "InputDispatcher.h" + +#include <inttypes.h> +#include <log/log_event_list.h> +#include <statslog.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <input/Input.h> +#include <log/log.h> +#include <server_configurable_flags/get_flags.h> + +using android::base::StringPrintf; +using std::chrono_literals::operator""ms; + +namespace { + +// Convert the provided nanoseconds into hundreds of microseconds. +// Use hundreds of microseconds (as opposed to microseconds) to preserve space. +static inline int64_t ns2hus(nsecs_t nanos) { + return ns2us(nanos) / 100; +} + +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +// Feature flag name for the threshold of end-to-end touch latency that would trigger +// SlowEventReported atom to be pushed +static const char* SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS = + "slow_event_min_reporting_latency_millis"; +// Feature flag name for the minimum delay before reporting a slow event after having just reported +// a slow event. This helps limit the amount of data sent to the server +static const char* SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS = + "slow_event_min_reporting_interval_millis"; + +// If an event has end-to-end latency > 200 ms, it will get reported as a slow event. +std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY = 200ms; +// If we receive two slow events less than 1 min apart, we will only report 1 of them. +std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL = 60000ms; + +static std::chrono::milliseconds getSlowEventMinReportingLatency() { + std::string millis = server_configurable_flags:: + GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS, + std::to_string( + DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY.count())); + return std::chrono::milliseconds(std::stoi(millis)); +} + +static std::chrono::milliseconds getSlowEventMinReportingInterval() { + std::string millis = server_configurable_flags:: + GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS, + std::to_string( + DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL.count())); + return std::chrono::milliseconds(std::stoi(millis)); +} + +} // namespace + +namespace android::inputdispatcher { + +int32_t LatencyStageIndexToAtomEnum(LatencyStageIndex latencyStageIndex) { + switch (latencyStageIndex) { + case LatencyStageIndex::EVENT_TO_READ: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__EVENT_TO_READ; + case LatencyStageIndex::READ_TO_DELIVER: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__READ_TO_DELIVER; + case LatencyStageIndex::DELIVER_TO_CONSUME: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__DELIVER_TO_CONSUME; + case LatencyStageIndex::CONSUME_TO_FINISH: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__CONSUME_TO_FINISH; + case LatencyStageIndex::CONSUME_TO_GPU_COMPLETE: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__CONSUME_TO_GPU_COMPLETE; + case LatencyStageIndex::GPU_COMPLETE_TO_PRESENT: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__GPU_COMPLETE_TO_PRESENT; + case LatencyStageIndex::END_TO_END: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__END_TO_END; + default: + return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__UNKNOWN_LATENCY_STAGE; + } +} + +int32_t InputEventTypeEnumToAtomEnum(InputEventActionType inputEventActionType) { + switch (inputEventActionType) { + case InputEventActionType::UNKNOWN_INPUT_EVENT: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__UNKNOWN_INPUT_EVENT; + case InputEventActionType::MOTION_ACTION_DOWN: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_DOWN; + case InputEventActionType::MOTION_ACTION_MOVE: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_MOVE; + case InputEventActionType::MOTION_ACTION_UP: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_UP; + case InputEventActionType::MOTION_ACTION_HOVER_MOVE: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_HOVER_MOVE; + case InputEventActionType::MOTION_ACTION_SCROLL: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_SCROLL; + case InputEventActionType::KEY: + return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__KEY; + } +} + +void LatencyAggregatorWithHistograms::processTimeline(const InputEventTimeline& timeline) { + processStatistics(timeline); + processSlowEvent(timeline); +} + +void LatencyAggregatorWithHistograms::addSampleToHistogram( + const InputEventLatencyIdentifier& identifier, LatencyStageIndex latencyStageIndex, + nsecs_t latency) { + // Only record positive values for the statistics + if (latency > 0) { + auto it = mHistograms.find(identifier); + if (it != mHistograms.end()) { + it->second[static_cast<size_t>(latencyStageIndex)].addSample(ns2hus(latency)); + } + } +} + +void LatencyAggregatorWithHistograms::processStatistics(const InputEventTimeline& timeline) { + // Only gather data for Down, Move and Up motion events and Key events + if (!(timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN || + timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_MOVE || + timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_UP || + timeline.inputEventActionType == InputEventActionType::KEY)) + return; + + // Don't collect data for unidentified devices. This situation can occur for the first few input + // events produced when an input device is first connected + if (timeline.vendorId == 0xFFFF && timeline.productId == 0xFFFF) return; + + InputEventLatencyIdentifier identifier = {timeline.vendorId, timeline.productId, + timeline.sources, timeline.inputEventActionType}; + // Check if there's a value in mHistograms map associated to identifier. + // If not, add an array with 7 empty histograms as an entry + if (mHistograms.count(identifier) == 0) { + if (static_cast<int32_t>(timeline.inputEventActionType) - 1 < 0) { + LOG(FATAL) << "Action index is smaller than 0. Action type: " + << ftl::enum_string(timeline.inputEventActionType); + return; + } + size_t actionIndex = + static_cast<size_t>(static_cast<int32_t>(timeline.inputEventActionType) - 1); + if (actionIndex >= NUM_INPUT_EVENT_TYPES) { + LOG(FATAL) << "Action index greater than the number of input event types. Action Type: " + << ftl::enum_string(timeline.inputEventActionType) + << "; Action Type Index: " << actionIndex; + return; + } + + std::array<Histogram, 7> histograms = + {Histogram(allBinSizes[binSizesMappings[0][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[1][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[2][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[3][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[4][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[5][actionIndex]]), + Histogram(allBinSizes[binSizesMappings[6][actionIndex]])}; + mHistograms.insert({identifier, histograms}); + } + + // Process common ones first + const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; + addSampleToHistogram(identifier, LatencyStageIndex::EVENT_TO_READ, eventToRead); + + // Now process per-connection ones + for (const auto& [connectionToken, connectionTimeline] : timeline.connectionTimelines) { + if (!connectionTimeline.isComplete()) { + continue; + } + const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime; + const nsecs_t deliverToConsume = + connectionTimeline.consumeTime - connectionTimeline.deliveryTime; + const nsecs_t consumeToFinish = + connectionTimeline.finishTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompletedTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t presentTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; + const nsecs_t endToEnd = presentTime - timeline.eventTime; + + addSampleToHistogram(identifier, LatencyStageIndex::READ_TO_DELIVER, readToDeliver); + addSampleToHistogram(identifier, LatencyStageIndex::DELIVER_TO_CONSUME, deliverToConsume); + addSampleToHistogram(identifier, LatencyStageIndex::CONSUME_TO_FINISH, consumeToFinish); + addSampleToHistogram(identifier, LatencyStageIndex::CONSUME_TO_GPU_COMPLETE, + consumeToGpuComplete); + addSampleToHistogram(identifier, LatencyStageIndex::GPU_COMPLETE_TO_PRESENT, + gpuCompleteToPresent); + addSampleToHistogram(identifier, LatencyStageIndex::END_TO_END, endToEnd); + } +} + +void LatencyAggregatorWithHistograms::pushLatencyStatistics() { + for (auto& [id, histograms] : mHistograms) { + auto [vendorId, productId, sources, action] = id; + for (size_t latencyStageIndex = static_cast<size_t>(LatencyStageIndex::EVENT_TO_READ); + latencyStageIndex < static_cast<size_t>(LatencyStageIndex::SIZE); + ++latencyStageIndex) { + // Convert sources set to vector for atom logging: + std::vector<int32_t> sourcesVector = {}; + for (auto& elem : sources) { + sourcesVector.push_back(static_cast<int32_t>(elem)); + } + + // convert histogram bin counts array to vector for atom logging: + std::array arr = histograms[latencyStageIndex].getBinCounts(); + std::vector<int32_t> binCountsVector(arr.begin(), arr.end()); + + if (static_cast<int32_t>(action) - 1 < 0) { + ALOGW("Action index is smaller than 0. Action type: %s", + ftl::enum_string(action).c_str()); + continue; + } + size_t actionIndex = static_cast<size_t>(static_cast<int32_t>(action) - 1); + if (actionIndex >= NUM_INPUT_EVENT_TYPES) { + ALOGW("Action index greater than the number of input event types. Action Type: %s; " + "Action Type Index: %zu", + ftl::enum_string(action).c_str(), actionIndex); + continue; + } + + stats_write(android::util::INPUT_EVENT_LATENCY_REPORTED, vendorId, productId, + sourcesVector, InputEventTypeEnumToAtomEnum(action), + LatencyStageIndexToAtomEnum( + static_cast<LatencyStageIndex>(latencyStageIndex)), + histogramVersions[latencyStageIndex][actionIndex], binCountsVector); + } + } + mHistograms.clear(); +} + +// TODO (b/270049345): For now, we just copied the code from LatencyAggregator to populate the old +// atom, but eventually we should migrate this to use the new SlowEventReported atom +void LatencyAggregatorWithHistograms::processSlowEvent(const InputEventTimeline& timeline) { + static const std::chrono::duration sSlowEventThreshold = getSlowEventMinReportingLatency(); + static const std::chrono::duration sSlowEventReportingInterval = + getSlowEventMinReportingInterval(); + for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) { + if (!connectionTimeline.isComplete()) { + continue; + } + mNumEventsSinceLastSlowEventReport++; + const nsecs_t presentTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; + const std::chrono::nanoseconds endToEndLatency = + std::chrono::nanoseconds(presentTime - timeline.eventTime); + if (endToEndLatency < sSlowEventThreshold) { + continue; + } + // This is a slow event. Before we report it, check if we are reporting too often + const std::chrono::duration elapsedSinceLastReport = + std::chrono::nanoseconds(timeline.eventTime - mLastSlowEventTime); + if (elapsedSinceLastReport < sSlowEventReportingInterval) { + mNumSkippedSlowEvents++; + continue; + } + + const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; + const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime; + const nsecs_t deliverToConsume = + connectionTimeline.consumeTime - connectionTimeline.deliveryTime; + const nsecs_t consumeToFinish = + connectionTimeline.finishTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompletedTime = + connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; + const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; + const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; + + android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, + timeline.inputEventActionType == + InputEventActionType::MOTION_ACTION_DOWN, + static_cast<int32_t>(ns2us(eventToRead)), + static_cast<int32_t>(ns2us(readToDeliver)), + static_cast<int32_t>(ns2us(deliverToConsume)), + static_cast<int32_t>(ns2us(consumeToFinish)), + static_cast<int32_t>(ns2us(consumeToGpuComplete)), + static_cast<int32_t>(ns2us(gpuCompleteToPresent)), + static_cast<int32_t>(ns2us(endToEndLatency.count())), + static_cast<int32_t>(mNumEventsSinceLastSlowEventReport), + static_cast<int32_t>(mNumSkippedSlowEvents)); + mNumEventsSinceLastSlowEventReport = 0; + mNumSkippedSlowEvents = 0; + mLastSlowEventTime = timeline.readTime; + } +} + +std::string LatencyAggregatorWithHistograms::dump(const char* prefix) const { + std::string statisticsStr = StringPrintf("%s Histograms:\n", prefix); + for (const auto& [id, histograms] : mHistograms) { + auto [vendorId, productId, sources, action] = id; + + std::string identifierStr = + StringPrintf("%s Identifier: vendor %d, product %d, sources: {", prefix, vendorId, + productId); + bool firstSource = true; + for (const auto& source : sources) { + if (!firstSource) { + identifierStr += ", "; + } + identifierStr += StringPrintf("%d", static_cast<int32_t>(source)); + firstSource = false; + } + identifierStr += StringPrintf("}, action: %d\n", static_cast<int32_t>(action)); + + std::string histogramsStr; + for (size_t stageIndex = 0; stageIndex < static_cast<size_t>(LatencyStageIndex::SIZE); + stageIndex++) { + const auto& histogram = histograms[stageIndex]; + const std::array<int, NUM_BINS>& binCounts = histogram.getBinCounts(); + + histogramsStr += StringPrintf("%s %zu: ", prefix, stageIndex); + histogramsStr += StringPrintf("%d", binCounts[0]); + for (size_t bin = 1; bin < NUM_BINS; bin++) { + histogramsStr += StringPrintf(", %d", binCounts[bin]); + } + histogramsStr += StringPrintf("\n"); + } + statisticsStr += identifierStr + histogramsStr; + } + + return StringPrintf("%sLatencyAggregatorWithHistograms:\n", prefix) + statisticsStr + + StringPrintf("%s mLastSlowEventTime=%" PRId64 "\n", prefix, mLastSlowEventTime) + + StringPrintf("%s mNumEventsSinceLastSlowEventReport = %zu\n", prefix, + mNumEventsSinceLastSlowEventReport) + + StringPrintf("%s mNumSkippedSlowEvents = %zu\n", prefix, mNumSkippedSlowEvents); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h new file mode 100644 index 0000000000..2ceb0e7cb7 --- /dev/null +++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h @@ -0,0 +1,164 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/thread_annotations.h> +#include <utils/Timers.h> + +#include "InputEventTimeline.h" + +namespace android::inputdispatcher { + +static constexpr size_t NUM_BINS = 20; +static constexpr size_t NUM_INPUT_EVENT_TYPES = 6; + +enum class LatencyStageIndex : size_t { + EVENT_TO_READ = 0, + READ_TO_DELIVER = 1, + DELIVER_TO_CONSUME = 2, + CONSUME_TO_FINISH = 3, + CONSUME_TO_GPU_COMPLETE = 4, + GPU_COMPLETE_TO_PRESENT = 5, + END_TO_END = 6, + SIZE = 7, // must be last +}; + +// Let's create a full timeline here: +// eventTime +// readTime +// <---- after this point, the data becomes per-connection +// deliveryTime // time at which the event was sent to the receiver +// consumeTime // time at which the receiver read the event +// finishTime // time at which the dispatcher reads the response from the receiver that the event +// was processed +// GraphicsTimeline::GPU_COMPLETED_TIME +// GraphicsTimeline::PRESENT_TIME + +/** + * Keep histograms with latencies of the provided events + */ +class LatencyAggregatorWithHistograms final : public InputEventTimelineProcessor { +public: + /** + * Record a complete event timeline + */ + void processTimeline(const InputEventTimeline& timeline) override; + + void pushLatencyStatistics() override; + + std::string dump(const char* prefix) const override; + +private: + // ---------- Slow event handling ---------- + void processSlowEvent(const InputEventTimeline& timeline); + nsecs_t mLastSlowEventTime = 0; + // How many slow events have been skipped due to rate limiting + size_t mNumSkippedSlowEvents = 0; + // How many events have been received since the last time we reported a slow event + size_t mNumEventsSinceLastSlowEventReport = 0; + + // ---------- Statistics handling ---------- + /** + * Data structure to gather time samples into NUM_BINS buckets + */ + class Histogram { + public: + Histogram(const std::array<int, NUM_BINS - 1>& binSizes) : mBinSizes(binSizes) { + mBinCounts.fill(0); + } + + // Increments binCounts of the appropriate bin when adding a new sample + void addSample(int64_t sample) { + size_t binIndex = getSampleBinIndex(sample); + mBinCounts[binIndex]++; + } + + const std::array<int32_t, NUM_BINS>& getBinCounts() const { return mBinCounts; } + + private: + // reference to an array that represents the range of values each bin holds. + // in bin i+1 live samples such that *mBinSizes[i] <= sample < *mBinSizes[i+1] + const std::array<int, NUM_BINS - 1>& mBinSizes; + std::array<int32_t, NUM_BINS> + mBinCounts; // the number of samples that currently live in each bin + + size_t getSampleBinIndex(int64_t sample) { + auto it = std::upper_bound(mBinSizes.begin(), mBinSizes.end(), sample); + return std::distance(mBinSizes.begin(), it); + } + }; + + void processStatistics(const InputEventTimeline& timeline); + + // Identifier for the an input event. If two input events have the same identifiers we + // want to use the same histograms to count the latency samples + using InputEventLatencyIdentifier = + std::tuple<uint16_t /*vendorId*/, uint16_t /*productId*/, + const std::set<InputDeviceUsageSource> /*sources*/, + InputEventActionType /*inputEventActionType*/>; + + // Maps an input event identifier to an array of 7 histograms, one for each latency + // stage. It is cleared after an atom push + std::map<InputEventLatencyIdentifier, std::array<Histogram, 7>> mHistograms; + + void addSampleToHistogram(const InputEventLatencyIdentifier& identifier, + LatencyStageIndex latencyStageIndex, nsecs_t time); + + // Stores all possible arrays of bin sizes. The order in the vector does not matter, as long + // as binSizesMappings points to the right index + static constexpr std::array<std::array<int, NUM_BINS - 1>, 6> allBinSizes = { + {{10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100}, + {1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32}, + {15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, + 285}, + {40, 80, 120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600, 640, 680, + 720, 760}, + {20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, 320, 340, 360, + 380}, + {200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, + 1700, 1800, 1900, 2000}}}; + + // Stores indexes in allBinSizes to use with each {LatencyStage, InputEventType} pair. + // Bin sizes for a certain latencyStage and inputEventType are at: + // *(allBinSizes[binSizesMappings[latencyStageIndex][inputEventTypeIndex]]) + // inputEventTypeIndex is the int value of InputEventActionType enum decreased by 1 since we + // don't want to record latencies for unknown events. + // e.g. MOTION_ACTION_DOWN is 0, MOTION_ACTION_MOVE is 1... + static constexpr std::array<std::array<int8_t, NUM_INPUT_EVENT_TYPES>, + static_cast<size_t>(LatencyStageIndex::SIZE)> + binSizesMappings = {{{0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 3, 3}, + {4, 4, 4, 4, 4, 4}, + {5, 5, 5, 5, 5, 5}}}; + + // Similar to binSizesMappings, but holds the index of the array of bin ranges to use on the + // server. The index gets pushed with the atom within the histogram_version field. + static constexpr std::array<std::array<int8_t, NUM_INPUT_EVENT_TYPES>, + static_cast<size_t>(LatencyStageIndex::SIZE)> + histogramVersions = {{{0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1}, + {2, 2, 2, 2, 2, 2}, + {3, 3, 3, 3, 3, 3}, + {4, 4, 4, 4, 4, 4}, + {5, 5, 5, 5, 5, 5}}}; +}; + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index 698bd9ff08..0852026823 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -62,14 +62,33 @@ static void eraseByValue(std::multimap<K, V>& map, const V& value) { } } -LatencyTracker::LatencyTracker(InputEventTimelineProcessor* processor) - : mTimelineProcessor(processor) { - LOG_ALWAYS_FATAL_IF(processor == nullptr); +LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor) + : mTimelineProcessor(&processor) {} + +void LatencyTracker::trackNotifyMotion(const NotifyMotionArgs& args) { + std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args); + trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action, + InputEventType::MOTION); } -void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, - nsecs_t readTime, DeviceId deviceId, - const std::set<InputDeviceUsageSource>& sources) { +void LatencyTracker::trackNotifyKey(const NotifyKeyArgs& args) { + int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NONE; + for (auto& inputDevice : mInputDevices) { + if (args.deviceId == inputDevice.getId()) { + keyboardType = inputDevice.getKeyboardType(); + break; + } + } + std::set<InputDeviceUsageSource> sources = + std::set{getUsageSourceForKeyArgs(keyboardType, args)}; + trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action, + InputEventType::KEY); +} + +void LatencyTracker::trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, + DeviceId deviceId, + const std::set<InputDeviceUsageSource>& sources, + int32_t inputEventAction, InputEventType inputEventType) { reportAndPruneMatureRecords(eventTime); const auto it = mTimelines.find(inputEventId); if (it != mTimelines.end()) { @@ -101,9 +120,41 @@ void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t ev return; } + const InputEventActionType inputEventActionType = [&]() { + switch (inputEventType) { + case InputEventType::MOTION: { + switch (MotionEvent::getActionMasked(inputEventAction)) { + case AMOTION_EVENT_ACTION_DOWN: + return InputEventActionType::MOTION_ACTION_DOWN; + case AMOTION_EVENT_ACTION_MOVE: + return InputEventActionType::MOTION_ACTION_MOVE; + case AMOTION_EVENT_ACTION_UP: + return InputEventActionType::MOTION_ACTION_UP; + case AMOTION_EVENT_ACTION_HOVER_MOVE: + return InputEventActionType::MOTION_ACTION_HOVER_MOVE; + case AMOTION_EVENT_ACTION_SCROLL: + return InputEventActionType::MOTION_ACTION_SCROLL; + default: + return InputEventActionType::UNKNOWN_INPUT_EVENT; + } + } + case InputEventType::KEY: { + switch (inputEventAction) { + case AKEY_EVENT_ACTION_DOWN: + case AKEY_EVENT_ACTION_UP: + return InputEventActionType::KEY; + default: + return InputEventActionType::UNKNOWN_INPUT_EVENT; + } + } + default: + return InputEventActionType::UNKNOWN_INPUT_EVENT; + } + }(); + mTimelines.emplace(inputEventId, - InputEventTimeline(isDown, eventTime, readTime, identifier->vendor, - identifier->product, sources)); + InputEventTimeline(eventTime, readTime, identifier->vendor, + identifier->product, sources, inputEventActionType)); mEventTimes.emplace(eventTime, inputEventId); } diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 890d61d431..eb58222ce6 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -42,7 +42,7 @@ public: * Create a LatencyTracker. * param reportingFunction: the function that will be called in order to report full latency. */ - LatencyTracker(InputEventTimelineProcessor* processor); + LatencyTracker(InputEventTimelineProcessor& processor); /** * Start keeping track of an event identified by inputEventId. This must be called first. * If duplicate events are encountered (events that have the same eventId), none of them will be @@ -52,12 +52,20 @@ public: * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we * must drop all duplicate data. */ - void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime, - DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources); + void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId, + const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction, + InputEventType inputEventType); void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken, nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime); void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); + /** + * trackNotifyMotion and trackNotifyKeys are intermediates between InputDispatcher and + * trackListener. They compute the InputDeviceUsageSource set and call trackListener with + * the relevant parameters for latency computation. + */ + void trackNotifyMotion(const NotifyMotionArgs& args); + void trackNotifyKey(const NotifyKeyArgs& args); std::string dump(const char* prefix) const; void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices); diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 0c9ad3c7b7..2bf63beb05 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -112,17 +112,18 @@ android::base::Result<void> TouchState::addOrUpdateWindow( } void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle, - DeviceId deviceId, const PointerProperties& pointer) { + DeviceId deviceId, const PointerProperties& pointer, + float x, float y) { for (TouchedWindow& touchedWindow : windows) { if (touchedWindow.windowHandle == windowHandle) { - touchedWindow.addHoveringPointer(deviceId, pointer); + touchedWindow.addHoveringPointer(deviceId, pointer, x, y); return; } } TouchedWindow touchedWindow; touchedWindow.windowHandle = windowHandle; - touchedWindow.addHoveringPointer(deviceId, pointer); + touchedWindow.addHoveringPointer(deviceId, pointer, x, y); windows.push_back(touchedWindow); } diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 5a70dd5e2c..451d91704c 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -49,7 +49,8 @@ struct TouchState { DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers, std::optional<nsecs_t> firstDownTimeInTarget); void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, - DeviceId deviceId, const PointerProperties& pointer); + DeviceId deviceId, const PointerProperties& pointer, float x, + float y); void removeHoveringPointer(DeviceId deviceId, int32_t pointerId); void clearHoveringPointers(DeviceId deviceId); diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp index 1f86f6635a..fa5be1a719 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.cpp +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -36,6 +36,13 @@ bool hasPointerId(const std::vector<PointerProperties>& pointers, int32_t pointe }) != pointers.end(); } +bool hasPointerId(const std::vector<TouchedWindow::HoveringPointer>& pointers, int32_t pointerId) { + return std::find_if(pointers.begin(), pointers.end(), + [&pointerId](const TouchedWindow::HoveringPointer& pointer) { + return pointer.properties.id == pointerId; + }) != pointers.end(); +} + } // namespace bool TouchedWindow::hasHoveringPointers() const { @@ -78,16 +85,18 @@ bool TouchedWindow::hasHoveringPointer(DeviceId deviceId, int32_t pointerId) con return hasPointerId(state.hoveringPointers, pointerId); } -void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer) { - std::vector<PointerProperties>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers; +void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& properties, + float x, float y) { + std::vector<HoveringPointer>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers; const size_t initialSize = hoveringPointers.size(); - std::erase_if(hoveringPointers, [&pointer](const PointerProperties& properties) { - return properties.id == pointer.id; + std::erase_if(hoveringPointers, [&properties](const HoveringPointer& pointer) { + return pointer.properties.id == properties.id; }); if (hoveringPointers.size() != initialSize) { - LOG(ERROR) << __func__ << ": " << pointer << ", device " << deviceId << " was in " << *this; + LOG(ERROR) << __func__ << ": " << properties << ", device " << deviceId << " was in " + << *this; } - hoveringPointers.push_back(pointer); + hoveringPointers.push_back({properties, x, y}); } Result<void> TouchedWindow::addTouchingPointers(DeviceId deviceId, @@ -173,8 +182,8 @@ bool TouchedWindow::hasActiveStylus() const { return true; } } - for (const PointerProperties& properties : state.hoveringPointers) { - if (properties.toolType == ToolType::STYLUS) { + for (const HoveringPointer& pointer : state.hoveringPointers) { + if (pointer.properties.toolType == ToolType::STYLUS) { return true; } } @@ -270,8 +279,8 @@ void TouchedWindow::removeHoveringPointer(DeviceId deviceId, int32_t pointerId) } DeviceState& state = stateIt->second; - std::erase_if(state.hoveringPointers, [&pointerId](const PointerProperties& properties) { - return properties.id == pointerId; + std::erase_if(state.hoveringPointers, [&pointerId](const HoveringPointer& pointer) { + return pointer.properties.id == pointerId; }); if (!state.hasPointers()) { @@ -279,6 +288,22 @@ void TouchedWindow::removeHoveringPointer(DeviceId deviceId, int32_t pointerId) } } +std::vector<DeviceId> TouchedWindow::eraseHoveringPointersIf( + std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition) { + std::vector<DeviceId> erasedDevices; + for (auto& [deviceId, state] : mDeviceStates) { + std::erase_if(state.hoveringPointers, [&](const HoveringPointer& pointer) { + if (condition(pointer.properties, pointer.x, pointer.y)) { + erasedDevices.push_back(deviceId); + return true; + } + return false; + }); + } + + return erasedDevices; +} + void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) { const auto stateIt = mDeviceStates.find(deviceId); if (stateIt == mDeviceStates.end()) { @@ -312,6 +337,11 @@ std::string TouchedWindow::dump() const { return out; } +std::ostream& operator<<(std::ostream& out, const TouchedWindow::HoveringPointer& pointer) { + out << pointer.properties << " at (" << pointer.x << ", " << pointer.y << ")"; + return out; +} + std::ostream& operator<<(std::ostream& out, const TouchedWindow& window) { out << window.dump(); return out; diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index 4f0ad1628a..c38681eef0 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -38,7 +38,7 @@ struct TouchedWindow { bool hasHoveringPointers() const; bool hasHoveringPointers(DeviceId deviceId) const; bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const; - void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer); + void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer, float x, float y); void removeHoveringPointer(DeviceId deviceId, int32_t pointerId); // Touching @@ -69,6 +69,15 @@ struct TouchedWindow { void clearHoveringPointers(DeviceId deviceId); std::string dump() const; + struct HoveringPointer { + PointerProperties properties; + float x; + float y; + }; + + std::vector<DeviceId> eraseHoveringPointersIf( + std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition); + private: struct DeviceState { std::vector<PointerProperties> touchingPointers; @@ -78,7 +87,7 @@ private: // NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE // scenario. std::optional<nsecs_t> downTimeInTarget; - std::vector<PointerProperties> hoveringPointers; + std::vector<HoveringPointer> hoveringPointers; bool hasPointers() const { return !touchingPointers.empty() || !hoveringPointers.empty(); }; }; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h index 5eb3a32ef9..ba197d45fd 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h @@ -34,8 +34,13 @@ struct InputDispatcherConfiguration { // The key repeat inter-key delay. nsecs_t keyRepeatDelay; + // Whether key repeat is enabled. + bool keyRepeatEnabled; + InputDispatcherConfiguration() - : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {} + : keyRepeatTimeout(500 * 1000000LL), + keyRepeatDelay(50 * 1000000LL), + keyRepeatEnabled(true) {} }; } // namespace android diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 653f595670..463a95238b 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -227,10 +227,11 @@ public: virtual void cancelCurrentTouch() = 0; /* - * Updates key repeat configuration timeout and delay. + * Updates whether key repeat is enabled and key repeat configuration timeout and delay. */ virtual void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, - std::chrono::nanoseconds delay) = 0; + std::chrono::nanoseconds delay, + bool keyRepeatEnabled) = 0; /* * Determine if a pointer from a device is being dispatched to the given window. diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 7bec94eea1..2f6c6d74e7 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -93,6 +93,9 @@ struct InputReaderConfiguration { // The touchpad settings changed. TOUCHPAD_SETTINGS = 1u << 13, + // The key remapping has changed. + KEY_REMAPPING = 1u << 14, + // All devices must be reopened. MUST_REOPEN = 1u << 31, }; @@ -246,6 +249,9 @@ struct InputReaderConfiguration { // True if a pointer icon should be shown for direct stylus pointers. bool stylusPointerIconEnabled; + // Keycodes to be remapped. + std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping; + InputReaderConfiguration() : virtualKeyQuietTime(0), defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT), @@ -333,9 +339,6 @@ public: 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; - virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const = 0; - virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0; /* Toggle Caps Lock */ @@ -466,6 +469,9 @@ public: virtual void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, int32_t deviceId) = 0; + /* Sends the Info of gestures that happen on the touchpad. */ + virtual void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) = 0; + /* Gets the keyboard layout for a particular input device. */ virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h index 5e75027056..fcd913db7c 100644 --- a/services/inputflinger/include/InputThread.h +++ b/services/inputflinger/include/InputThread.h @@ -38,6 +38,7 @@ private: std::string mName; std::function<void()> mThreadWake; sp<Thread> mThread; + bool applyInputEventProfile(); }; } // namespace android diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index e34ed0fbd8..8f3d9ca778 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -72,10 +72,6 @@ public: /* Dumps the state of the pointer controller. */ virtual std::string dump() = 0; - /* Gets the bounds of the region that the pointer can traverse. - * Returns true if the bounds are available. */ - virtual std::optional<FloatRect> getBounds() const = 0; - /* Move the pointer. */ virtual void move(float deltaX, float deltaY) = 0; diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index e11adb8c76..0865eed4a2 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -1177,7 +1177,8 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t return false; } -void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { +void EventHub::setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); if (device == nullptr) { @@ -1185,7 +1186,7 @@ void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t to } const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap(); if (kcm) { - kcm->addKeyRemapping(fromKeyCode, toKeyCode); + kcm->setKeyRemapping(keyRemapping); } } diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 70f024ea96..6185f1ab9e 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -365,6 +365,18 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, // so update the enabled state when there is a change in display info. out += updateEnableState(when, readerConfig, forceEnable); } + + if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEY_REMAPPING)) { + const bool isFullKeyboard = + (mSources & AINPUT_SOURCE_KEYBOARD) == AINPUT_SOURCE_KEYBOARD && + mKeyboardType == KeyboardType::ALPHABETIC; + if (isFullKeyboard) { + for_each_subdevice([&readerConfig](auto& context) { + context.setKeyRemapping(readerConfig.keyRemapping); + }); + bumpGeneration(); + } + } } return out; } @@ -689,12 +701,6 @@ void InputDevice::updateMetaState(int32_t keyCode) { }); } -void InputDevice::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) { - for_each_subdevice([fromKeyCode, toKeyCode](auto& context) { - context.addKeyRemapping(fromKeyCode, toKeyCode); - }); -} - void InputDevice::bumpGeneration() { mGeneration = mContext->bumpGeneration(); } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index a5b12490a3..e579390005 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -625,15 +625,6 @@ bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceM return result; } -void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { - std::scoped_lock _l(mLock); - - InputDevice* device = findInputDeviceLocked(deviceId); - if (device != nullptr) { - device->addKeyRemapping(fromKeyCode, toKeyCode); - } -} - int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { std::scoped_lock _l(mLock); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 657126a825..edc30379b2 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -281,8 +281,8 @@ public: virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0; - virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const = 0; + virtual void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const = 0; virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, @@ -513,8 +513,8 @@ public: bool hasMscEvent(int32_t deviceId, int mscEvent) const override final; - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, - int32_t toKeyCode) const override final; + void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const override final; status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 021978dee7..62cc4da5ec 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -124,8 +124,6 @@ public: int32_t getMetaState(); void updateMetaState(int32_t keyCode); - void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode); - void setKeyboardType(KeyboardType keyboardType); void bumpGeneration(); @@ -329,8 +327,8 @@ public: inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); } - inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const { - mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode); + inline void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) const { + mEventHub->setKeyRemapping(mId, keyRemapping); } inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState, diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 2cc0a00496..100387195a 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -65,8 +65,6 @@ public: int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override; int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override; - int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override; void toggleCapsLockState(int32_t deviceId) override; diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index dbc28721d1..9a36bfbeaf 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -425,7 +425,7 @@ std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent& rawEvent) { std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent); if (state) { if (mTouchpadHardwareStateNotificationsEnabled) { - getPolicy()->notifyTouchpadHardwareState(*state, rawEvent.deviceId); + getPolicy()->notifyTouchpadHardwareState(*state, getDeviceId()); } updatePalmDetectionMetrics(); return sendHardwareState(rawEvent.when, rawEvent.readTime, *state); @@ -480,6 +480,9 @@ void TouchpadInputMapper::consumeGesture(const Gesture* gesture) { return; } mGesturesToProcess.push_back(*gesture); + if (mTouchpadHardwareStateNotificationsEnabled) { + getPolicy()->notifyTouchpadGestureInfo(gesture->type, getDeviceId()); + } } std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) { diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 9924d0d491..da2c683d95 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -60,6 +60,21 @@ uint32_t gesturesButtonToMotionEventButton(uint32_t gesturesButton) { } } +bool isGestureNoFocusChange(MotionClassification classification) { + switch (classification) { + case MotionClassification::TWO_FINGER_SWIPE: + case MotionClassification::MULTI_FINGER_SWIPE: + case MotionClassification::PINCH: + // Most gestures can be performed on an unfocused window, so they should not + // not affect window focus. + return true; + case MotionClassification::NONE: + case MotionClassification::AMBIGUOUS_GESTURE: + case MotionClassification::DEEP_PRESS: + return false; + } +} + } // namespace GestureConverter::GestureConverter(InputReaderContext& readerContext, @@ -67,6 +82,7 @@ GestureConverter::GestureConverter(InputReaderContext& readerContext, : mDeviceId(deviceId), mReaderContext(readerContext), mEnableFlingStop(input_flags::enable_touchpad_fling_stop()), + mEnableNoFocusChange(input_flags::enable_touchpad_no_focus_change()), // We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub // won't classify a device as a touchpad if they're not present. mXAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value()), @@ -338,7 +354,6 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data()); - args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); } float deltaX = gesture.details.scroll.dx; @@ -353,7 +368,6 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data()); - args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); return out; } @@ -427,7 +441,6 @@ std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data()); - args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); mCurrentClassification = MotionClassification::NONE; out += enterHover(when, readTime); @@ -624,6 +637,18 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime int32_t actionButton, int32_t buttonState, uint32_t pointerCount, const PointerCoords* pointerCoords) { + int32_t flags = 0; + if (action == AMOTION_EVENT_ACTION_CANCEL) { + flags |= AMOTION_EVENT_FLAG_CANCELED; + } + if (mEnableNoFocusChange && isGestureNoFocusChange(mCurrentClassification)) { + flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE; + } + if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) { + // This helps to make GestureDetector responsive. + flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; + } + return {mReaderContext.getNextId(), when, readTime, @@ -633,7 +658,7 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime /* policyFlags= */ POLICY_FLAG_WAKE, action, /* actionButton= */ actionButton, - /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0, + flags, mReaderContext.getGlobalMetaState(), buttonState, mCurrentClassification, diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index 829fb9289b..c9a35c151f 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -99,6 +99,7 @@ private: const int32_t mDeviceId; InputReaderContext& mReaderContext; const bool mEnableFlingStop; + const bool mEnableNoFocusChange; std::optional<ui::LogicalDisplayId> mDisplayId; FloatRect mBoundsInLogicalDisplay{}; diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs index 2d5039a1b1..e05e8e512f 100644 --- a/services/inputflinger/rust/bounce_keys_filter.rs +++ b/services/inputflinger/rust/bounce_keys_filter.rs @@ -17,12 +17,13 @@ //! Bounce keys input filter implementation. //! Bounce keys is an accessibility feature to aid users who have physical disabilities, that //! allows the user to configure the device to ignore rapid, repeated key presses of the same key. -use crate::input_filter::Filter; +use crate::input_filter::{Filter, VIRTUAL_KEYBOARD_DEVICE_ID}; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; +use input::KeyboardType; use log::debug; use std::collections::{HashMap, HashSet}; @@ -42,7 +43,7 @@ pub struct BounceKeysFilter { next: Box<dyn Filter + Send + Sync>, key_event_map: HashMap<i32, LastUpKeyEvent>, blocked_events: Vec<BlockedEvent>, - external_devices: HashSet<i32>, + supported_devices: HashSet<i32>, bounce_key_threshold_ns: i64, } @@ -56,7 +57,7 @@ impl BounceKeysFilter { next, key_event_map: HashMap::new(), blocked_events: Vec::new(), - external_devices: HashSet::new(), + supported_devices: HashSet::new(), bounce_key_threshold_ns, } } @@ -64,7 +65,10 @@ impl BounceKeysFilter { impl Filter for BounceKeysFilter { fn notify_key(&mut self, event: &KeyEvent) { - if !(self.external_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) { + // Check if it is a supported device and event source contains Source::KEYBOARD + if !(self.supported_devices.contains(&event.deviceId) + && event.source.0 & Source::KEYBOARD.0 != 0) + { self.next.notify_key(event); return; } @@ -110,10 +114,17 @@ impl Filter for BounceKeysFilter { self.blocked_events.retain(|blocked_event| { device_infos.iter().any(|x| blocked_event.device_id == x.deviceId) }); - self.external_devices.clear(); + self.supported_devices.clear(); for device_info in device_infos { - if device_info.external { - self.external_devices.insert(device_info.deviceId); + if device_info.deviceId == VIRTUAL_KEYBOARD_DEVICE_ID { + continue; + } + if device_info.keyboardType == KeyboardType::None as i32 { + continue; + } + // Support Alphabetic keyboards and Non-alphabetic external keyboards + if device_info.external || device_info.keyboardType == KeyboardType::Alphabetic as i32 { + self.supported_devices.insert(device_info.deviceId); } } self.next.notify_devices_changed(device_infos); @@ -122,16 +133,26 @@ impl Filter for BounceKeysFilter { fn destroy(&mut self) { self.next.destroy(); } + + fn dump(&mut self, dump_str: String) -> String { + let mut result = "Bounce Keys filter: \n".to_string(); + result += &format!("\tthreshold = {:?}ns\n", self.bounce_key_threshold_ns); + result += &format!("\tkey_event_map = {:?}\n", self.key_event_map); + result += &format!("\tblocked_events = {:?}\n", self.blocked_events); + result += &format!("\tsupported_devices = {:?}\n", self.supported_devices); + self.next.dump(dump_str + &result) + } } #[cfg(test)] mod tests { use crate::bounce_keys_filter::BounceKeysFilter; - use crate::input_filter::{test_filter::TestFilter, Filter}; + use crate::input_filter::{test_filter::TestFilter, Filter, VIRTUAL_KEYBOARD_DEVICE_ID}; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::KeyboardType; static BASE_KEY_EVENT: KeyEvent = KeyEvent { id: 1, @@ -156,6 +177,7 @@ mod tests { Box::new(next.clone()), 1, /* device_id */ 100, /* threshold */ + KeyboardType::Alphabetic, ); let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; @@ -181,12 +203,103 @@ mod tests { } #[test] - fn test_is_notify_key_doesnt_block_for_internal_keyboard() { + fn test_is_notify_key_for_tv_remote() { + let mut next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + 1, /* device_id */ + 100, /* threshold */ + KeyboardType::NonAlphabetic, + ); + + let source = Source(Source::KEYBOARD.0 | Source::DPAD.0); + let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { action: KeyEventAction::UP, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + next.clear(); + let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = + KeyEvent { eventTime: 100, action: KeyEventAction::UP, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = + KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_blocks_for_internal_keyboard() { + let mut next = TestFilter::new(); + let mut filter = setup_filter_with_internal_device( + Box::new(next.clone()), + 1, /* device_id */ + 100, /* threshold */ + KeyboardType::Alphabetic, + ); + + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + next.clear(); + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = KeyEvent { eventTime: 100, action: KeyEventAction::UP, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_doesnt_block_for_internal_non_alphabetic_keyboard() { let next = TestFilter::new(); let mut filter = setup_filter_with_internal_device( Box::new(next.clone()), 1, /* device_id */ 100, /* threshold */ + KeyboardType::NonAlphabetic, + ); + + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_doesnt_block_for_virtual_keyboard() { + let next = TestFilter::new(); + let mut filter = setup_filter_with_internal_device( + Box::new(next.clone()), + VIRTUAL_KEYBOARD_DEVICE_ID, /* device_id */ + 100, /* threshold */ + KeyboardType::Alphabetic, ); let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; @@ -209,6 +322,7 @@ mod tests { Box::new(next.clone()), 1, /* device_id */ 100, /* threshold */ + KeyboardType::NonAlphabetic, ); let event = @@ -233,12 +347,60 @@ mod tests { let mut filter = setup_filter_with_devices( Box::new(next.clone()), &[ - DeviceInfo { deviceId: 1, external: true }, - DeviceInfo { deviceId: 2, external: true }, + DeviceInfo { + deviceId: 1, + external: true, + keyboardType: KeyboardType::Alphabetic as i32, + }, + DeviceInfo { + deviceId: 2, + external: true, + keyboardType: KeyboardType::Alphabetic as i32, + }, + ], + 100, /* threshold */ + ); + + // Bounce key scenario on the external keyboard + let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + let event = KeyEvent { deviceId: 1, action: KeyEventAction::UP, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + + next.clear(); + let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert!(next.last_event().is_none()); + + let event = KeyEvent { deviceId: 2, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; + filter.notify_key(&event); + assert_eq!(next.last_event().unwrap(), event); + } + + #[test] + fn test_is_notify_key_for_external_and_internal_alphabetic_keyboards() { + let mut next = TestFilter::new(); + let mut filter = setup_filter_with_devices( + Box::new(next.clone()), + &[ + DeviceInfo { + deviceId: 1, + external: false, + keyboardType: KeyboardType::Alphabetic as i32, + }, + DeviceInfo { + deviceId: 2, + external: true, + keyboardType: KeyboardType::Alphabetic as i32, + }, ], 100, /* threshold */ ); + // Bounce key scenario on the internal keyboard let event = KeyEvent { deviceId: 1, action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; filter.notify_key(&event); assert_eq!(next.last_event().unwrap(), event); @@ -261,10 +423,15 @@ mod tests { next: Box<dyn Filter + Send + Sync>, device_id: i32, threshold: i64, + keyboard_type: KeyboardType, ) -> BounceKeysFilter { setup_filter_with_devices( next, - &[DeviceInfo { deviceId: device_id, external: true }], + &[DeviceInfo { + deviceId: device_id, + external: true, + keyboardType: keyboard_type as i32, + }], threshold, ) } @@ -273,10 +440,15 @@ mod tests { next: Box<dyn Filter + Send + Sync>, device_id: i32, threshold: i64, + keyboard_type: KeyboardType, ) -> BounceKeysFilter { setup_filter_with_devices( next, - &[DeviceInfo { deviceId: device_id, external: false }], + &[DeviceInfo { + deviceId: device_id, + external: false, + keyboardType: keyboard_type as i32, + }], threshold, ) } diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs index 8b44af38db..e2212449aa 100644 --- a/services/inputflinger/rust/input_filter.rs +++ b/services/inputflinger/rust/input_filter.rs @@ -35,11 +35,15 @@ use input::ModifierState; use log::{error, info}; use std::sync::{Arc, Mutex, RwLock}; +/// Virtual keyboard device ID +pub const VIRTUAL_KEYBOARD_DEVICE_ID: i32 = -1; + /// Interface for all the sub input filters pub trait Filter { fn notify_key(&mut self, event: &KeyEvent); fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]); fn destroy(&mut self); + fn dump(&mut self, dump_str: String) -> String; } struct InputFilterState { @@ -119,18 +123,30 @@ impl IInputFilter for InputFilter { self.input_filter_thread.clone(), )); state.enabled = true; - info!("Slow keys filter is installed"); + info!( + "Slow keys filter is installed, threshold = {:?}ns", + config.slowKeysThresholdNs + ); } if config.bounceKeysThresholdNs > 0 { first_filter = Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs)); state.enabled = true; - info!("Bounce keys filter is installed"); + info!( + "Bounce keys filter is installed, threshold = {:?}ns", + config.bounceKeysThresholdNs + ); } state.first_filter = first_filter; } Result::Ok(()) } + + fn dumpFilter(&self) -> binder::Result<String> { + let first_filter = &mut self.state.lock().unwrap().first_filter; + let dump_str = first_filter.dump(String::new()); + Result::Ok(dump_str) + } } struct BaseFilter { @@ -158,6 +174,11 @@ impl Filter for BaseFilter { fn destroy(&mut self) { // do nothing } + + fn dump(&mut self, dump_str: String) -> String { + // do nothing + dump_str + } } /// This struct wraps around IInputFilterCallbacks restricting access to only @@ -214,6 +235,7 @@ mod tests { InputFilterConfiguration::InputFilterConfiguration, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::KeyboardType; use std::sync::{Arc, RwLock}; #[test] @@ -256,7 +278,11 @@ mod tests { Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks)))), ); assert!(input_filter - .notifyInputDevicesChanged(&[DeviceInfo { deviceId: 0, external: true }]) + .notifyInputDevicesChanged(&[DeviceInfo { + deviceId: 0, + external: true, + keyboardType: KeyboardType::None as i32 + }]) .is_ok()); assert!(test_filter.is_device_changed_called()); } @@ -389,6 +415,10 @@ pub mod test_filter { fn destroy(&mut self) { self.inner().is_destroy_called = true; } + fn dump(&mut self, dump_str: String) -> String { + // do nothing + dump_str + } } } diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs index 0f18a2f395..8830aac529 100644 --- a/services/inputflinger/rust/slow_keys_filter.rs +++ b/services/inputflinger/rust/slow_keys_filter.rs @@ -18,12 +18,13 @@ //! Slow keys is an accessibility feature to aid users who have physical disabilities, that allows //! the user to specify the duration for which one must press-and-hold a key before the system //! accepts the keypress. -use crate::input_filter::Filter; +use crate::input_filter::{Filter, VIRTUAL_KEYBOARD_DEVICE_ID}; use crate::input_filter_thread::{InputFilterThread, ThreadCallback}; use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source; use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; +use input::KeyboardType; use log::debug; use std::collections::HashSet; use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}; @@ -41,7 +42,7 @@ struct OngoingKeyDown { struct SlowKeysFilterInner { next: Box<dyn Filter + Send + Sync>, slow_key_threshold_ns: i64, - external_devices: HashSet<i32>, + supported_devices: HashSet<i32>, // This tracks KeyEvents that are blocked by Slow keys filter and will be passed through if the // press duration exceeds the slow keys threshold. pending_down_events: Vec<KeyEvent>, @@ -65,7 +66,7 @@ impl SlowKeysFilter { let filter = Self(Arc::new(RwLock::new(SlowKeysFilterInner { next, slow_key_threshold_ns, - external_devices: HashSet::new(), + supported_devices: HashSet::new(), pending_down_events: Vec::new(), ongoing_down_events: Vec::new(), input_filter_thread: input_filter_thread.clone(), @@ -98,8 +99,8 @@ impl Filter for SlowKeysFilter { { // acquire write lock let mut slow_filter = self.write_inner(); - if !(slow_filter.external_devices.contains(&event.deviceId) - && event.source == Source::KEYBOARD) + if !(slow_filter.supported_devices.contains(&event.deviceId) + && event.source.0 & Source::KEYBOARD.0 != 0) { slow_filter.next.notify_key(event); return; @@ -164,10 +165,17 @@ impl Filter for SlowKeysFilter { slow_filter .ongoing_down_events .retain(|event| device_infos.iter().any(|x| event.device_id == x.deviceId)); - slow_filter.external_devices.clear(); + slow_filter.supported_devices.clear(); for device_info in device_infos { - if device_info.external { - slow_filter.external_devices.insert(device_info.deviceId); + if device_info.deviceId == VIRTUAL_KEYBOARD_DEVICE_ID { + continue; + } + if device_info.keyboardType == KeyboardType::None as i32 { + continue; + } + // Support Alphabetic keyboards and Non-alphabetic external keyboards + if device_info.external || device_info.keyboardType == KeyboardType::Alphabetic as i32 { + slow_filter.supported_devices.insert(device_info.deviceId); } } slow_filter.next.notify_devices_changed(device_infos); @@ -178,6 +186,16 @@ impl Filter for SlowKeysFilter { slow_filter.input_filter_thread.unregister_thread_callback(Box::new(self.clone())); slow_filter.next.destroy(); } + + fn dump(&mut self, dump_str: String) -> String { + let mut slow_filter = self.write_inner(); + let mut result = "Slow Keys filter: \n".to_string(); + result += &format!("\tthreshold = {:?}ns\n", slow_filter.slow_key_threshold_ns); + result += &format!("\tongoing_down_events = {:?}\n", slow_filter.ongoing_down_events); + result += &format!("\tpending_down_events = {:?}\n", slow_filter.pending_down_events); + result += &format!("\tsupported_devices = {:?}\n", slow_filter.supported_devices); + slow_filter.next.dump(dump_str + &result) + } } impl ThreadCallback for SlowKeysFilter { @@ -217,6 +235,7 @@ mod tests { use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{ DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::KeyboardType; use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId}; use std::sync::{Arc, RwLock}; use std::time::Duration; @@ -240,7 +259,7 @@ mod tests { static SLOW_KEYS_THRESHOLD_NS: i64 = 100 * 1000000; // 100 ms #[test] - fn test_is_notify_key_for_internal_keyboard_not_blocked() { + fn test_is_notify_key_for_internal_non_alphabetic_keyboard_not_blocked() { let test_callbacks = TestCallbacks::new(); let test_thread = get_thread(test_callbacks.clone()); let next = TestFilter::new(); @@ -249,6 +268,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::NonAlphabetic, ); let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT }; @@ -266,6 +286,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::NonAlphabetic, ); let event = @@ -275,6 +296,115 @@ mod tests { } #[test] + fn test_notify_key_for_tv_remote_when_key_pressed_for_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = get_thread(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_external_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + SLOW_KEYS_THRESHOLD_NS, + KeyboardType::NonAlphabetic, + ); + let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + let source = Source(Source::KEYBOARD.0 | Source::DPAD.0); + filter.notify_key(&KeyEvent { + action: KeyEventAction::DOWN, + downTime: down_time, + eventTime: down_time, + source, + ..BASE_KEY_EVENT + }); + assert!(next.last_event().is_none()); + + std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64)); + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::DOWN, + downTime: down_time + SLOW_KEYS_THRESHOLD_NS, + eventTime: down_time + SLOW_KEYS_THRESHOLD_NS, + source, + policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT, + ..BASE_KEY_EVENT + } + ); + + let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + filter.notify_key(&KeyEvent { + action: KeyEventAction::UP, + downTime: down_time, + eventTime: up_time, + source, + ..BASE_KEY_EVENT + }); + + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::UP, + downTime: down_time + SLOW_KEYS_THRESHOLD_NS, + eventTime: up_time, + source, + ..BASE_KEY_EVENT + } + ); + } + + #[test] + fn test_notify_key_for_internal_alphabetic_keyboard_when_key_pressed_for_threshold_time() { + let test_callbacks = TestCallbacks::new(); + let test_thread = get_thread(test_callbacks.clone()); + let next = TestFilter::new(); + let mut filter = setup_filter_with_internal_device( + Box::new(next.clone()), + test_thread.clone(), + 1, /* device_id */ + SLOW_KEYS_THRESHOLD_NS, + KeyboardType::Alphabetic, + ); + let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + filter.notify_key(&KeyEvent { + action: KeyEventAction::DOWN, + downTime: down_time, + eventTime: down_time, + ..BASE_KEY_EVENT + }); + assert!(next.last_event().is_none()); + + std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64)); + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::DOWN, + downTime: down_time + SLOW_KEYS_THRESHOLD_NS, + eventTime: down_time + SLOW_KEYS_THRESHOLD_NS, + policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT, + ..BASE_KEY_EVENT + } + ); + + let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); + filter.notify_key(&KeyEvent { + action: KeyEventAction::UP, + downTime: down_time, + eventTime: up_time, + ..BASE_KEY_EVENT + }); + + assert_eq!( + next.last_event().unwrap(), + KeyEvent { + action: KeyEventAction::UP, + downTime: down_time + SLOW_KEYS_THRESHOLD_NS, + eventTime: up_time, + ..BASE_KEY_EVENT + } + ); + } + + #[test] fn test_notify_key_for_external_keyboard_when_key_pressed_for_threshold_time() { let test_callbacks = TestCallbacks::new(); let test_thread = get_thread(test_callbacks.clone()); @@ -284,6 +414,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::Alphabetic, ); let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); filter.notify_key(&KeyEvent { @@ -335,6 +466,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::Alphabetic, ); let mut now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); filter.notify_key(&KeyEvent { @@ -367,6 +499,7 @@ mod tests { test_thread.clone(), 1, /* device_id */ SLOW_KEYS_THRESHOLD_NS, + KeyboardType::Alphabetic, ); let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds(); @@ -388,11 +521,16 @@ mod tests { test_thread: InputFilterThread, device_id: i32, threshold: i64, + keyboard_type: KeyboardType, ) -> SlowKeysFilter { setup_filter_with_devices( next, test_thread, - &[DeviceInfo { deviceId: device_id, external: true }], + &[DeviceInfo { + deviceId: device_id, + external: true, + keyboardType: keyboard_type as i32, + }], threshold, ) } @@ -402,11 +540,16 @@ mod tests { test_thread: InputFilterThread, device_id: i32, threshold: i64, + keyboard_type: KeyboardType, ) -> SlowKeysFilter { setup_filter_with_devices( next, test_thread, - &[DeviceInfo { deviceId: device_id, external: false }], + &[DeviceInfo { + deviceId: device_id, + external: false, + keyboardType: keyboard_type as i32, + }], threshold, ) } diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs index 6c7c7fba39..161a5fca60 100644 --- a/services/inputflinger/rust/sticky_keys_filter.rs +++ b/services/inputflinger/rust/sticky_keys_filter.rs @@ -134,6 +134,14 @@ impl Filter for StickyKeysFilter { fn destroy(&mut self) { self.next.destroy(); } + + fn dump(&mut self, dump_str: String) -> String { + let mut result = "Sticky Keys filter: \n".to_string(); + result += &format!("\tmodifier_state = {:?}\n", self.modifier_state); + result += &format!("\tlocked_modifier_state = {:?}\n", self.locked_modifier_state); + result += &format!("\tcontributing_devices = {:?}\n", self.contributing_devices); + self.next.dump(dump_str + &result) + } } fn is_modifier_key(keycode: i32) -> bool { @@ -235,6 +243,7 @@ mod tests { DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction, }; + use input::KeyboardType; use input::ModifierState; use std::sync::{Arc, RwLock}; @@ -496,7 +505,11 @@ mod tests { ..BASE_KEY_UP }); - sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]); + sticky_keys_filter.notify_devices_changed(&[DeviceInfo { + deviceId: 2, + external: true, + keyboardType: KeyboardType::Alphabetic as i32, + }]); assert_eq!( test_callbacks.get_last_modifier_state(), ModifierState::CtrlLeftOn | ModifierState::CtrlOn diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 95283ba6ab..744cf4a514 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -123,4 +123,5 @@ cc_test { "device-tests", "device-platinum-tests", ], + native_coverage: false, } diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp index d39ad3fd1e..353011aa5c 100644 --- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp +++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp @@ -755,7 +755,7 @@ TEST_F(CapturedTouchpadEventConverterTest, TwoFingers_motionReportedCorrectly) { WithMotionAction(AMOTION_EVENT_ACTION_UP)))); EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(AllOf(WithPointerCount(1u), WithCoords(255, 202), - WithPointerRelativeMotion(1, 0, 0), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER))))); } diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp index 31fbf209a3..943de6e3cf 100644 --- a/services/inputflinger/tests/FakeEventHub.cpp +++ b/services/inputflinger/tests/FakeEventHub.cpp @@ -151,9 +151,10 @@ void FakeEventHub::addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int3 getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode); } -void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { +void FakeEventHub::setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const { Device* device = getDevice(deviceId); - device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode); + device->keyRemapping = keyRemapping; } void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) { diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h index 3d8dddd532..2dfbb2388a 100644 --- a/services/inputflinger/tests/FakeEventHub.h +++ b/services/inputflinger/tests/FakeEventHub.h @@ -55,7 +55,7 @@ class FakeEventHub : public EventHubInterface { KeyedVector<int32_t, int32_t> absoluteAxisValue; KeyedVector<int32_t, KeyInfo> keysByScanCode; KeyedVector<int32_t, KeyInfo> keysByUsageCode; - std::unordered_map<int32_t, int32_t> keyRemapping; + std::map<int32_t, int32_t> keyRemapping; KeyedVector<int32_t, bool> leds; // fake mapping which would normally come from keyCharacterMap std::unordered_map<int32_t, int32_t> keyCodeMapping; @@ -129,7 +129,7 @@ public: void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode, uint32_t flags); void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode); - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const; + void setKeyRemapping(int32_t deviceId, const std::map<int32_t, int32_t>& keyRemapping) const; void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition); void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType, diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index d77d539f74..f373cac085 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -32,19 +32,23 @@ static const int HW_TIMEOUT_MULTIPLIER = base::GetIntProperty("ro.hw_timeout_mul } // namespace void FakeInputReaderPolicy::assertInputDevicesChanged() { - waitForInputDevices([](bool devicesChanged) { - if (!devicesChanged) { - FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; - } - }); + waitForInputDevices( + [](bool devicesChanged) { + if (!devicesChanged) { + FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called."; + } + }, + ADD_INPUT_DEVICE_TIMEOUT); } void FakeInputReaderPolicy::assertInputDevicesNotChanged() { - waitForInputDevices([](bool devicesChanged) { - if (devicesChanged) { - FAIL() << "Expected notifyInputDevicesChanged() to not be called."; - } - }); + waitForInputDevices( + [](bool devicesChanged) { + if (devicesChanged) { + FAIL() << "Expected notifyInputDevicesChanged() to not be called."; + } + }, + INPUT_DEVICES_DIDNT_CHANGE_TIMEOUT); } void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) { @@ -252,6 +256,10 @@ void FakeInputReaderPolicy::notifyTouchpadHardwareState(const SelfContainedHardw mTouchpadHardwareStateNotified.notify_all(); } +void FakeInputReaderPolicy::notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) { + std::scoped_lock lock(mLock); +} + std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) { return nullptr; @@ -261,13 +269,13 @@ std::string FakeInputReaderPolicy::getDeviceAlias(const InputDeviceIdentifier&) return ""; } -void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged) { +void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged, + std::chrono::milliseconds timeout) { std::unique_lock<std::mutex> lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); const bool devicesChanged = - mDevicesChangedCondition.wait_for(lock, - ADD_INPUT_DEVICE_TIMEOUT * HW_TIMEOUT_MULTIPLIER, + mDevicesChangedCondition.wait_for(lock, timeout * HW_TIMEOUT_MULTIPLIER, [this]() REQUIRES(mLock) { return mInputDevicesChanged; }); diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index e5ba620555..3a2b4e9ed9 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -85,10 +85,12 @@ private: void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override; void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, int32_t deviceId) override; + void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override; std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override; std::string getDeviceAlias(const InputDeviceIdentifier&) override; - void waitForInputDevices(std::function<void(bool)> processDevicesChanged); + void waitForInputDevices(std::function<void(bool)> processDevicesChanged, + std::chrono::milliseconds timeout); void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override; mutable std::mutex mLock; diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp index d0998ba851..887a939e09 100644 --- a/services/inputflinger/tests/FakePointerController.cpp +++ b/services/inputflinger/tests/FakePointerController.cpp @@ -148,12 +148,6 @@ bool FakePointerController::isPointerShown() { return mIsPointerShown; } -std::optional<FloatRect> FakePointerController::getBounds() const { - if (!mEnabled) return std::nullopt; - - return mHaveBounds ? std::make_optional<FloatRect>(mMinX, mMinY, mMaxX, mMaxY) : std::nullopt; -} - void FakePointerController::move(float deltaX, float deltaY) { if (!mEnabled) return; diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index 2c76c6214c..9b773a7715 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -65,7 +65,6 @@ public: private: std::string dump() override { return ""; } - std::optional<FloatRect> getBounds() const override; void move(float deltaX, float deltaY) override; void unfade(Transition) override; void setPresentation(Presentation) override {} diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index d0cd677ade..225ae0f67c 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -279,6 +279,8 @@ TEST_F(GestureConverterTest, DragWithButton) { } TEST_F(GestureConverterTest, Scroll) { + input_flags::enable_touchpad_no_focus_change(true); + const nsecs_t downTime = 12345; InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); @@ -300,7 +302,8 @@ TEST_F(GestureConverterTest, Scroll) { ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>( AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE | + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE), WithToolType(ToolType::FINGER), WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); @@ -312,7 +315,8 @@ TEST_F(GestureConverterTest, Scroll) { WithGestureScrollDistance(0, 5, EPSILON), WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE | + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE), WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1, @@ -325,7 +329,8 @@ TEST_F(GestureConverterTest, Scroll) { WithGestureScrollDistance(0, 0, EPSILON), WithMotionClassification( MotionClassification::TWO_FINGER_SWIPE), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE | + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), @@ -845,6 +850,8 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { } TEST_F(GestureConverterTest, Pinch_Inwards) { + input_flags::enable_touchpad_no_focus_change(true); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ui::LogicalDisplayId::DEFAULT); @@ -867,7 +874,8 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { AllOf(WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithToolType(ToolType::FINGER), - WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); + WithDisplayId(ui::LogicalDisplayId::DEFAULT), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 0.8, GESTURES_ZOOM_UPDATE); @@ -879,7 +887,8 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { WithGesturePinchScaleFactor(0.8f, EPSILON), WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0), WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); + WithDisplayId(ui::LogicalDisplayId::DEFAULT), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); @@ -891,12 +900,14 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCount(2u))), + WithPointerCount(2u), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCount(1u))), + WithPointerCount(1u), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), @@ -908,6 +919,8 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { } TEST_F(GestureConverterTest, Pinch_Outwards) { + input_flags::enable_touchpad_no_focus_change(true); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ui::LogicalDisplayId::DEFAULT); @@ -930,7 +943,8 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { AllOf(WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithToolType(ToolType::FINGER), - WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); + WithDisplayId(ui::LogicalDisplayId::DEFAULT), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))))); Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1.1, GESTURES_ZOOM_UPDATE); @@ -942,7 +956,8 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { WithGesturePinchScaleFactor(1.1f, EPSILON), WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0), WithPointerCount(2u), WithToolType(ToolType::FINGER), - WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); + WithDisplayId(ui::LogicalDisplayId::DEFAULT), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))))); Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1, GESTURES_ZOOM_END); @@ -954,12 +969,14 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCount(2u))), + WithPointerCount(2u), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), - WithPointerCount(1u))), + WithPointerCount(1u), + WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), @@ -1055,6 +1072,8 @@ TEST_F(GestureConverterTest, ResetWithButtonPressed) { } TEST_F(GestureConverterTest, ResetDuringScroll) { + input_flags::enable_touchpad_no_focus_change(true); + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); converter.setDisplayId(ui::LogicalDisplayId::DEFAULT); @@ -1070,7 +1089,8 @@ TEST_F(GestureConverterTest, ResetDuringScroll) { WithGestureScrollDistance(0, 0, EPSILON), WithMotionClassification( MotionClassification::TWO_FINGER_SWIPE), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE | + AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))), VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c2f174f6b4..c5702e92d9 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1913,6 +1913,99 @@ TEST_F(InputDispatcherTest, HoverMoveAndScroll) { window->consumeMotionEvent(WithMotionAction(ACTION_SCROLL)); } +/** + * Two windows: a trusted overlay and a regular window underneath. Both windows are visible. + * Mouse is hovered, and the hover event should only go to the overlay. + * However, next, the touchable region of the trusted overlay shrinks. The mouse position hasn't + * changed, but the cursor would now end up hovering above the regular window underneatch. + * If the mouse is now clicked, this would generate an ACTION_DOWN event, which would go to the + * regular window. However, the trusted overlay is also watching for outside touch. + * The trusted overlay should get two events: + * 1) The ACTION_OUTSIDE event, since the click is now not inside its touchable region + * 2) The HOVER_EXIT event, since the mouse pointer is no longer hovering inside this window + * + * This test reproduces a crash where there is an overlap between dispatch modes for the trusted + * overlay touch target, since the event is causing both an ACTION_OUTSIDE, and as a HOVER_EXIT. + */ +TEST_F(InputDispatcherTest, MouseClickUnderShrinkingTrustedOverlay) { + std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(app, mDispatcher, "Trusted overlay", + ui::LogicalDisplayId::DEFAULT); + overlay->setTrustedOverlay(true); + overlay->setWatchOutsideTouch(true); + overlay->setFrame(Rect(0, 0, 200, 200)); + + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(app, mDispatcher, "Regular window", + ui::LogicalDisplayId::DEFAULT); + window->setFrame(Rect(0, 0, 200, 200)); + + mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0}); + // Hover the mouse into the overlay + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110)) + .build()); + overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + // Now, shrink the touchable region of the overlay! This will cause the cursor to suddenly have + // the regular window as the touch target + overlay->setTouchableRegion(Region({0, 0, 0, 0})); + mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0}); + + // Now we can click with the mouse. The click should go into the regular window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110)) + .build()); + overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + overlay->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE)); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); +} + +/** + * Similar to above, but also has a spy on top that also catches the HOVER + * events. Also, instead of ACTION_DOWN, we are continuing to send the hovering + * stream to ensure that the spy receives hover events correctly. + */ +TEST_F(InputDispatcherTest, MouseClickUnderShrinkingTrustedOverlayWithSpy) { + std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> spyWindow = + sp<FakeWindowHandle>::make(app, mDispatcher, "Spy", ui::LogicalDisplayId::DEFAULT); + spyWindow->setFrame(Rect(0, 0, 200, 200)); + spyWindow->setTrustedOverlay(true); + spyWindow->setSpy(true); + sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(app, mDispatcher, "Trusted overlay", + ui::LogicalDisplayId::DEFAULT); + overlay->setTrustedOverlay(true); + overlay->setWatchOutsideTouch(true); + overlay->setFrame(Rect(0, 0, 200, 200)); + + sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(app, mDispatcher, "Regular window", + ui::LogicalDisplayId::DEFAULT); + window->setFrame(Rect(0, 0, 200, 200)); + + mDispatcher->onWindowInfosChanged( + {{*spyWindow->getInfo(), *overlay->getInfo(), *window->getInfo()}, {}, 0, 0}); + // Hover the mouse into the overlay + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110)) + .build()); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + // Now, shrink the touchable region of the overlay! This will cause the cursor to suddenly have + // the regular window as the touch target + overlay->setTouchableRegion(Region({0, 0, 0, 0})); + mDispatcher->onWindowInfosChanged( + {{*spyWindow->getInfo(), *overlay->getInfo(), *window->getInfo()}, {}, 0, 0}); + + // Now we can click with the mouse. The click should go into the regular window + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110)) + .build()); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE)); + overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); +} + using InputDispatcherMultiDeviceTest = InputDispatcherTest; /** @@ -9091,6 +9184,7 @@ class InputDispatcherKeyRepeatTest : public InputDispatcherTest { protected: static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms; static constexpr std::chrono::nanoseconds KEY_REPEAT_DELAY = 40ms; + static constexpr bool KEY_REPEAT_ENABLED = true; std::shared_ptr<FakeApplicationHandle> mApp; sp<FakeWindowHandle> mWindow; @@ -9098,7 +9192,8 @@ protected: virtual void SetUp() override { InputDispatcherTest::SetUp(); - mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); + mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY, + KEY_REPEAT_ENABLED); setUpWindow(); } @@ -9247,6 +9342,24 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_CorrectRepeatCountWhenInjectK expectKeyRepeatOnce(3); } +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_NoRepeatWhenKeyRepeatDisabled) { + SCOPED_FLAG_OVERRIDE(keyboard_repeat_keys, true); + static constexpr std::chrono::milliseconds KEY_NO_REPEAT_ASSERTION_TIMEOUT = 100ms; + + mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY, + /*repeatKeyEnabled=*/false); + sendAndConsumeKeyDown(/*deviceId=*/1); + + ASSERT_GT(KEY_NO_REPEAT_ASSERTION_TIMEOUT, KEY_REPEAT_TIMEOUT) + << "Ensure the check for no key repeats extends beyond the repeat timeout duration."; + ASSERT_GT(KEY_NO_REPEAT_ASSERTION_TIMEOUT, KEY_REPEAT_DELAY) + << "Ensure the check for no key repeats extends beyond the repeat delay duration."; + + // No events should be returned if key repeat is turned off. + // Wait for KEY_NO_REPEAT_ASSERTION_TIMEOUT to return no events to ensure key repeat disabled. + mWindow->assertNoEvents(KEY_NO_REPEAT_ASSERTION_TIMEOUT); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 4a9e89319d..17c37d5a41 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -3330,11 +3330,11 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0); - mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B); KeyboardInputMapper& mapper = constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + mFakeEventHub->setKeyRemapping(EVENTHUB_ID, {{AKEYCODE_A, AKEYCODE_B}}); // Key down by scan code. process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1); NotifyKeyArgs args; diff --git a/services/inputflinger/tests/InputTracingTest.cpp b/services/inputflinger/tests/InputTracingTest.cpp index 2ccd93ec4c..3cc4bdd79e 100644 --- a/services/inputflinger/tests/InputTracingTest.cpp +++ b/services/inputflinger/tests/InputTracingTest.cpp @@ -133,8 +133,8 @@ protected: mDispatcher->setFocusedWindow(request); } - void tapAndExpect(const std::vector<const sp<FakeWindowHandle>>& windows, - Level inboundTraceLevel, Level dispatchTraceLevel, InputTraceSession& s) { + void tapAndExpect(const std::vector<sp<FakeWindowHandle>>& windows, Level inboundTraceLevel, + Level dispatchTraceLevel, InputTraceSession& s) { const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110)) .build(); @@ -156,7 +156,7 @@ protected: } } - void keypressAndExpect(const std::vector<const sp<FakeWindowHandle>>& windows, + void keypressAndExpect(const std::vector<sp<FakeWindowHandle>>& windows, Level inboundTraceLevel, Level dispatchTraceLevel, InputTraceSession& s) { const auto down = KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build(); diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 5a3d79da5e..f41b39ac8e 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -97,7 +97,8 @@ public: MOCK_METHOD(bool, hasRelativeAxis, (int32_t deviceId, int axis), (const)); MOCK_METHOD(bool, hasInputProperty, (int32_t deviceId, int property), (const)); MOCK_METHOD(bool, hasMscEvent, (int32_t deviceId, int mscEvent), (const)); - MOCK_METHOD(void, addKeyRemapping, (int32_t deviceId, int fromKeyCode, int toKeyCode), (const)); + MOCK_METHOD(void, setKeyRemapping, + (int32_t deviceId, (const std::map<int32_t, int32_t>& keyRemapping)), (const)); MOCK_METHOD(status_t, mapKey, (int32_t deviceId, int scanCode, int usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags), @@ -247,8 +248,6 @@ public: MOCK_METHOD(int32_t, getMetaState, (), ()); MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ()); - MOCK_METHOD(void, addKeyRemapping, (int32_t fromKeyCode, int32_t toKeyCode), ()); - MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ()); MOCK_METHOD(void, bumpGeneration, (), ()); diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index 4fcffddee2..3f14c2312b 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -61,12 +61,12 @@ const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds( InputEventTimeline getTestTimeline() { InputEventTimeline t( - /*isDown=*/true, /*eventTime=*/2, /*readTime=*/3, /*vendorId=*/0, /*productId=*/0, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT); ConnectionTimeline expectedCT(/*deliveryTime=*/6, /*consumeTime=*/7, /*finishTime=*/8); std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 9; @@ -87,7 +87,7 @@ protected: connection1 = sp<BBinder>::make(); connection2 = sp<BBinder>::make(); - mTracker = std::make_unique<LatencyTracker>(this); + mTracker = std::make_unique<LatencyTracker>(*this); setDefaultInputDeviceInfo(*mTracker); } void TearDown() override {} @@ -106,6 +106,8 @@ private: void processTimeline(const InputEventTimeline& timeline) override { mReceivedTimelines.push_back(timeline); } + void pushLatencyStatistics() override {} + std::string dump(const char* prefix) const { return ""; }; std::deque<InputEventTimeline> mReceivedTimelines; }; @@ -116,9 +118,10 @@ private: void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) { const nsecs_t triggerEventTime = lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1; - mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/true, triggerEventTime, + mTracker->trackListener(/*inputEventId=*/1, triggerEventTime, /*readTime=*/3, DEVICE_ID, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION); } void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) { @@ -167,12 +170,15 @@ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTim * any additional ConnectionTimeline's. */ TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { - mTracker->trackListener(/*inputEventId=*/1, /*isDown=*/false, /*eventTime=*/2, - /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(/*inputEventId=*/1, /*eventTime=*/2, + /*readTime=*/3, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}, + AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION); triggerEventReporting(/*eventTime=*/2); - assertReceivedTimeline(InputEventTimeline{/*isDown=*/false, /*eventTime=*/2, - /*readTime=*/3, /*vendorId=*/0, /*productID=*/0, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}}); + assertReceivedTimeline( + InputEventTimeline{/*eventTime=*/2, + /*readTime=*/3, /*vendorId=*/0, /*productID=*/0, + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT}); } /** @@ -203,8 +209,9 @@ TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { const auto& [connectionToken, expectedCT] = *expected.connectionTimelines.begin(); - mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime, - DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); mTracker->trackFinishedEvent(inputEventId, connectionToken, expectedCT.deliveryTime, expectedCT.consumeTime, expectedCT.finishTime); mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline); @@ -220,14 +227,15 @@ TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) { constexpr nsecs_t inputEventId = 1; constexpr nsecs_t readTime = 3; // does not matter for this test - constexpr bool isDown = true; // does not matter for this test // In the following 2 calls to trackListener, the inputEventId's are the same, but event times // are different. - mTracker->trackListener(inputEventId, isDown, /*eventTime=*/1, readTime, DEVICE_ID, - {InputDeviceUsageSource::UNKNOWN}); - mTracker->trackListener(inputEventId, isDown, /*eventTime=*/2, readTime, DEVICE_ID, - {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId, /*eventTime=*/1, readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); + mTracker->trackListener(inputEventId, /*eventTime=*/2, readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); triggerEventReporting(/*eventTime=*/2); // Since we sent duplicate input events, the tracker should just delete all of them, because it @@ -238,12 +246,12 @@ TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) { TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { constexpr int32_t inputEventId1 = 1; InputEventTimeline timeline1( - /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3, /*vendorId=*/0, /*productId=*/0, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + /*inputEventType=*/InputEventActionType::UNKNOWN_INPUT_EVENT); timeline1.connectionTimelines.emplace(connection1, ConnectionTimeline(/*deliveryTime*/ 6, /*consumeTime*/ 7, /*finishTime*/ 8)); @@ -255,12 +263,12 @@ TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { constexpr int32_t inputEventId2 = 10; InputEventTimeline timeline2( - /*isDown=*/false, /*eventTime=*/20, /*readTime=*/30, /*vendorId=*/0, /*productId=*/0, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT); timeline2.connectionTimelines.emplace(connection2, ConnectionTimeline(/*deliveryTime=*/60, /*consumeTime=*/70, @@ -272,11 +280,13 @@ TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { connectionTimeline2.setGraphicsTimeline(std::move(graphicsTimeline2)); // Start processing first event - mTracker->trackListener(inputEventId1, timeline1.isDown, timeline1.eventTime, - timeline1.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId1, timeline1.eventTime, timeline1.readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); // Start processing second event - mTracker->trackListener(inputEventId2, timeline2.isDown, timeline2.eventTime, - timeline2.readTime, DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId2, timeline2.eventTime, timeline2.readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); mTracker->trackFinishedEvent(inputEventId1, connection1, connectionTimeline1.deliveryTime, connectionTimeline1.consumeTime, connectionTimeline1.finishTime); @@ -301,12 +311,14 @@ TEST_F(LatencyTrackerTest, IncompleteEvents_AreHandledConsistently) { const sp<IBinder>& token = timeline.connectionTimelines.begin()->first; for (size_t i = 1; i <= 100; i++) { - mTracker->trackListener(/*inputEventId=*/i, timeline.isDown, timeline.eventTime, - timeline.readTime, /*deviceId=*/DEVICE_ID, - /*sources=*/{InputDeviceUsageSource::UNKNOWN}); - expectedTimelines.push_back(InputEventTimeline{timeline.isDown, timeline.eventTime, - timeline.readTime, timeline.vendorId, - timeline.productId, timeline.sources}); + mTracker->trackListener(/*inputEventId=*/i, timeline.eventTime, timeline.readTime, + /*deviceId=*/DEVICE_ID, + /*sources=*/{InputDeviceUsageSource::UNKNOWN}, + AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION); + expectedTimelines.push_back(InputEventTimeline{timeline.eventTime, timeline.readTime, + timeline.vendorId, timeline.productId, + timeline.sources, + timeline.inputEventActionType}); } // Now, complete the first event that was sent. mTracker->trackFinishedEvent(/*inputEventId=*/1, token, expectedCT.deliveryTime, @@ -332,12 +344,13 @@ TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { expectedCT.consumeTime, expectedCT.finishTime); mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline); - mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime, - DEVICE_ID, {InputDeviceUsageSource::UNKNOWN}); + mTracker->trackListener(inputEventId, expected.eventTime, expected.readTime, DEVICE_ID, + {InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL, + InputEventType::MOTION); triggerEventReporting(expected.eventTime); - assertReceivedTimeline(InputEventTimeline{expected.isDown, expected.eventTime, - expected.readTime, expected.vendorId, - expected.productId, expected.sources}); + assertReceivedTimeline(InputEventTimeline{expected.eventTime, expected.readTime, + expected.vendorId, expected.productId, + expected.sources, expected.inputEventActionType}); } /** @@ -348,22 +361,92 @@ TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline) { constexpr int32_t inputEventId = 1; InputEventTimeline timeline( - /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3, + /*eventTime*/ 2, /*readTime*/ 3, /*vendorId=*/50, /*productId=*/60, /*sources=*/ - {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT}); + {InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT}, + /*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT); InputDeviceInfo deviceInfo1 = generateTestDeviceInfo( /*vendorId=*/5, /*productId=*/6, /*deviceId=*/DEVICE_ID + 1); InputDeviceInfo deviceInfo2 = generateTestDeviceInfo( /*vendorId=*/50, /*productId=*/60, /*deviceId=*/DEVICE_ID); mTracker->setInputDevices({deviceInfo1, deviceInfo2}); - mTracker->trackListener(inputEventId, timeline.isDown, timeline.eventTime, timeline.readTime, - DEVICE_ID, + mTracker->trackListener(inputEventId, timeline.eventTime, timeline.readTime, DEVICE_ID, {InputDeviceUsageSource::TOUCHSCREEN, - InputDeviceUsageSource::STYLUS_DIRECT}); + InputDeviceUsageSource::STYLUS_DIRECT}, + AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION); triggerEventReporting(timeline.eventTime); assertReceivedTimeline(timeline); } +/** + * Check that InputEventActionType is correctly assigned to InputEventTimeline in trackListener. + */ +TEST_F(LatencyTrackerTest, TrackListenerCheck_InputEventActionTypeFieldInputEventTimeline) { + constexpr int32_t inputEventId = 1; + // Create timelines for different event types (Motion, Key) + InputEventTimeline motionDownTimeline( + /*eventTime*/ 2, /*readTime*/ 3, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_DOWN); + + InputEventTimeline motionMoveTimeline( + /*eventTime*/ 4, /*readTime*/ 5, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_MOVE); + + InputEventTimeline motionUpTimeline( + /*eventTime*/ 6, /*readTime*/ 7, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::MOTION_ACTION_UP); + + InputEventTimeline keyDownTimeline( + /*eventTime*/ 8, /*readTime*/ 9, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::KEY); + + InputEventTimeline keyUpTimeline( + /*eventTime*/ 10, /*readTime*/ 11, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::KEY); + + InputEventTimeline unknownTimeline( + /*eventTime*/ 12, /*readTime*/ 13, + /*vendorId*/ 0, /*productId*/ 0, + /*sources*/ {InputDeviceUsageSource::UNKNOWN}, + /*inputEventActionType*/ InputEventActionType::UNKNOWN_INPUT_EVENT); + + mTracker->trackListener(inputEventId, motionDownTimeline.eventTime, motionDownTimeline.readTime, + DEVICE_ID, motionDownTimeline.sources, AMOTION_EVENT_ACTION_DOWN, + InputEventType::MOTION); + mTracker->trackListener(inputEventId + 1, motionMoveTimeline.eventTime, + motionMoveTimeline.readTime, DEVICE_ID, motionMoveTimeline.sources, + AMOTION_EVENT_ACTION_MOVE, InputEventType::MOTION); + mTracker->trackListener(inputEventId + 2, motionUpTimeline.eventTime, motionUpTimeline.readTime, + DEVICE_ID, motionUpTimeline.sources, AMOTION_EVENT_ACTION_UP, + InputEventType::MOTION); + mTracker->trackListener(inputEventId + 3, keyDownTimeline.eventTime, keyDownTimeline.readTime, + DEVICE_ID, keyDownTimeline.sources, AKEY_EVENT_ACTION_DOWN, + InputEventType::KEY); + mTracker->trackListener(inputEventId + 4, keyUpTimeline.eventTime, keyUpTimeline.readTime, + DEVICE_ID, keyUpTimeline.sources, AKEY_EVENT_ACTION_UP, + InputEventType::KEY); + mTracker->trackListener(inputEventId + 5, unknownTimeline.eventTime, unknownTimeline.readTime, + DEVICE_ID, unknownTimeline.sources, AMOTION_EVENT_ACTION_POINTER_DOWN, + InputEventType::MOTION); + + triggerEventReporting(unknownTimeline.eventTime); + + std::vector<InputEventTimeline> expectedTimelines = {motionDownTimeline, motionMoveTimeline, + motionUpTimeline, keyDownTimeline, + keyUpTimeline, unknownTimeline}; + assertReceivedTimelines(expectedTimelines); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 18222ddb4b..411c7baf77 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -978,6 +978,36 @@ TEST_F(PointerChoreographerTest, WhenTouchDeviceIsResetClearsSpots) { assertPointerControllerRemoved(pc); } +/** + * When both "show touches" and "stylus hover icons" are enabled, if the app doesn't specify an + * icon for the hovering stylus, fall back to using the spot hover icon. + */ +TEST_F(PointerChoreographerTest, ShowTouchesOverridesUnspecifiedStylusIcon) { + mChoreographer.setShowTouchesEnabled(true); + mChoreographer.setStylusPointerIconEnabled(true); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, + DISPLAY_ID)}}); + + mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, + AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::STYLUS); + + mChoreographer.setPointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED, DISPLAY_ID, DEVICE_ID); + pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER); + + mChoreographer.setPointerIcon(PointerIconStyle::TYPE_ARROW, DISPLAY_ID, DEVICE_ID); + pc->assertPointerIconSet(PointerIconStyle::TYPE_ARROW); + + mChoreographer.setPointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED, DISPLAY_ID, DEVICE_ID); + pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER); +} + using StylusFixtureParam = std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>; diff --git a/services/inputflinger/tests/TestConstants.h b/services/inputflinger/tests/TestConstants.h index 082bbb8136..d2337dd92c 100644 --- a/services/inputflinger/tests/TestConstants.h +++ b/services/inputflinger/tests/TestConstants.h @@ -25,7 +25,10 @@ namespace android { using std::chrono_literals::operator""ms; // Timeout for waiting for an input device to be added and processed -static constexpr std::chrono::duration ADD_INPUT_DEVICE_TIMEOUT = 500ms; +static constexpr std::chrono::duration ADD_INPUT_DEVICE_TIMEOUT = 5000ms; + +// Timeout for asserting that an input device change did not occur +static constexpr std::chrono::duration INPUT_DEVICES_DIDNT_CHANGE_TIMEOUT = 100ms; // Timeout for waiting for an expected event static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms; diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index f3be04115a..6fa3365faa 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -615,7 +615,12 @@ public: explicit WithPointerIdMatcher(size_t index, int32_t pointerId) : mIndex(index), mPointerId(pointerId) {} - bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const { + bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream* os) const { + if (mIndex >= args.pointerCoords.size()) { + *os << "Pointer index " << mIndex << " is out of bounds"; + return false; + } + return args.pointerProperties[mIndex].id == mPointerId; } @@ -646,21 +651,51 @@ MATCHER_P2(WithCursorPosition, x, y, "InputEvent with specified cursor position" return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY); } -MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") { - const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX - << ", " << argY << ")"; - return argX == x && argY == y; +/// Relative motion matcher +class WithRelativeMotionMatcher { +public: + using is_gtest_matcher = void; + explicit WithRelativeMotionMatcher(size_t pointerIndex, float relX, float relY) + : mPointerIndex(pointerIndex), mRelX(relX), mRelY(relY) {} + + bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const { + if (mPointerIndex >= event.pointerCoords.size()) { + *os << "Pointer index " << mPointerIndex << " is out of bounds"; + return false; + } + + const PointerCoords& coords = event.pointerCoords[mPointerIndex]; + bool matches = mRelX == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) && + mRelY == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + if (!matches) { + *os << "expected relative motion (" << mRelX << ", " << mRelY << ") at pointer index " + << mPointerIndex << ", but got (" + << coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) << ", " + << coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y) << ")"; + } + return matches; + } + + void DescribeTo(std::ostream* os) const { + *os << "with relative motion (" << mRelX << ", " << mRelY << ") at pointer index " + << mPointerIndex; + } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong relative motion"; } + +private: + const size_t mPointerIndex; + const float mRelX; + const float mRelY; +}; + +inline WithRelativeMotionMatcher WithRelativeMotion(float relX, float relY) { + return WithRelativeMotionMatcher(0, relX, relY); } -MATCHER_P3(WithPointerRelativeMotion, pointer, x, y, - "InputEvent with specified relative motion for pointer") { - const auto argX = arg.pointerCoords[pointer].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const auto argY = arg.pointerCoords[pointer].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - *result_listener << "expected pointer " << pointer << " to have relative motion (" << x << ", " - << y << "), but got (" << argX << ", " << argY << ")"; - return argX == x && argY == y; +inline WithRelativeMotionMatcher WithPointerRelativeMotion(size_t pointerIndex, float relX, + float relY) { + return WithRelativeMotionMatcher(pointerIndex, relX, relY); } MATCHER_P3(WithGestureOffset, dx, dy, epsilon, @@ -767,10 +802,14 @@ MATCHER_P(WithToolType, toolType, "InputEvent with specified tool type") { return argToolType == toolType; } -MATCHER_P2(WithPointerToolType, pointer, toolType, +MATCHER_P2(WithPointerToolType, pointerIndex, toolType, "InputEvent with specified tool type for pointer") { - const auto argToolType = arg.pointerProperties[pointer].toolType; - *result_listener << "expected pointer " << pointer << " to have tool type " + if (std::cmp_greater_equal(pointerIndex, arg.getPointerCount())) { + *result_listener << "Pointer index " << pointerIndex << " is out of bounds"; + return false; + } + const auto argToolType = arg.pointerProperties[pointerIndex].toolType; + *result_listener << "expected pointer " << pointerIndex << " to have tool type " << ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType); return argToolType == toolType; } diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp index 3e4a19becd..5442a65f2f 100644 --- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp @@ -155,10 +155,6 @@ public: return reader->getLightPlayerId(deviceId, lightId); } - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const { - reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode); - } - int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const { return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode); } diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp index 6daeaafbb3..908fa40f05 100644 --- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -18,6 +18,7 @@ #include <linux/input.h> #include "../../InputDeviceMetricsSource.h" +#include "../InputEventTimeline.h" #include "dispatcher/LatencyTracker.h" namespace android { @@ -37,6 +38,10 @@ public: connectionTimeline.isComplete(); } }; + + void pushLatencyStatistics() override {} + + std::string dump(const char* prefix) const { return ""; }; }; static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp, @@ -52,7 +57,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { FuzzedDataProvider fdp(data, size); EmptyProcessor emptyProcessor; - LatencyTracker tracker(&emptyProcessor); + LatencyTracker tracker(emptyProcessor); // Make some pre-defined tokens to ensure that some timelines are complete. std::array<sp<IBinder> /*token*/, 10> predefinedTokens; @@ -65,14 +70,15 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { fdp.PickValueInArray<std::function<void()>>({ [&]() -> void { int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); - int32_t isDown = fdp.ConsumeBool(); nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>(); nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>(); const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>(); std::set<InputDeviceUsageSource> sources = { fdp.ConsumeEnum<InputDeviceUsageSource>()}; - tracker.trackListener(inputEventId, isDown, eventTime, readTime, deviceId, - sources); + const int32_t inputEventActionType = fdp.ConsumeIntegral<int32_t>(); + const InputEventType inputEventType = fdp.ConsumeEnum<InputEventType>(); + tracker.trackListener(inputEventId, eventTime, readTime, deviceId, sources, + inputEventActionType, inputEventType); }, [&]() -> void { int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index ddc3310448..fa8270a3d9 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -202,7 +202,8 @@ public: int32_t getSwitchState(int32_t deviceId, int32_t sw) const override { return mFdp->ConsumeIntegral<int32_t>(); } - void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {} + void setKeyRemapping(int32_t deviceId, + const std::map<int32_t, int32_t>& keyRemapping) const override {} int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override { return mFdp->ConsumeIntegral<int32_t>(); } @@ -283,6 +284,7 @@ public: void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {} void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, int32_t deviceId) override {} + void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {} std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, const std::optional<KeyboardLayoutInfo> layoutInfo) override { diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index f4b0265afb..7b2596adca 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -52,6 +52,7 @@ cc_library { "-Wall", "-Werror", "-Wextra", + "-Wthread-safety", "-fvisibility=hidden", ], diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 130c112fd2..0d00642688 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -172,7 +172,7 @@ bool SensorService::SensorEventConnection::addSensor(int32_t handle) { bool SensorService::SensorEventConnection::removeSensor(int32_t handle) { Mutex::Autolock _l(mConnectionLock); - if (mSensorInfo.erase(handle) >= 0) { + if (mSensorInfo.erase(handle) > 0) { return true; } return false; @@ -711,14 +711,17 @@ status_t SensorService::SensorEventConnection::enableDisable( if (err == OK && isSensorCapped) { if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) || !isRateCappedBasedOnPermission()) { + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs; } else { + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; } } } else { err = mService->disable(this, handle); + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup.erase(handle); } return err; @@ -750,8 +753,10 @@ status_t SensorService::SensorEventConnection::setEventRate(int handle, nsecs_t if (ret == OK && isSensorCapped) { if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) || !isRateCappedBasedOnPermission()) { + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs; } else { + Mutex::Autolock _l(mConnectionLock); mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; } } diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h index 6a98a40686..bb8733dc3a 100644 --- a/services/sensorservice/SensorEventConnection.h +++ b/services/sensorservice/SensorEventConnection.h @@ -199,7 +199,8 @@ private: // valid mapping for sensors that require a permission in order to reduce the lookup time. std::unordered_map<int32_t, int32_t> mHandleToAppOp; // Mapping of sensor handles to its rate before being capped by the mic toggle. - std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup; + std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup + GUARDED_BY(mConnectionLock); userid_t mUserId; std::optional<bool> mIsRateCappedBasedOnPermission; diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h index dc9e8215e7..a8a773ad6b 100644 --- a/services/sensorservice/SensorRegistrationInfo.h +++ b/services/sensorservice/SensorRegistrationInfo.h @@ -38,10 +38,11 @@ public: mActivated = false; } - SensorRegistrationInfo(int32_t handle, const String8 &packageName, - int64_t samplingRateNs, int64_t maxReportLatencyNs, bool activate) { + SensorRegistrationInfo(int32_t handle, const String8& packageName, int64_t samplingRateNs, + int64_t maxReportLatencyNs, bool activate, status_t registerStatus) { mSensorHandle = handle; mPackageName = packageName; + mRegisteredStatus = registerStatus; mSamplingRateUs = static_cast<int64_t>(samplingRateNs/1000); mMaxReportLatencyUs = static_cast<int64_t>(maxReportLatencyNs/1000); @@ -60,28 +61,43 @@ public: return (info.mSensorHandle == INT32_MIN && info.mRealtimeSec == 0); } - // Dumpable interface - virtual std::string dump() const override { + std::string dump(SensorService* sensorService) const { struct tm* timeinfo = localtime(&mRealtimeSec); const int8_t hour = static_cast<int8_t>(timeinfo->tm_hour); const int8_t min = static_cast<int8_t>(timeinfo->tm_min); const int8_t sec = static_cast<int8_t>(timeinfo->tm_sec); std::ostringstream ss; - ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":" - << std::setw(2) << static_cast<int>(min) << ":" - << std::setw(2) << static_cast<int>(sec) - << (mActivated ? " +" : " -") - << " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec - << std::setfill(' ') << " pid=" << std::setw(5) << mPid - << " uid=" << std::setw(5) << mUid << " package=" << mPackageName; - if (mActivated) { - ss << " samplingPeriod=" << mSamplingRateUs << "us" - << " batchingPeriod=" << mMaxReportLatencyUs << "us"; - }; + ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":" << std::setw(2) + << static_cast<int>(min) << ":" << std::setw(2) << static_cast<int>(sec) + << (mActivated ? " +" : " -") << " 0x" << std::hex << std::setw(8) << mSensorHandle; + ss << std::dec << std::setfill(' ') << " pid=" << std::setw(5) << mPid + << " uid=" << std::setw(5) << mUid; + + std::string samplingRate = + mActivated ? std::to_string(mSamplingRateUs) : " N/A "; + std::string batchingInterval = + mActivated ? std::to_string(mMaxReportLatencyUs) : " N/A "; + ss << " samplingPeriod=" << std::setfill(' ') << std::setw(8) + << samplingRate << "us" << " batchingPeriod=" << std::setfill(' ') + << std::setw(8) << batchingInterval << "us" + << " result=" << statusToString(mRegisteredStatus) + << " (sensor, package): (" << std::setfill(' ') << std::left + << std::setw(27); + + if (sensorService != nullptr) { + ss << sensorService->getSensorName(mSensorHandle); + } else { + ss << "null"; + } + ss << ", " << mPackageName << ")"; + return ss.str(); } + // Dumpable interface + virtual std::string dump() const override { return dump(static_cast<SensorService*>(nullptr)); } + /** * Dump debugging information as android.service.SensorRegistrationInfoProto protobuf message * using ProtoOutputStream. @@ -110,6 +126,7 @@ private: int64_t mMaxReportLatencyUs; bool mActivated; time_t mRealtimeSec; + status_t mRegisteredStatus; }; } // namespace android; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 3895ffe55e..060508ca25 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -708,7 +708,7 @@ status_t SensorService::dump(int fd, const Vector<String16>& args) { SENSOR_REGISTRATIONS_BUF_SIZE; continue; } - result.appendFormat("%s\n", reg_info.dump().c_str()); + result.appendFormat("%s\n", reg_info.dump(this).c_str()); currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) % SENSOR_REGISTRATIONS_BUF_SIZE; } while(startIndex != currentIndex); @@ -1583,7 +1583,11 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri // Only 4 modes supported for a SensorEventConnection ... NORMAL, DATA_INJECTION, // REPLAY_DATA_INJECTION and HAL_BYPASS_REPLAY_DATA_INJECTION if (requestedMode != NORMAL && !isInjectionMode(requestedMode)) { - return nullptr; + ALOGE( + "Failed to create sensor event connection: invalid request mode. " + "requestMode: %d", + requestedMode); + return nullptr; } resetTargetSdkVersionCache(opPackageName); @@ -1591,8 +1595,19 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri // 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 nullptr; - if (!isAllowListedPackage(packageName)) return nullptr; + if (mCurrentOperatingMode != DATA_INJECTION) { + ALOGE( + "Failed to create sensor event connection: sensor service not in " + "DI mode when creating a client in DATA_INJECTION mode"); + return nullptr; + } + if (!isAllowListedPackage(packageName)) { + ALOGE( + "Failed to create sensor event connection: package %s not in " + "allowed list for DATA_INJECTION mode", + packageName.c_str()); + return nullptr; + } } uid_t uid = IPCThreadState::self()->getCallingUid(); @@ -2152,17 +2167,17 @@ status_t SensorService::enable(const sp<SensorEventConnection>& connection, sensor->getSensor().getRequiredAppOp() >= 0) { connection->mHandleToAppOp[handle] = sensor->getSensor().getRequiredAppOp(); } - - mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) = - SensorRegistrationInfo(handle, connection->getPackageName(), - samplingPeriodNs, maxBatchReportLatencyNs, true); - mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE; } if (err != NO_ERROR) { // batch/activate has failed, reset our state. cleanupWithoutDisableLocked(connection, handle); } + + mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) = + SensorRegistrationInfo(handle, connection->getPackageName(), samplingPeriodNs, + maxBatchReportLatencyNs, /*activate=*/ true, err); + mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE; return err; } @@ -2175,13 +2190,10 @@ status_t SensorService::disable(const sp<SensorEventConnection>& connection, int if (err == NO_ERROR) { std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle); err = sensor != nullptr ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE); - - } - if (err == NO_ERROR) { - mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) = - SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, false); - mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE; } + mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) = + SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, /*activate=*/ false, err); + mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE; return err; } diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h index b0b1a02164..eb6e677117 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h @@ -33,6 +33,7 @@ #include "DisplayHardware/HWC2.h" #include <aidl/android/hardware/graphics/composer3/Composition.h> +#include <aidl/android/hardware/graphics/composer3/Lut.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" @@ -77,6 +78,7 @@ public: Error(const std::string&, bool, const std::vector<uint8_t>&)); MOCK_METHOD1(setBrightness, Error(float)); MOCK_METHOD1(setBlockingRegion, Error(const android::Region&)); + MOCK_METHOD(Error, setLuts, (std::vector<aidl::android::hardware::graphics::composer3::Lut>&)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 629d9f23ff..e910c72e2e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -51,7 +51,8 @@ public: MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t()); MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t()); MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*)); - MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId)); + MOCK_METHOD3(allocatePhysicalDisplay, + void(hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>)); MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId)); MOCK_METHOD(status_t, getDeviceCompositionChanges, @@ -151,6 +152,10 @@ public: getOverlaySupport, (), (const, override)); MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); + MOCK_METHOD(status_t, getRequestedLuts, + (PhysicalDisplayId, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*), + (override)); }; } // namespace mock diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 0eced73ff9..66237b9d8a 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -60,6 +60,7 @@ using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::Disp using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities; using AidlHdrConversionCapability = aidl::android::hardware::graphics::common::HdrConversionCapability; +using AidlHdcpLevels = aidl::android::hardware::drm::HdcpLevels; using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy; using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties; using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata; @@ -223,6 +224,12 @@ public: return ::ndk::ScopedAStatus::ok(); } + ::ndk::ScopedAStatus onHdcpLevelsChanged(int64_t in_display, + const AidlHdcpLevels& levels) override { + mCallback.onComposerHalHdcpLevelsChanged(translate<Display>(in_display), levels); + return ::ndk::ScopedAStatus::ok(); + } + private: HWC2::ComposerCallback& mCallback; }; @@ -1540,7 +1547,7 @@ Error AidlComposer::getClientTargetProperty( return error; } -Error AidlComposer::getDisplayLuts(Display display, std::vector<Lut>* outLuts) { +Error AidlComposer::getRequestedLuts(Display display, std::vector<DisplayLuts::LayerLut>* outLuts) { Error error = Error::NONE; mMutex.lock_shared(); if (auto reader = getReader(display)) { @@ -1552,6 +1559,18 @@ Error AidlComposer::getDisplayLuts(Display display, std::vector<Lut>* outLuts) { return error; } +Error AidlComposer::setLayerLuts(Display display, Layer layer, std::vector<Lut>& luts) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerLuts(translate<int64_t>(display), translate<int64_t>(layer), luts); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + Error AidlComposer::setLayerBrightness(Display display, Layer layer, float brightness) { Error error = Error::NONE; mMutex.lock_shared(); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 3669d4c996..246223a668 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -244,9 +244,13 @@ public: Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime, int32_t frameIntervalNs) override; - Error getDisplayLuts( + Error getRequestedLuts( Display display, - std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) override; + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* + outLuts) override; + Error setLayerLuts( + Display display, Layer layer, + std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 888dc08134..7db9a94e54 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -40,6 +40,7 @@ #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h> +#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h> #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h> #include <aidl/android/hardware/graphics/composer3/Lut.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> @@ -304,7 +305,9 @@ public: virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0; virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime, int32_t frameIntervalNs) = 0; - virtual Error getDisplayLuts(Display display, std::vector<V3_0::Lut>* outLuts) = 0; + virtual Error getRequestedLuts(Display display, + std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0; + virtual Error setLayerLuts(Display display, Layer layer, std::vector<V3_0::Lut>& luts) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index d5f65c6211..f1fa9389eb 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -41,6 +41,7 @@ using aidl::android::hardware::graphics::composer3::Color; using aidl::android::hardware::graphics::composer3::Composition; using AidlCapability = aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::DisplayLuts; using aidl::android::hardware::graphics::composer3::Lut; using aidl::android::hardware::graphics::composer3::OverlayProperties; @@ -608,13 +609,14 @@ Error Display::getClientTargetProperty( return static_cast<Error>(error); } -Error Display::getDisplayLuts(std::vector<Lut>* outLuts) { - std::vector<Lut> tmpLuts; - const auto error = mComposer.getDisplayLuts(mId, &tmpLuts); - for (Lut& lut : tmpLuts) { - if (lut.pfd.get() >= 0) { - outLuts->push_back( - {lut.layer, ndk::ScopedFileDescriptor(lut.pfd.release()), lut.lutProperties}); +Error Display::getRequestedLuts(std::vector<DisplayLuts::LayerLut>* outLayerLuts) { + std::vector<DisplayLuts::LayerLut> tmpLayerLuts; + const auto error = mComposer.getRequestedLuts(mId, &tmpLayerLuts); + for (DisplayLuts::LayerLut& layerLut : tmpLayerLuts) { + if (layerLut.lut.pfd.get() >= 0) { + outLayerLuts->push_back({layerLut.layer, + Lut{ndk::ScopedFileDescriptor(layerLut.lut.pfd.release()), + layerLut.lut.lutProperties}}); } } return static_cast<Error>(error); @@ -673,6 +675,11 @@ void Display::loadDisplayCapabilities() { } }); } + +void Display::setPhysicalSizeInMm(std::optional<ui::Size> size) { + mPhysicalSize = size; +} + } // namespace impl // Layer methods @@ -1050,6 +1057,14 @@ Error Layer::setBlockingRegion(const Region& region) { return static_cast<Error>(intError); } +Error Layer::setLuts(std::vector<Lut>& luts) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + const auto intError = mComposer.setLayerLuts(mDisplay->getId(), mId, luts); + return static_cast<Error>(intError); +} + } // namespace impl } // namespace HWC2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index be2059a935..8e2aeaf234 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -67,6 +67,7 @@ class Layer; namespace hal = android::hardware::graphics::composer::hal; +using aidl::android::hardware::drm::HdcpLevels; using aidl::android::hardware::graphics::common::DisplayHotplugEvent; using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; @@ -85,6 +86,7 @@ struct ComposerCallback { virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0; virtual void onComposerHalVsyncIdle(hal::HWDisplayId) = 0; virtual void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) = 0; + virtual void onComposerHalHdcpLevelsChanged(hal::HWDisplayId, const HdcpLevels& levels) = 0; protected: ~ComposerCallback() = default; @@ -103,6 +105,7 @@ public: virtual bool isVsyncPeriodSwitchSupported() const = 0; virtual bool hasDisplayIdleTimerCapability() const = 0; virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0; + virtual std::optional<ui::Size> getPhysicalSizeInMm() const = 0; [[nodiscard]] virtual hal::Error acceptChanges() = 0; [[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> @@ -179,8 +182,9 @@ public: [[nodiscard]] virtual hal::Error getClientTargetProperty( aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness* outClientTargetProperty) = 0; - [[nodiscard]] virtual hal::Error getDisplayLuts( - std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) = 0; + [[nodiscard]] virtual hal::Error getRequestedLuts( + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* + outLuts) = 0; [[nodiscard]] virtual hal::Error getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) = 0; @@ -264,8 +268,9 @@ public: hal::Error getClientTargetProperty( aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness* outClientTargetProperty) override; - hal::Error getDisplayLuts( - std::vector<aidl::android::hardware::graphics::composer3::Lut>* outLuts) override; + hal::Error getRequestedLuts( + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* + outLuts) override; hal::Error getDisplayDecorationSupport( std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) override; @@ -281,6 +286,8 @@ public: bool hasDisplayIdleTimerCapability() const override; void onLayerDestroyed(hal::HWLayerId layerId) override; hal::Error getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const override; + void setPhysicalSizeInMm(std::optional<ui::Size> size); + std::optional<ui::Size> getPhysicalSizeInMm() const override { return mPhysicalSize; } private: void loadDisplayCapabilities(); @@ -314,6 +321,8 @@ private: std::optional< std::unordered_set<aidl::android::hardware::graphics::composer3::DisplayCapability>> mDisplayCapabilities GUARDED_BY(mDisplayCapabilitiesMutex); + // Physical size in mm. + std::optional<ui::Size> mPhysicalSize; }; } // namespace impl @@ -359,6 +368,8 @@ public: // AIDL HAL [[nodiscard]] virtual hal::Error setBrightness(float brightness) = 0; [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0; + [[nodiscard]] virtual hal::Error setLuts( + std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) = 0; }; namespace impl { @@ -409,6 +420,8 @@ public: // AIDL HAL hal::Error setBrightness(float brightness) override; hal::Error setBlockingRegion(const android::Region& region) override; + hal::Error setLuts( + std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override; private: // These are references to data owned by HWComposer, which will outlive diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 73fa855c3c..d08e261e6c 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -76,6 +76,7 @@ using aidl::android::hardware::graphics::common::HdrConversionCapability; using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::DisplayConfiguration; using namespace std::string_literals; namespace android { @@ -222,8 +223,8 @@ bool HWComposer::allocateVirtualDisplay(HalVirtualDisplayId displayId, ui::Size return true; } -void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, - PhysicalDisplayId displayId) { +void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, PhysicalDisplayId displayId, + std::optional<ui::Size> physicalSize) { mPhysicalDisplayIdMap[hwcDisplayId] = displayId; if (!mPrimaryHwcDisplayId) { @@ -235,6 +236,7 @@ void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId, hal::DisplayType::PHYSICAL); newDisplay->setConnected(true); + newDisplay->setPhysicalSizeInMm(physicalSize); displayData.hwcDisplay = std::move(newDisplay); } @@ -276,6 +278,47 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModes(PhysicalDisplayId d return getModesFromLegacyDisplayConfigs(hwcDisplayId); } +DisplayConfiguration::Dpi HWComposer::getEstimatedDotsPerInchFromSize( + uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const { + if (!FlagManager::getInstance().correct_dpi_with_display_size()) { + return {-1, -1}; + } + if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) { + if (const auto it = mDisplayData.find(displayId.value()); + it != mDisplayData.end() && it->second.hwcDisplay->getPhysicalSizeInMm()) { + ui::Size size = it->second.hwcDisplay->getPhysicalSizeInMm().value(); + if (hwcMode.width > 0 && hwcMode.height > 0 && size.width > 0 && size.height > 0) { + static constexpr float kMmPerInch = 25.4f; + return {hwcMode.width * kMmPerInch / size.width, + hwcMode.height * kMmPerInch / size.height}; + } + } + } + return {-1, -1}; +} + +DisplayConfiguration::Dpi HWComposer::correctedDpiIfneeded( + DisplayConfiguration::Dpi dpi, DisplayConfiguration::Dpi estimatedDpi) const { + // hwc can be unreliable when it comes to dpi. A rough estimated dpi may yield better + // results. For instance, libdrm and bad edid may result in a dpi of {350, 290} for a + // 16:9 3840x2160 display, which would match a 4:3 aspect ratio. + // The logic here checks if hwc was able to provide some dpi, and if so if the dpi + // disparity between the axes is more reasonable than a rough estimate, otherwise use + // the estimated dpi as a corrected value. + if (estimatedDpi.x == -1 || estimatedDpi.y == -1) { + return dpi; + } + if (dpi.x == -1 || dpi.y == -1) { + return estimatedDpi; + } + if (std::min(dpi.x, dpi.y) != 0 && std::min(estimatedDpi.x, estimatedDpi.y) != 0 && + abs(dpi.x - dpi.y) / std::min(dpi.x, dpi.y) > + abs(estimatedDpi.x - estimatedDpi.y) / std::min(estimatedDpi.x, estimatedDpi.y)) { + return estimatedDpi; + } + return dpi; +} + std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigurations( uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const { std::vector<hal::DisplayConfiguration> configs; @@ -294,9 +337,16 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigura .configGroup = config.configGroup, .vrrConfig = config.vrrConfig}; + const DisplayConfiguration::Dpi estimatedDPI = + getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode); if (config.dpi) { - hwcMode.dpiX = config.dpi->x; - hwcMode.dpiY = config.dpi->y; + const DisplayConfiguration::Dpi dpi = + correctedDpiIfneeded(config.dpi.value(), estimatedDPI); + hwcMode.dpiX = dpi.x; + hwcMode.dpiY = dpi.y; + } else if (estimatedDPI.x != -1 && estimatedDPI.y != -1) { + hwcMode.dpiX = estimatedDPI.x; + hwcMode.dpiY = estimatedDPI.y; } if (!mEnableVrrTimeout) { @@ -328,12 +378,14 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayCon const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X); const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y); - if (dpiX != -1) { - hwcMode.dpiX = static_cast<float>(dpiX) / 1000.f; - } - if (dpiY != -1) { - hwcMode.dpiY = static_cast<float>(dpiY) / 1000.f; - } + const DisplayConfiguration::Dpi hwcDpi = + DisplayConfiguration::Dpi{dpiX == -1 ? dpiY : dpiX / 1000.f, + dpiY == -1 ? dpiY : dpiY / 1000.f}; + const DisplayConfiguration::Dpi estimatedDPI = + getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode); + const DisplayConfiguration::Dpi dpi = correctedDpiIfneeded(hwcDpi, estimatedDPI); + hwcMode.dpiX = dpi.x; + hwcMode.dpiY = dpi.y; modes.push_back(hwcMode); } @@ -533,6 +585,7 @@ status_t HWComposer::getDeviceCompositionChanges( DeviceRequestedChanges::ClientTargetProperty clientTargetProperty; error = hwcDisplay->getClientTargetProperty(&clientTargetProperty); + RETURN_IF_HWC_ERROR_FOR("getClientTargetProperty", error, displayId, BAD_INDEX); outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests), std::move(layerRequests), @@ -925,6 +978,21 @@ status_t HWComposer::getDisplayDecorationSupport( return NO_ERROR; } +status_t HWComposer::getRequestedLuts( + PhysicalDisplayId displayId, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* outLuts) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = mDisplayData[displayId].hwcDisplay->getRequestedLuts(outLuts); + if (error == hal::Error::UNSUPPORTED) { + RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); + } + if (error == hal::Error::BAD_PARAMETER) { + RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); + } + RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); + return NO_ERROR; +} + status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on); @@ -1056,6 +1124,8 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( getDisplayIdentificationData(hwcDisplayId, &port, &data); if (auto newInfo = parseDisplayIdentificationData(port, data)) { info->deviceProductInfo = std::move(newInfo->deviceProductInfo); + info->preferredDetailedTimingDescriptor = + std::move(newInfo->preferredDetailedTimingDescriptor); } else { ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId); } @@ -1098,7 +1168,11 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( } if (!isConnected(info->id)) { - allocatePhysicalDisplay(hwcDisplayId, info->id); + std::optional<ui::Size> size = std::nullopt; + if (info->preferredDetailedTimingDescriptor) { + size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; + } + allocatePhysicalDisplay(hwcDisplayId, info->id, size); } return info; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 9368b7b6dd..b95c619f75 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -52,6 +52,7 @@ #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> +#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> namespace android { @@ -133,7 +134,8 @@ public: // supported by the HWC can be queried in advance, but allocation may fail for other reasons. virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0; - virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0; + virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId, + std::optional<ui::Size> physicalSize) = 0; // Attempts to create a new layer on this display virtual std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) = 0; @@ -309,6 +311,11 @@ public: virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) = 0; + + // Composer 4.0 + virtual status_t getRequestedLuts( + PhysicalDisplayId, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -343,7 +350,8 @@ public: bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override; // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated. - void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override; + void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId, + std::optional<ui::Size> physicalSize) override; // Attempts to create a new layer on this display std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) override; @@ -472,6 +480,12 @@ public: status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) override; + // Composer 4.0 + status_t getRequestedLuts( + PhysicalDisplayId, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) + override; + // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; void dumpOverlayProperties(std::string& out) const override; @@ -520,6 +534,13 @@ private: std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId); bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const; + aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi + getEstimatedDotsPerInchFromSize(uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const; + + aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi correctedDpiIfneeded( + aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi dpi, + aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi estimatedDpi) + const; std::vector<HWCDisplayMode> getModesFromDisplayConfigurations(uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const; std::vector<HWCDisplayMode> getModesFromLegacyDisplayConfigs(uint64_t hwcDisplayId) const; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index ec2a3ec024..ee1e07ae7d 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -46,6 +46,7 @@ using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; using aidl::android::hardware::graphics::composer3::DimmingStage; using aidl::android::hardware::graphics::composer3::DisplayCapability; +using aidl::android::hardware::graphics::composer3::DisplayLuts; using aidl::android::hardware::graphics::composer3::Lut; using aidl::android::hardware::graphics::composer3::OverlayProperties; @@ -1409,7 +1410,11 @@ Error HidlComposer::getClientTargetProperty( return Error::NONE; } -Error HidlComposer::getDisplayLuts(Display, std::vector<Lut>*) { +Error HidlComposer::getRequestedLuts(Display, std::vector<DisplayLuts::LayerLut>*) { + return Error::NONE; +} + +Error HidlComposer::setLayerLuts(Display, Layer, std::vector<Lut>&) { return Error::NONE; } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index 8bca5ad31f..701a54bc66 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -351,8 +351,12 @@ public: Hdr*) override; Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; Error notifyExpectedPresent(Display, nsecs_t, int32_t) override; - Error getDisplayLuts(Display, - std::vector<aidl::android::hardware::graphics::composer3::Lut>*) override; + Error getRequestedLuts( + Display, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) + override; + Error setLayerLuts(Display, Layer, + std::vector<aidl::android::hardware::graphics::composer3::Lut>&) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 3736abc79f..47b811b721 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -38,6 +38,8 @@ using base::StringAppendF; using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent; using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource; +namespace { + void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals, const std::string& indent, PredictionState predictionState, nsecs_t baseTime) { StringAppendF(&result, "%s", indent.c_str()); @@ -319,6 +321,16 @@ nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions, return minTime; } +bool shouldTraceForDataSource(const FrameTimelineDataSource::TraceContext& ctx, nsecs_t timestamp) { + if (auto ds = ctx.GetDataSourceLocked(); ds && ds->getStartTime() > timestamp) { + return false; + } + + return true; +} + +} // namespace + int64_t TraceCookieCounter::getCookieForTracing() { return ++mTraceCookie; } @@ -726,15 +738,24 @@ void SurfaceFrame::onCommitNotComposited(Fps refreshRate, Fps displayFrameRender classifyJankLocked(JankType::None, refreshRate, displayFrameRenderRate, nullptr); } -void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const { +void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Expected timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + const auto timestamp = mPredictions.startTime; + if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime + monoBootOffset)); + packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start(); @@ -748,42 +769,54 @@ void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootO expectedSurfaceFrameStartEvent->set_layer_name(mDebugName); }); - // Expected timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - std::scoped_lock lock(mMutex); - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset)); + if (traced) { + // Expected timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + std::scoped_lock lock(mMutex); + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset)); - auto* event = packet->set_frame_timeline_event(); - auto* expectedSurfaceFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* expectedSurfaceFrameEndEvent = event->set_frame_end(); - expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie); - }); + expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie); + }); + } } -void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const { +void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + const auto timestamp = [&]() { + std::scoped_lock lock(mMutex); + // Actual start time is not yet available, so use expected start instead + if (mPredictionState == PredictionState::Expired) { + // If prediction is expired, we can't use the predicted start time. Instead, just + // use a start time a little earlier than the end time so that we have some info + // about this frame in the trace. + nsecs_t endTime = + (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime); + return endTime - kPredictionExpiredStartTimeDelta; + } + + return mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime; + }(); + + if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - // Actual start time is not yet available, so use expected start instead - if (mPredictionState == PredictionState::Expired) { - // If prediction is expired, we can't use the predicted start time. Instead, just use a - // start time a little earlier than the end time so that we have some info about this - // frame in the trace. - nsecs_t endTime = - (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime); - const auto timestamp = endTime - kPredictionExpiredStartTimeDelta; - packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); - } else { - const auto timestamp = - mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime; - packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); - } + packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start(); @@ -812,28 +845,31 @@ void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffse actualSurfaceFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); - // Actual timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - std::scoped_lock lock(mMutex); - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - if (mPresentState == PresentState::Dropped) { - packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset)); - } else { - packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset)); - } + if (traced) { + // Actual timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + std::scoped_lock lock(mMutex); + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + if (mPresentState == PresentState::Dropped) { + packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset)); + } else { + packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset)); + } - auto* event = packet->set_frame_timeline_event(); - auto* actualSurfaceFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* actualSurfaceFrameEndEvent = event->set_frame_end(); - actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie); - }); + actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie); + }); + } } /** * TODO(b/178637512): add inputEventId to the perfetto trace. */ -void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const { +void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID || displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) { // No packets can be traced with a missing token. @@ -842,9 +878,9 @@ void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset) cons if (getPredictionState() != PredictionState::Expired) { // Expired predictions have zeroed timestamps. This cannot be used in any meaningful way in // a trace. - tracePredictions(displayFrameToken, monoBootOffset); + tracePredictions(displayFrameToken, monoBootOffset, filterFramesBeforeTraceStarts); } - traceActuals(displayFrameToken, monoBootOffset); + traceActuals(displayFrameToken, monoBootOffset, filterFramesBeforeTraceStarts); } namespace impl { @@ -870,8 +906,12 @@ std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) } FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, - JankClassificationThresholds thresholds, bool useBootTimeClock) + JankClassificationThresholds thresholds, bool useBootTimeClock, + bool filterFramesBeforeTraceStarts) : mUseBootTimeClock(useBootTimeClock), + mFilterFramesBeforeTraceStarts( + FlagManager::getInstance().filter_frames_before_trace_starts() && + filterFramesBeforeTraceStarts), mMaxDisplayFrames(kDefaultMaxDisplayFrames), mTimeStats(std::move(timeStats)), mSurfaceFlingerPid(surfaceFlingerPid), @@ -1154,16 +1194,23 @@ void FrameTimeline::DisplayFrame::onCommitNotComposited() { } } -void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, - nsecs_t monoBootOffset) const { +void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Expected timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + const auto timestamp = mSurfaceFlingerPredictions.startTime; + if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp( - static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime + monoBootOffset)); + packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start(); @@ -1174,22 +1221,25 @@ void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid); }); - // Expected timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp( - static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset)); + if (traced) { + // Expected timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset)); - auto* event = packet->set_frame_timeline_event(); - auto* expectedDisplayFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* expectedDisplayFrameEndEvent = event->set_frame_end(); - expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie); - }); + expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie); + }); + } } void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousPredictionPresentTime) const { + nsecs_t previousPredictionPresentTime, + bool filterFramesBeforeTraceStarts) const { nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0; const constexpr float kThresh = 0.5f; const constexpr float kRange = 1.5f; @@ -1205,7 +1255,7 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs (static_cast<float>(previousPredictionPresentTime) + kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) && // sf skipped frame is not considered if app is self janked - !surfaceFrame->isSelfJanky()) { + surfaceFrame->getJankType() != JankType::None && !surfaceFrame->isSelfJanky()) { skippedFrameStartTime = surfaceFrame->getPredictions().endTime; skippedFramePresentTime = surfaceFrame->getPredictions().presentTime; break; @@ -1215,9 +1265,17 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs // add slice if (skippedFrameStartTime != 0 && skippedFramePresentTime != 0) { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + if (filterFramesBeforeTraceStarts && + !shouldTraceForDataSource(ctx, skippedFrameStartTime)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); packet->set_timestamp(static_cast<uint64_t>(skippedFrameStartTime + monoBootOffset)); @@ -1238,30 +1296,40 @@ void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs actualDisplayFrameStartEvent->set_jank_severity_type(toProto(JankSeverityType::None)); }); - // Actual timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp(static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset)); + if (traced) { + // Actual timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset)); - auto* event = packet->set_frame_timeline_event(); - auto* actualDisplayFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* actualDisplayFrameEndEvent = event->set_frame_end(); - actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); - }); + actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); + }); + } } } -void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, - nsecs_t monoBootOffset) const { +void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + bool traced = false; // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + const auto timestamp = mSurfaceFlingerActuals.startTime; + if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) { + // Do not trace packets started before tracing starts. + return; + } + traced = true; + auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp( - static_cast<uint64_t>(mSurfaceFlingerActuals.startTime + monoBootOffset)); + packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset)); auto* event = packet->set_frame_timeline_event(); auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start(); @@ -1280,22 +1348,25 @@ void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, actualDisplayFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType)); }); - // Actual timeline end - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); - packet->set_timestamp( - static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset)); + if (traced) { + // Actual timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME); + packet->set_timestamp( + static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset)); - auto* event = packet->set_frame_timeline_event(); - auto* actualDisplayFrameEndEvent = event->set_frame_end(); + auto* event = packet->set_frame_timeline_event(); + auto* actualDisplayFrameEndEvent = event->set_frame_end(); - actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); - }); + actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); + }); + } } nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousPredictionPresentTime) const { + nsecs_t previousPredictionPresentTime, + bool filterFramesBeforeTraceStarts) const { if (mSurfaceFrames.empty()) { // We don't want to trace display frames without any surface frames updates as this cannot // be janky @@ -1311,16 +1382,17 @@ nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t mono if (mPredictionState == PredictionState::Valid) { // Expired and unknown predictions have zeroed timestamps. This cannot be used in any // meaningful way in a trace. - tracePredictions(surfaceFlingerPid, monoBootOffset); + tracePredictions(surfaceFlingerPid, monoBootOffset, filterFramesBeforeTraceStarts); } - traceActuals(surfaceFlingerPid, monoBootOffset); + traceActuals(surfaceFlingerPid, monoBootOffset, filterFramesBeforeTraceStarts); for (auto& surfaceFrame : mSurfaceFrames) { - surfaceFrame->trace(mToken, monoBootOffset); + surfaceFrame->trace(mToken, monoBootOffset, filterFramesBeforeTraceStarts); } if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) { - addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime); + addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime, + filterFramesBeforeTraceStarts); } return mSurfaceFlingerPredictions.presentTime; } @@ -1414,8 +1486,9 @@ void FrameTimeline::flushPendingPresentFences() { const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); - mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, - mPreviousPredictionPresentTime); + mPreviousPredictionPresentTime = + displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, + mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); mPendingPresentFences.erase(mPendingPresentFences.begin()); } @@ -1431,8 +1504,9 @@ void FrameTimeline::flushPendingPresentFences() { auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); - mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, - mPreviousPredictionPresentTime); + mPreviousPredictionPresentTime = + displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, + mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); mPreviousActualPresentTime = signalTime; mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 94cfcb40b9..cffb61ee10 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -214,7 +214,8 @@ public: // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding // DisplayFrame at the trace processor side. monoBootOffset is the difference // between SYSTEM_TIME_BOOTTIME and SYSTEM_TIME_MONOTONIC. - void trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const; + void trace(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; // Getter functions used only by FrameTimelineTests and SurfaceFrame internally TimelineItem getActuals() const; @@ -234,8 +235,10 @@ public: std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); private: - void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const; - void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const; + void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; + void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate, Fps displayFrameRenderRate, nsecs_t* outDeadlineDelta) REQUIRES(mMutex); @@ -367,9 +370,15 @@ private: class FrameTimeline : public android::frametimeline::FrameTimeline { public: class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> { - void OnSetup(const SetupArgs&) override{}; - void OnStart(const StartArgs&) override{}; - void OnStop(const StopArgs&) override{}; + public: + nsecs_t getStartTime() const { return mTraceStartTime; } + + private: + void OnSetup(const SetupArgs&) override {}; + void OnStart(const StartArgs&) override { mTraceStartTime = systemTime(); }; + void OnStop(const StopArgs&) override {}; + + nsecs_t mTraceStartTime = 0; }; /* @@ -390,7 +399,8 @@ public: // is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME // and SYSTEM_TIME_MONOTONIC. nsecs_t trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousPredictionPresentTime) const; + nsecs_t previousPredictionPresentTime, + bool filterFramesBeforeTraceStarts) const; // Sets the token, vsyncPeriod, predictions and SF start time. void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate, std::optional<TimelineItem> predictions, nsecs_t wakeUpTime); @@ -424,10 +434,13 @@ public: private: void dump(std::string& result, nsecs_t baseTime) const; - void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const; - void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const; + void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; + void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, + bool filterFramesBeforeTraceStarts) const; void addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset, - nsecs_t previousActualPresentTime) const; + nsecs_t previousActualPresentTime, + bool filterFramesBeforeTraceStarts) const; void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync, nsecs_t previousPresentTime); @@ -471,7 +484,8 @@ public: }; FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, - JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true); + JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true, + bool filterFramesBeforeTraceStarts = true); ~FrameTimeline() = default; frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } @@ -516,6 +530,7 @@ private: TraceCookieCounter mTraceCookieCounter; mutable std::mutex mMutex; const bool mUseBootTimeClock; + const bool mFilterFramesBeforeTraceStarts; uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; const pid_t mSurfaceFlingerPid; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index ac15b92175..ee605b7a3e 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -279,24 +279,6 @@ void updateVisibility(LayerSnapshot& snapshot, bool visible) { snapshot.getDebugString().c_str()); } -bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) { - if (requested.potentialCursor) { - return false; - } - - if (snapshot.inputInfo.token != nullptr) { - return true; - } - - if (snapshot.hasBufferOrSidebandStream()) { - return true; - } - - return requested.windowInfoHandle && - requested.windowInfoHandle->getInfo()->inputConfig.test( - gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); -} - void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested, const LayerSnapshotBuilder::Args& args) { snapshot.metadata.clear(); @@ -1162,7 +1144,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, } updateVisibility(snapshot, snapshot.isVisible); - if (!needsInputInfo(snapshot, requested)) { + if (!requested.needsInputInfo()) { return; } @@ -1172,7 +1154,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, bool noValidDisplay = !displayInfoOpt.has_value(); auto displayInfo = displayInfoOpt.value_or(sDefaultInfo); - if (!requested.windowInfoHandle) { + if (!requested.hasInputInfo()) { snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL; } fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot); diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 17d2610d7a..5734ccf38f 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -62,6 +62,8 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) metadata.merge(args.metadata); changes |= RequestedLayerState::Changes::Metadata; handleAlive = true; + // TODO: b/305254099 remove once we don't pass invisible windows to input + windowInfoHandle = nullptr; if (parentId != UNASSIGNED_LAYER_ID) { canBeRoot = false; } @@ -553,6 +555,24 @@ bool RequestedLayerState::hasInputInfo() const { windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); } +bool RequestedLayerState::needsInputInfo() const { + if (potentialCursor) { + return false; + } + + if ((sidebandStream != nullptr) || (externalTexture != nullptr)) { + return true; + } + + if (!windowInfoHandle) { + return false; + } + + const auto windowInfo = windowInfoHandle->getInfo(); + return windowInfo->token != nullptr || + windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); +} + bool RequestedLayerState::hasBlur() const { return backgroundBlurRadius > 0 || blurRegions.size() > 0; } diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 48b9640486..1d96dff336 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -87,6 +87,7 @@ struct RequestedLayerState : layer_state_t { aidl::android::hardware::graphics::composer3::Composition getCompositionType() const; bool hasValidRelativeParent() const; bool hasInputInfo() const; + bool needsInputInfo() const; bool hasBlur() const; bool hasFrameUpdate() const; bool hasReadyFrame() const; diff --git a/services/surfaceflinger/FrontEnd/Update.h b/services/surfaceflinger/FrontEnd/Update.h index e5cca8fa95..4af27ab84d 100644 --- a/services/surfaceflinger/FrontEnd/Update.h +++ b/services/surfaceflinger/FrontEnd/Update.h @@ -22,28 +22,13 @@ #include "RequestedLayerState.h" #include "TransactionState.h" -namespace android { -struct LayerCreatedState { - LayerCreatedState(const wp<Layer>& layer, const wp<Layer>& parent, bool addToRoot) - : layer(layer), initialParent(parent), addToRoot(addToRoot) {} - wp<Layer> layer; - // Indicates the initial parent of the created layer, only used for creating layer in - // SurfaceFlinger. If nullptr, it may add the created layer into the current root layers. - wp<Layer> initialParent; - // Indicates whether the layer getting created should be added at root if there's no parent - // and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will - // be added offscreen. - bool addToRoot; -}; -} // namespace android - namespace android::surfaceflinger::frontend { // Atomic set of changes affecting layer state. These changes are queued in binder threads and // applied every vsync. struct Update { std::vector<TransactionState> transactions; - std::vector<LayerCreatedState> layerCreatedStates; + std::vector<sp<Layer>> legacyLayers; std::vector<std::unique_ptr<frontend::RequestedLayerState>> newLayers; std::vector<LayerCreationArgs> layerCreationArgs; std::vector<std::pair<uint32_t, std::string /* debugName */>> destroyedHandles; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 636f7bdabf..dcb0812b67 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -60,9 +60,7 @@ #include <utils/StopWatch.h> #include <algorithm> -#include <mutex> #include <optional> -#include <sstream> #include "DisplayDevice.h" #include "DisplayHardware/HWComposer.h" @@ -72,7 +70,6 @@ #include "FrontEnd/LayerHandle.h" #include "Layer.h" #include "LayerProtoHelper.h" -#include "MutexUtils.h" #include "SurfaceFlinger.h" #include "TimeStats/TimeStats.h" #include "TransactionCallbackInvoker.h" @@ -89,18 +86,6 @@ constexpr int kDumpTableRowLength = 159; const ui::Transform kIdentityTransform; -ui::LogicalDisplayId toLogicalDisplayId(const ui::LayerStack& layerStack) { - return ui::LogicalDisplayId{static_cast<int32_t>(layerStack.id)}; -} - -bool assignTransform(ui::Transform* dst, ui::Transform& from) { - if (*dst == from) { - return false; - } - *dst = from; - return true; -} - TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) { using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility; using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness; @@ -149,24 +134,11 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) : sequence(args.sequence), mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)), mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)), - mClientRef(args.client), mWindowType(static_cast<WindowInfo::Type>( - args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))), - mLayerCreationFlags(args.flags), - mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName, this)) { + args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) { ALOGV("Creating Layer %s", getDebugName()); - uint32_t layerFlags = 0; - 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; - if (args.flags & ISurfaceComposerClient::eSkipScreenshot) - layerFlags |= layer_state_t::eLayerSkipScreenshot; - mDrawingState.flags = layerFlags; mDrawingState.crop.makeInvalid(); - mDrawingState.z = 0; - mDrawingState.color.a = 1.0f; - mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK; mDrawingState.sequence = 0; mDrawingState.transform.set(0, 0); mDrawingState.frameNumber = 0; @@ -179,33 +151,9 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mDrawingState.acquireFence = sp<Fence>::make(-1); mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence); mDrawingState.dataspace = ui::Dataspace::V0_SRGB; - mDrawingState.hdrMetadata.validTypes = 0; - mDrawingState.surfaceDamageRegion = Region::INVALID_REGION; - mDrawingState.cornerRadius = 0.0f; - mDrawingState.backgroundBlurRadius = 0; - mDrawingState.api = -1; - mDrawingState.hasColorTransform = false; - mDrawingState.colorSpaceAgnostic = false; - mDrawingState.frameRateSelectionPriority = PRIORITY_UNSET; mDrawingState.metadata = args.metadata; - mDrawingState.shadowRadius = 0.f; - mDrawingState.fixedTransformHint = ui::Transform::ROT_INVALID; mDrawingState.frameTimelineInfo = {}; mDrawingState.postTime = -1; - mDrawingState.destinationFrame.makeInvalid(); - mDrawingState.isTrustedOverlay = false; - mDrawingState.dropInputMode = gui::DropInputMode::NONE; - mDrawingState.dimmingEnabled = true; - mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default; - mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Propagate; - - if (args.flags & ISurfaceComposerClient::eNoColorFill) { - // Set an invalid color so there is no color fill. - mDrawingState.color.r = -1.0_hf; - mDrawingState.color.g = -1.0_hf; - mDrawingState.color.b = -1.0_hf; - } - mFrameTracker.setDisplayRefreshPeriod( args.flinger->mScheduler->getPacesetterVsyncPeriod().ns()); @@ -213,14 +161,9 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mOwnerPid = args.ownerPid; mOwnerAppId = mOwnerUid % PER_USER_RANGE; - mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied); mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow; - mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp; - - mSnapshot->sequence = sequence; - mSnapshot->name = getDebugName(); - mSnapshot->premultipliedAlpha = mPremultipliedAlpha; - mSnapshot->parentTransform = {}; + mLayerFEs.emplace_back(frontend::LayerHierarchy::TraversalPath{static_cast<uint32_t>(sequence)}, + args.flinger->getFactory().createLayerFE(mName, this)); } void Layer::onFirstRef() { @@ -231,10 +174,6 @@ Layer::~Layer() { LOG_ALWAYS_FATAL_IF(std::this_thread::get_id() != mFlinger->mMainThreadId, "Layer destructor called off the main thread."); - // The original layer and the clone layer share the same texture and buffer. Therefore, only - // one of the layers, in this case the original layer, needs to handle the deletion. The - // original layer and the clone should be removed at the same time so there shouldn't be any - // issue with the clone layer trying to use the texture. if (mBufferInfo.mBuffer != nullptr) { callReleaseBufferCallback(mDrawingState.releaseBufferListener, mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber, @@ -250,10 +189,6 @@ Layer::~Layer() { if (mDrawingState.sidebandStream != nullptr) { mFlinger->mTunnelModeEnabledReporter->decrementTunnelModeCount(); } - if (mHadClonedChild) { - auto& roots = mFlinger->mLayerMirrorRoots; - roots.erase(std::remove(roots.begin(), roots.end(), this), roots.end()); - } if (hasTrustedPresentationListener()) { mFlinger->mNumTrustedPresentationListeners--; updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/); @@ -261,77 +196,8 @@ Layer::~Layer() { } // --------------------------------------------------------------------------- -// callbacks -// --------------------------------------------------------------------------- - -void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) { - if (mDrawingState.zOrderRelativeOf == nullptr) { - return; - } - - sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote(); - if (strongRelative == nullptr) { - setZOrderRelativeOf(nullptr); - return; - } - - if (!std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) { - strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this)); - mFlinger->setTransactionFlags(eTraversalNeeded); - setZOrderRelativeOf(nullptr); - } -} - -void Layer::removeFromCurrentState() { - if (!mRemovedFromDrawingState) { - mRemovedFromDrawingState = true; - mFlinger->mScheduler->deregisterLayer(this); - } - updateTrustedPresentationState(nullptr, nullptr, -1 /* time_in_ms */, true /* leaveState*/); - - mFlinger->markLayerPendingRemovalLocked(sp<Layer>::fromExisting(this)); -} - -sp<Layer> Layer::getRootLayer() { - sp<Layer> parent = getParent(); - if (parent == nullptr) { - return sp<Layer>::fromExisting(this); - } - return parent->getRootLayer(); -} - -void Layer::onRemovedFromCurrentState() { - // Use the root layer since we want to maintain the hierarchy for the entire subtree. - auto layersInTree = getRootLayer()->getLayersInTree(LayerVector::StateSet::Current); - std::sort(layersInTree.begin(), layersInTree.end()); - - REQUIRE_MUTEX(mFlinger->mStateLock); - traverse(LayerVector::StateSet::Current, - [&](Layer* layer) REQUIRES(layer->mFlinger->mStateLock) { - layer->removeFromCurrentState(); - layer->removeRelativeZ(layersInTree); - }); -} - -void Layer::addToCurrentState() { - if (mRemovedFromDrawingState) { - mRemovedFromDrawingState = false; - mFlinger->mScheduler->registerLayer(this, FrameRateCompatibility::Default); - } - - for (const auto& child : mCurrentChildren) { - child->addToCurrentState(); - } -} - -// --------------------------------------------------------------------------- // set-up // --------------------------------------------------------------------------- - -bool Layer::getPremultipledAlpha() const { - return mPremultipliedAlpha; -} - sp<IBinder> Layer::getHandle() { Mutex::Autolock _l(mLock); if (mGetHandleCalled) { @@ -347,46 +213,6 @@ sp<IBinder> Layer::getHandle() { // h/w composer set-up // --------------------------------------------------------------------------- -static Rect reduce(const Rect& win, const Region& exclude) { - if (CC_LIKELY(exclude.isEmpty())) { - return win; - } - if (exclude.isRect()) { - return win.reduce(exclude.getBounds()); - } - return Region(win).subtract(exclude).getBounds(); -} - -static FloatRect reduce(const FloatRect& win, const Region& exclude) { - if (CC_LIKELY(exclude.isEmpty())) { - return win; - } - // Convert through Rect (by rounding) for lack of FloatRegion - return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect(); -} - -Rect Layer::getScreenBounds(bool reduceTransparentRegion) const { - if (!reduceTransparentRegion) { - return Rect{mScreenBounds}; - } - - FloatRect bounds = getBounds(); - ui::Transform t = getTransform(); - // Transform to screen space. - bounds = t.transform(bounds); - return Rect{bounds}; -} - -FloatRect Layer::getBounds() const { - const State& s(getDrawingState()); - return getBounds(getActiveTransparentRegion(s)); -} - -FloatRect Layer::getBounds(const Region& activeTransparentRegion) const { - // Subtract the transparent region and snap to the bounds. - return reduce(mBounds, activeTransparentRegion); -} - // No early returns. void Layer::updateTrustedPresentationState(const DisplayDevice* display, const frontend::LayerSnapshot* snapshot, @@ -488,57 +314,6 @@ bool Layer::computeTrustedPresentationState(const FloatRect& bounds, const Float return true; } -void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform, - float parentShadowRadius) { - const State& s(getDrawingState()); - - // Calculate effective layer transform - mEffectiveTransform = parentTransform * getActiveTransform(s); - - if (CC_UNLIKELY(!isTransformValid())) { - ALOGW("Stop computing bounds for %s because it has invalid transformation.", - getDebugName()); - return; - } - - // Transform parent bounds to layer space - parentBounds = getActiveTransform(s).inverse().transform(parentBounds); - - // Calculate source bounds - mSourceBounds = computeSourceBounds(parentBounds); - - // Calculate bounds by croping diplay frame with layer crop and parent bounds - FloatRect bounds = mSourceBounds; - const Rect layerCrop = getCrop(s); - if (!layerCrop.isEmpty()) { - bounds = mSourceBounds.intersect(layerCrop.toFloatRect()); - } - bounds = bounds.intersect(parentBounds); - - mBounds = bounds; - mScreenBounds = mEffectiveTransform.transform(mBounds); - - // Use the layer's own shadow radius if set. Otherwise get the radius from - // parent. - if (s.shadowRadius > 0.f) { - mEffectiveShadowRadius = s.shadowRadius; - } else { - mEffectiveShadowRadius = parentShadowRadius; - } - - // Shadow radius is passed down to only one layer so if the layer can draw shadows, - // don't pass it to its children. - const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius; - - for (const sp<Layer>& child : mDrawingChildren) { - child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius); - } - - if (mPotentialCursor) { - prepareCursorCompositionState(); - } -} - Rect Layer::getCroppedBufferSize(const State& s) const { Rect size = getBufferSize(s); Rect crop = getCrop(s); @@ -550,181 +325,6 @@ Rect Layer::getCroppedBufferSize(const State& s) const { return size; } -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; -} - -void Layer::prepareBasicGeometryCompositionState() { - const auto& drawingState{getDrawingState()}; - const auto alpha = static_cast<float>(getAlpha()); - const bool opaque = isOpaque(drawingState); - const bool usesRoundedCorners = hasRoundedCorners(); - - auto blendMode = Hwc2::IComposerClient::BlendMode::NONE; - if (!opaque || alpha != 1.0f) { - blendMode = mPremultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED - : Hwc2::IComposerClient::BlendMode::COVERAGE; - } - - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - snapshot->outputFilter = getOutputFilter(); - snapshot->isVisible = isVisible(); - snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f; - snapshot->shadowSettings.length = mEffectiveShadowRadius; - - snapshot->contentDirty = contentDirty; - contentDirty = false; - - snapshot->geomLayerBounds = mBounds; - snapshot->geomLayerTransform = getTransform(); - snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse(); - snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState); - snapshot->localTransform = getActiveTransform(drawingState); - snapshot->localTransformInverse = snapshot->localTransform.inverse(); - snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); - snapshot->alpha = alpha; - snapshot->backgroundBlurRadius = getBackgroundBlurRadius(); - snapshot->blurRegions = getBlurRegions(); - snapshot->stretchEffect = getStretchEffect(); -} - -void Layer::prepareGeometryCompositionState() { - const auto& drawingState{getDrawingState()}; - auto* snapshot = editLayerSnapshot(); - - // Please keep in sync with LayerSnapshotBuilder - snapshot->geomBufferSize = getBufferSize(drawingState); - snapshot->geomContentCrop = getBufferCrop(); - snapshot->geomCrop = getCrop(drawingState); - snapshot->geomBufferTransform = getBufferTransform(); - snapshot->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse(); - snapshot->geomUsesSourceCrop = usesSourceCrop(); - snapshot->isSecure = isSecure(); - - snapshot->metadata.clear(); - const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata(); - for (const auto& [key, mandatory] : supportedMetadata) { - const auto& genericLayerMetadataCompatibilityMap = - mFlinger->getGenericLayerMetadataKeyMap(); - auto compatIter = genericLayerMetadataCompatibilityMap.find(key); - if (compatIter == std::end(genericLayerMetadataCompatibilityMap)) { - continue; - } - const uint32_t id = compatIter->second; - - auto it = drawingState.metadata.mMap.find(id); - if (it == std::end(drawingState.metadata.mMap)) { - continue; - } - - snapshot->metadata.emplace(key, - compositionengine::GenericLayerMetadataEntry{mandatory, - it->second}); - } -} - -void Layer::preparePerFrameCompositionState() { - const auto& drawingState{getDrawingState()}; - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - - snapshot->forceClientComposition = false; - - snapshot->isColorspaceAgnostic = isColorSpaceAgnostic(); - snapshot->dataspace = getDataSpace(); - snapshot->colorTransform = getColorTransform(); - snapshot->colorTransformIsIdentity = !hasColorTransform(); - snapshot->surfaceDamage = surfaceDamageRegion; - snapshot->hasProtectedContent = isProtected(); - snapshot->dimmingEnabled = isDimmingEnabled(); - snapshot->currentHdrSdrRatio = getCurrentHdrSdrRatio(); - snapshot->desiredHdrSdrRatio = getDesiredHdrSdrRatio(); - snapshot->cachingHint = getCachingHint(); - - const bool usesRoundedCorners = hasRoundedCorners(); - - snapshot->isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf; - - // Force client composition for special cases known only to the front-end. - // Rounded corners no longer force client composition, since we may use a - // hole punch so that the layer will appear to have rounded corners. - if (drawShadows() || snapshot->stretchEffect.hasEffect()) { - snapshot->forceClientComposition = true; - } - // If there are no visible region changes, we still need to update blur parameters. - snapshot->blurRegions = getBlurRegions(); - snapshot->backgroundBlurRadius = getBackgroundBlurRadius(); - - // Layer framerate is used in caching decisions. - // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in - // LayerFECompositionState where it would be visible to Flattener. - snapshot->fps = mFlinger->getLayerFramerate(systemTime(), getSequence()); - - if (hasBufferOrSidebandStream()) { - preparePerFrameBufferCompositionState(); - } else { - preparePerFrameEffectsCompositionState(); - } -} - -void Layer::preparePerFrameBufferCompositionState() { - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - // Sideband layers - if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) { - snapshot->compositionType = - aidl::android::hardware::graphics::composer3::Composition::SIDEBAND; - return; - } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) { - snapshot->compositionType = - aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; - } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) { - snapshot->compositionType = - aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR; - } else { - // Normal buffer layers - snapshot->hdrMetadata = mBufferInfo.mHdrMetadata; - snapshot->compositionType = mPotentialCursor - ? aidl::android::hardware::graphics::composer3::Composition::CURSOR - : aidl::android::hardware::graphics::composer3::Composition::DEVICE; - } - - snapshot->buffer = getBuffer(); - snapshot->acquireFence = mBufferInfo.mFence; - snapshot->frameNumber = mBufferInfo.mFrameNumber; - snapshot->sidebandStreamHasFrame = false; -} - -void Layer::preparePerFrameEffectsCompositionState() { - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - snapshot->color = getColor(); - snapshot->compositionType = - aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR; -} - -void Layer::prepareCursorCompositionState() { - const State& drawingState{getDrawingState()}; - // Please keep in sync with LayerSnapshotBuilder - auto* snapshot = editLayerSnapshot(); - - // 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 = getCroppedBufferSize(drawingState); - // Subtract the transparent region and snap to the bounds - Rect bounds = reduce(win, getActiveTransparentRegion(drawingState)); - Rect frame(getTransform().transform(bounds)); - - snapshot->cursorFrame = frame; -} - const char* Layer::getDebugName() const { return mName.c_str(); } @@ -752,56 +352,9 @@ aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionT } // ---------------------------------------------------------------------------- -// local state -// ---------------------------------------------------------------------------- - -bool Layer::isSecure() const { - const State& s(mDrawingState); - if (s.flags & layer_state_t::eLayerSecure) { - return true; - } - - const auto p = mDrawingParent.promote(); - return (p != nullptr) ? p->isSecure() : false; -} - -// ---------------------------------------------------------------------------- // transaction // ---------------------------------------------------------------------------- -uint32_t Layer::doTransaction(uint32_t flags) { - SFTRACE_CALL(); - - // TODO: This is unfortunate. - mDrawingStateModified = mDrawingState.modified; - mDrawingState.modified = false; - - const State& s(getDrawingState()); - - if (updateGeometry()) { - // invalidate and recompute the visible regions if needed - flags |= Layer::eVisibleRegion; - } - - if (s.sequence != mLastCommittedTxSequence) { - // invalidate and recompute the visible regions if needed - mLastCommittedTxSequence = s.sequence; - flags |= eVisibleRegion; - this->contentDirty = true; - - // we may use linear filtering, if the matrix scales us - mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering(); - } - - if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) { - mFlinger->mUpdateInputInfo = true; - } - - commitTransaction(); - - return flags; -} - void Layer::commitTransaction() { // Set the present state for all bufferlessSurfaceFramesTX to Presented. The // bufferSurfaceFrameTX will be presented in latchBuffer. @@ -816,285 +369,15 @@ void Layer::commitTransaction() { mDrawingState.bufferlessSurfaceFramesTX.clear(); } -uint32_t Layer::clearTransactionFlags(uint32_t mask) { - const auto flags = mTransactionFlags & mask; - mTransactionFlags &= ~mask; - return flags; -} - void Layer::setTransactionFlags(uint32_t mask) { mTransactionFlags |= mask; } -bool Layer::setChildLayer(const sp<Layer>& childLayer, int32_t z) { - ssize_t idx = mCurrentChildren.indexOf(childLayer); - if (idx < 0) { - return false; - } - if (childLayer->setLayer(z)) { - mCurrentChildren.removeAt(idx); - mCurrentChildren.add(childLayer); - return true; - } - return false; -} - -bool Layer::setChildRelativeLayer(const sp<Layer>& childLayer, - const sp<IBinder>& relativeToHandle, int32_t relativeZ) { - ssize_t idx = mCurrentChildren.indexOf(childLayer); - if (idx < 0) { - return false; - } - if (childLayer->setRelativeLayer(relativeToHandle, relativeZ)) { - mCurrentChildren.removeAt(idx); - mCurrentChildren.add(childLayer); - return true; - } - return false; -} - -bool Layer::setLayer(int32_t z) { - if (mDrawingState.z == z && !usingRelativeZ(LayerVector::StateSet::Current)) return false; - mDrawingState.sequence++; - mDrawingState.z = z; - mDrawingState.modified = true; - - mFlinger->mSomeChildrenChanged = true; - - // Discard all relative layering. - if (mDrawingState.zOrderRelativeOf != nullptr) { - sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote(); - if (strongRelative != nullptr) { - strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this)); - } - setZOrderRelativeOf(nullptr); - } - setTransactionFlags(eTransactionNeeded); - return true; -} - -void Layer::removeZOrderRelative(const wp<Layer>& relative) { - mDrawingState.zOrderRelatives.remove(relative); - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); -} - -void Layer::addZOrderRelative(const wp<Layer>& relative) { - mDrawingState.zOrderRelatives.add(relative); - mDrawingState.modified = true; - mDrawingState.sequence++; - setTransactionFlags(eTransactionNeeded); -} - -void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) { - mDrawingState.zOrderRelativeOf = relativeOf; - mDrawingState.sequence++; - mDrawingState.modified = true; - mDrawingState.isRelativeOf = relativeOf != nullptr; - - setTransactionFlags(eTransactionNeeded); -} - -bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) { - sp<Layer> relative = LayerHandle::getLayer(relativeToHandle); - if (relative == nullptr) { - return false; - } - - if (mDrawingState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) && - mDrawingState.zOrderRelativeOf == relative) { - return false; - } - - if (CC_UNLIKELY(relative->usingRelativeZ(LayerVector::StateSet::Drawing)) && - (relative->mDrawingState.zOrderRelativeOf == this)) { - ALOGE("Detected relative layer loop between %s and %s", - mName.c_str(), relative->mName.c_str()); - ALOGE("Ignoring new call to set relative layer"); - return false; - } - - mFlinger->mSomeChildrenChanged = true; - - mDrawingState.sequence++; - mDrawingState.modified = true; - mDrawingState.z = relativeZ; - - auto oldZOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote(); - if (oldZOrderRelativeOf != nullptr) { - oldZOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this)); - } - setZOrderRelativeOf(relative); - relative->addZOrderRelative(wp<Layer>::fromExisting(this)); - - setTransactionFlags(eTransactionNeeded); - - return true; -} - -bool Layer::setTrustedOverlay(bool isTrustedOverlay) { - if (mDrawingState.isTrustedOverlay == isTrustedOverlay) return false; - mDrawingState.isTrustedOverlay = isTrustedOverlay; - mDrawingState.modified = true; - mFlinger->mUpdateInputInfo = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::isTrustedOverlay() const { - if (getDrawingState().isTrustedOverlay) { - return true; - } - const auto& p = mDrawingParent.promote(); - return (p != nullptr) && p->isTrustedOverlay(); -} - -bool Layer::setAlpha(float alpha) { - if (mDrawingState.color.a == alpha) return false; - mDrawingState.sequence++; - mDrawingState.color.a = alpha; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace) { - if (!mDrawingState.bgColorLayer && alpha == 0) { - return false; - } - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - if (!mDrawingState.bgColorLayer && alpha != 0) { - // create background color layer if one does not yet exist - uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect; - std::string name = mName + "BackgroundColorLayer"; - mDrawingState.bgColorLayer = mFlinger->getFactory().createEffectLayer( - surfaceflinger::LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), flags, - LayerMetadata())); - - // add to child list - addChild(mDrawingState.bgColorLayer); - mFlinger->mLayersAdded = true; - // set up SF to handle added color layer - if (isRemovedFromCurrentState()) { - MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock); - mDrawingState.bgColorLayer->onRemovedFromCurrentState(); - } - mFlinger->setTransactionFlags(eTransactionNeeded); - } else if (mDrawingState.bgColorLayer && alpha == 0) { - MUTEX_ALIAS(mFlinger->mStateLock, mDrawingState.bgColorLayer->mFlinger->mStateLock); - mDrawingState.bgColorLayer->reparent(nullptr); - mDrawingState.bgColorLayer = nullptr; - return true; - } - - mDrawingState.bgColorLayer->setColor(color); - mDrawingState.bgColorLayer->setLayer(std::numeric_limits<int32_t>::min()); - mDrawingState.bgColorLayer->setAlpha(alpha); - mDrawingState.bgColorLayer->setDataspace(dataspace); - - return true; -} - -bool Layer::setCornerRadius(float cornerRadius) { - if (mDrawingState.cornerRadius == cornerRadius) return false; - - mDrawingState.sequence++; - mDrawingState.cornerRadius = cornerRadius; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) { - if (mDrawingState.backgroundBlurRadius == backgroundBlurRadius) return false; - // If we start or stop drawing blur then the layer's visibility state may change so increment - // the magic sequence number. - if (mDrawingState.backgroundBlurRadius == 0 || backgroundBlurRadius == 0) { - mDrawingState.sequence++; - } - mDrawingState.backgroundBlurRadius = backgroundBlurRadius; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setTransparentRegionHint(const Region& transparent) { - mDrawingState.sequence++; - mDrawingState.transparentRegionHint = transparent; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) { - // If we start or stop drawing blur then the layer's visibility state may change so increment - // the magic sequence number. - if (mDrawingState.blurRegions.size() == 0 || blurRegions.size() == 0) { - mDrawingState.sequence++; - } - mDrawingState.blurRegions = blurRegions; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setFlags(uint32_t flags, uint32_t mask) { - const uint32_t newFlags = (mDrawingState.flags & ~mask) | (flags & mask); - if (mDrawingState.flags == newFlags) return false; - mDrawingState.sequence++; - mDrawingState.flags = newFlags; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - bool Layer::setCrop(const Rect& crop) { if (mDrawingState.crop == crop) return false; mDrawingState.sequence++; mDrawingState.crop = crop; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setMetadata(const LayerMetadata& data) { - if (!mDrawingState.metadata.merge(data, true /* eraseEmpty */)) return false; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setLayerStack(ui::LayerStack layerStack) { - if (mDrawingState.layerStack == layerStack) return false; - mDrawingState.sequence++; - mDrawingState.layerStack = layerStack; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setColorSpaceAgnostic(const bool agnostic) { - if (mDrawingState.colorSpaceAgnostic == agnostic) { - return false; - } - mDrawingState.sequence++; - mDrawingState.colorSpaceAgnostic = agnostic; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setDimmingEnabled(const bool dimmingEnabled) { - if (mDrawingState.dimmingEnabled == dimmingEnabled) return false; - - mDrawingState.sequence++; - mDrawingState.dimmingEnabled = dimmingEnabled; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -1103,68 +386,6 @@ bool Layer::isLayerFocusedBasedOnPriority(int32_t priority) { return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE; }; -ui::LayerStack Layer::getLayerStack(LayerVector::StateSet state) const { - bool useDrawing = state == LayerVector::StateSet::Drawing; - const auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); - if (parent) { - return parent->getLayerStack(); - } - return getDrawingState().layerStack; -} - -bool Layer::setShadowRadius(float shadowRadius) { - if (mDrawingState.shadowRadius == shadowRadius) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.shadowRadius = shadowRadius; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint) { - if (mDrawingState.fixedTransformHint == fixedTransformHint) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.fixedTransformHint = fixedTransformHint; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setStretchEffect(const StretchEffect& effect) { - StretchEffect temp = effect; - temp.sanitize(); - if (mDrawingState.stretchEffect == temp) { - return false; - } - mDrawingState.sequence++; - mDrawingState.stretchEffect = temp; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -StretchEffect Layer::getStretchEffect() const { - if (mDrawingState.stretchEffect.hasEffect()) { - return mDrawingState.stretchEffect; - } - - sp<Layer> parent = getParent(); - if (parent != nullptr) { - auto effect = parent->getStretchEffect(); - if (effect.hasEffect()) { - // TODO(b/179047472): Map it? Or do we make the effect be in global space? - return effect; - } - } - return StretchEffect{}; -} - void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode) { mDrawingState.postTime = postTime; @@ -1193,7 +414,6 @@ void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInf gui::GameMode gameMode) { mDrawingState.frameTimelineInfo = info; mDrawingState.postTime = postTime; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); if (const auto& bufferSurfaceFrameTX = mDrawingState.bufferSurfaceFrameTX; @@ -1315,40 +535,6 @@ Layer::FrameRate Layer::getFrameRateForLayerTree() const { return getDrawingState().frameRateForLayerTree; } -bool Layer::isHiddenByPolicy() const { - const State& s(mDrawingState); - const auto& parent = mDrawingParent.promote(); - if (parent != nullptr && parent->isHiddenByPolicy()) { - return true; - } - if (usingRelativeZ(LayerVector::StateSet::Drawing)) { - auto zOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote(); - if (zOrderRelativeOf != nullptr) { - if (zOrderRelativeOf->isHiddenByPolicy()) { - return true; - } - } - } - if (CC_UNLIKELY(!isTransformValid())) { - ALOGW("Hide layer %s because it has invalid transformation.", getDebugName()); - return true; - } - return s.flags & layer_state_t::eLayerHidden; -} - -uint32_t Layer::getEffectiveUsage(uint32_t usage) const { - // TODO: should we do something special if mSecure is set? - if (mProtectedByApp) { - // need a hardware-protected path to external video sink - usage |= GraphicBuffer::USAGE_PROTECTED; - } - if (mPotentialCursor) { - usage |= GraphicBuffer::USAGE_CURSOR; - } - usage |= GraphicBuffer::USAGE_HW_COMPOSER; - return usage; -} - // ---------------------------------------------------------------------------- // debugging // ---------------------------------------------------------------------------- @@ -1427,489 +613,12 @@ void Layer::getFrameStats(FrameStats* outStats) const { mFrameTracker.getStats(outStats); } -void Layer::dumpOffscreenDebugInfo(std::string& result) const { - std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : ""; - StringAppendF(&result, "Layer %s%s pid:%d uid:%d%s\n", getName().c_str(), hasBuffer.c_str(), - mOwnerPid, mOwnerUid, isHandleAlive() ? " handleAlive" : ""); -} - void Layer::onDisconnect() { const int32_t layerId = getSequence(); mFlinger->mTimeStats->onDestroy(layerId); mFlinger->mFrameTracer->onDestroy(layerId); } -size_t Layer::getDescendantCount() const { - size_t count = 0; - for (const sp<Layer>& child : mDrawingChildren) { - count += 1 + child->getChildrenCount(); - } - return count; -} - -void Layer::addChild(const sp<Layer>& layer) { - mFlinger->mSomeChildrenChanged = true; - setTransactionFlags(eTransactionNeeded); - - mCurrentChildren.add(layer); - layer->setParent(sp<Layer>::fromExisting(this)); -} - -ssize_t Layer::removeChild(const sp<Layer>& layer) { - mFlinger->mSomeChildrenChanged = true; - setTransactionFlags(eTransactionNeeded); - - layer->setParent(nullptr); - const auto removeResult = mCurrentChildren.remove(layer); - - return removeResult; -} - -void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { - for (const sp<Layer>& child : mDrawingChildren) { - child->mDrawingParent = newParent; - const float parentShadowRadius = - newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius; - child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform, - parentShadowRadius); - } -} - -bool Layer::reparent(const sp<IBinder>& newParentHandle) { - sp<Layer> newParent; - if (newParentHandle != nullptr) { - newParent = LayerHandle::getLayer(newParentHandle); - if (newParent == nullptr) { - ALOGE("Unable to promote Layer handle"); - return false; - } - if (newParent == this) { - ALOGE("Invalid attempt to reparent Layer (%s) to itself", getName().c_str()); - return false; - } - } - - sp<Layer> parent = getParent(); - if (parent != nullptr) { - parent->removeChild(sp<Layer>::fromExisting(this)); - } - - if (newParentHandle != nullptr) { - newParent->addChild(sp<Layer>::fromExisting(this)); - if (!newParent->isRemovedFromCurrentState()) { - addToCurrentState(); - } else { - onRemovedFromCurrentState(); - } - } else { - onRemovedFromCurrentState(); - } - - return true; -} - -bool Layer::setColorTransform(const mat4& matrix) { - static const mat4 identityMatrix = mat4(); - - if (mDrawingState.colorTransform == matrix) { - return false; - } - ++mDrawingState.sequence; - mDrawingState.colorTransform = matrix; - mDrawingState.hasColorTransform = matrix != identityMatrix; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -mat4 Layer::getColorTransform() const { - mat4 colorTransform = mat4(getDrawingState().colorTransform); - if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) { - colorTransform = parent->getColorTransform() * colorTransform; - } - return colorTransform; -} - -bool Layer::hasColorTransform() const { - bool hasColorTransform = getDrawingState().hasColorTransform; - if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) { - hasColorTransform = hasColorTransform || parent->hasColorTransform(); - } - return hasColorTransform; -} - -bool Layer::isLegacyDataSpace() const { - // return true when no higher bits are set - return !(getDataSpace() & - (ui::Dataspace::STANDARD_MASK | ui::Dataspace::TRANSFER_MASK | - ui::Dataspace::RANGE_MASK)); -} - -void Layer::setParent(const sp<Layer>& layer) { - mCurrentParent = layer; -} - -int32_t Layer::getZ(LayerVector::StateSet) const { - return mDrawingState.z; -} - -bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const { - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const State& state = useDrawing ? mDrawingState : mDrawingState; - return state.isRelativeOf; -} - -__attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList( - LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers) { - LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid, - "makeTraversalList received invalid stateSet"); - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mDrawingState; - - if (state.zOrderRelatives.size() == 0) { - *outSkipRelativeZUsers = true; - return children; - } - - LayerVector traverse(stateSet); - for (const wp<Layer>& weakRelative : state.zOrderRelatives) { - sp<Layer> strongRelative = weakRelative.promote(); - if (strongRelative != nullptr) { - traverse.add(strongRelative); - } - } - - for (const sp<Layer>& child : children) { - if (child->usingRelativeZ(stateSet)) { - continue; - } - traverse.add(child); - } - - return traverse; -} - -/** - * Negatively signed relatives are before 'this' in Z-order. - */ -void Layer::traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor) { - // In the case we have other layers who are using a relative Z to us, makeTraversalList will - // produce a new list for traversing, including our relatives, and not including our children - // who are relatives of another surface. In the case that there are no relative Z, - // makeTraversalList returns our children directly to avoid significant overhead. - // However in this case we need to take the responsibility for filtering children which - // are relatives of another surface here. - bool skipRelativeZUsers = false; - const LayerVector list = makeTraversalList(stateSet, &skipRelativeZUsers); - - size_t i = 0; - for (; i < list.size(); i++) { - const auto& relative = list[i]; - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { - continue; - } - - if (relative->getZ(stateSet) >= 0) { - break; - } - relative->traverseInZOrder(stateSet, visitor); - } - - visitor(this); - for (; i < list.size(); i++) { - const auto& relative = list[i]; - - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { - continue; - } - relative->traverseInZOrder(stateSet, visitor); - } -} - -/** - * Positively signed relatives are before 'this' in reverse Z-order. - */ -void Layer::traverseInReverseZOrder(LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor) { - // See traverseInZOrder for documentation. - bool skipRelativeZUsers = false; - LayerVector list = makeTraversalList(stateSet, &skipRelativeZUsers); - - int32_t i = 0; - for (i = int32_t(list.size()) - 1; i >= 0; i--) { - const auto& relative = list[i]; - - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { - continue; - } - - if (relative->getZ(stateSet) < 0) { - break; - } - relative->traverseInReverseZOrder(stateSet, visitor); - } - visitor(this); - for (; i >= 0; i--) { - const auto& relative = list[i]; - - if (skipRelativeZUsers && relative->usingRelativeZ(stateSet)) { - continue; - } - - relative->traverseInReverseZOrder(stateSet, visitor); - } -} - -void Layer::traverse(LayerVector::StateSet state, const LayerVector::Visitor& visitor) { - visitor(this); - const LayerVector& children = - state == LayerVector::StateSet::Drawing ? mDrawingChildren : mCurrentChildren; - for (const sp<Layer>& child : children) { - child->traverse(state, visitor); - } -} - -void Layer::traverseChildren(const LayerVector::Visitor& visitor) { - for (const sp<Layer>& child : mDrawingChildren) { - visitor(child.get()); - } -} - -LayerVector Layer::makeChildrenTraversalList(LayerVector::StateSet stateSet, - const std::vector<Layer*>& layersInTree) { - LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid, - "makeTraversalList received invalid stateSet"); - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mDrawingState; - - LayerVector traverse(stateSet); - for (const wp<Layer>& weakRelative : state.zOrderRelatives) { - sp<Layer> strongRelative = weakRelative.promote(); - // Only add relative layers that are also descendents of the top most parent of the tree. - // If a relative layer is not a descendent, then it should be ignored. - if (std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) { - traverse.add(strongRelative); - } - } - - for (const sp<Layer>& child : children) { - const State& childState = useDrawing ? child->mDrawingState : child->mDrawingState; - // 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. - if (std::binary_search(layersInTree.begin(), layersInTree.end(), - childState.zOrderRelativeOf.promote().get())) { - continue; - } - traverse.add(child); - } - - return traverse; -} - -void Layer::traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree, - LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor) { - const LayerVector list = makeChildrenTraversalList(stateSet, layersInTree); - - size_t i = 0; - for (; i < list.size(); i++) { - const auto& relative = list[i]; - if (relative->getZ(stateSet) >= 0) { - break; - } - relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); - } - - visitor(this); - for (; i < list.size(); i++) { - const auto& relative = list[i]; - relative->traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); - } -} - -std::vector<Layer*> Layer::getLayersInTree(LayerVector::StateSet stateSet) { - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - - std::vector<Layer*> layersInTree = {this}; - for (size_t i = 0; i < children.size(); i++) { - const auto& child = children[i]; - std::vector<Layer*> childLayers = child->getLayersInTree(stateSet); - layersInTree.insert(layersInTree.end(), childLayers.cbegin(), childLayers.cend()); - } - - return layersInTree; -} - -void Layer::traverseChildrenInZOrder(LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor) { - std::vector<Layer*> layersInTree = getLayersInTree(stateSet); - std::sort(layersInTree.begin(), layersInTree.end()); - traverseChildrenInZOrderInner(layersInTree, stateSet, visitor); -} - -ui::Transform Layer::getTransform() const { - return mEffectiveTransform; -} - -bool Layer::isTransformValid() const { - float transformDet = getTransform().det(); - return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet); -} - -half Layer::getAlpha() const { - const auto& p = mDrawingParent.promote(); - - half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf; - return parentAlpha * getDrawingState().color.a; -} - -ui::Transform::RotationFlags Layer::getFixedTransformHint() const { - ui::Transform::RotationFlags fixedTransformHint = mDrawingState.fixedTransformHint; - if (fixedTransformHint != ui::Transform::ROT_INVALID) { - return fixedTransformHint; - } - const auto& p = mCurrentParent.promote(); - if (!p) return fixedTransformHint; - return p->getFixedTransformHint(); -} - -half4 Layer::getColor() const { - const half4 color(getDrawingState().color); - return half4(color.r, color.g, color.b, getAlpha()); -} - -int32_t Layer::getBackgroundBlurRadius() const { - if (getDrawingState().backgroundBlurRadius == 0) { - return 0; - } - - const auto& p = mDrawingParent.promote(); - half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf; - return parentAlpha * getDrawingState().backgroundBlurRadius; -} - -const std::vector<BlurRegion> Layer::getBlurRegions() const { - auto regionsCopy(getDrawingState().blurRegions); - float layerAlpha = getAlpha(); - for (auto& region : regionsCopy) { - region.alpha = region.alpha * layerAlpha; - } - return regionsCopy; -} - -RoundedCornerState Layer::getRoundedCornerState() const { - // Today's DPUs cannot do rounded corners. If RenderEngine cannot render - // protected content, remove rounded corners from protected content so it - // can be rendered by the DPU. - if (isProtected() && !mFlinger->getRenderEngine().supportsProtectedContent()) { - return {}; - } - - // Get parent settings - RoundedCornerState parentSettings; - const auto& parent = mDrawingParent.promote(); - if (parent != nullptr) { - parentSettings = parent->getRoundedCornerState(); - if (parentSettings.hasRoundedCorners()) { - ui::Transform t = getActiveTransform(getDrawingState()); - t = t.inverse(); - parentSettings.cropRect = t.transform(parentSettings.cropRect); - parentSettings.radius.x *= t.getScaleX(); - parentSettings.radius.y *= t.getScaleY(); - } - } - - // Get layer settings - Rect layerCropRect = getCroppedBufferSize(getDrawingState()); - const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius); - RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius); - const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid(); - - if (layerSettingsValid && parentSettings.hasRoundedCorners()) { - // If the parent and the layer have rounded corner settings, use the parent settings if the - // parent crop is entirely inside the layer crop. - // This has limitations and cause rendering artifacts. See b/200300845 for correct fix. - if (parentSettings.cropRect.left > layerCropRect.left && - parentSettings.cropRect.top > layerCropRect.top && - parentSettings.cropRect.right < layerCropRect.right && - parentSettings.cropRect.bottom < layerCropRect.bottom) { - return parentSettings; - } else { - return layerSettings; - } - } else if (layerSettingsValid) { - return layerSettings; - } else if (parentSettings.hasRoundedCorners()) { - return parentSettings; - } - return {}; -} - -bool Layer::findInHierarchy(const sp<Layer>& l) { - if (l == this) { - return true; - } - for (auto& child : mDrawingChildren) { - if (child->findInHierarchy(l)) { - return true; - } - } - return false; -} - -void Layer::commitChildList() { - for (size_t i = 0; i < mCurrentChildren.size(); i++) { - const auto& child = mCurrentChildren[i]; - child->commitChildList(); - } - mDrawingChildren = mCurrentChildren; - mDrawingParent = mCurrentParent; - if (CC_UNLIKELY(usingRelativeZ(LayerVector::StateSet::Drawing))) { - auto zOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote(); - if (zOrderRelativeOf == nullptr) return; - if (findInHierarchy(zOrderRelativeOf)) { - ALOGE("Detected Z ordering loop between %s and %s", mName.c_str(), - zOrderRelativeOf->mName.c_str()); - ALOGE("Severing rel Z loop, potentially dangerous"); - mDrawingState.isRelativeOf = false; - zOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this)); - } - } -} - - -void Layer::setInputInfo(const WindowInfo& info) { - mDrawingState.inputInfo = info; - mDrawingState.touchableRegionCrop = - LayerHandle::getLayer(info.touchableRegionCropHandle.promote()); - mDrawingState.modified = true; - mFlinger->mUpdateInputInfo = true; - setTransactionFlags(eTransactionNeeded); -} - -perfetto::protos::LayerProto* Layer::writeToProto(perfetto::protos::LayersProto& layersProto, - uint32_t traceFlags) { - perfetto::protos::LayerProto* layerProto = layersProto.add_layers(); - writeToProtoDrawingState(layerProto); - writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags); - - if (traceFlags & LayerTracing::TRACE_COMPOSITION) { - ui::LayerStack layerStack = - (mSnapshot) ? mSnapshot->outputFilter.layerStack : ui::INVALID_LAYER_STACK; - writeCompositionStateToProto(layerProto, layerStack); - } - - for (const sp<Layer>& layer : mDrawingChildren) { - layer->writeToProto(layersProto, traceFlags); - } - - return layerProto; -} - void Layer::writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto, ui::LayerStack layerStack) { ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread. @@ -1925,376 +634,6 @@ void Layer::writeCompositionStateToProto(perfetto::protos::LayerProto* layerProt } } -void Layer::writeToProtoDrawingState(perfetto::protos::LayerProto* layerInfo) { - const ui::Transform transform = getTransform(); - auto buffer = getExternalTexture(); - if (buffer != nullptr) { - LayerProtoHelper::writeToProto(*buffer, - [&]() { return layerInfo->mutable_active_buffer(); }); - LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()), - layerInfo->mutable_buffer_transform()); - } - layerInfo->set_invalidate(contentDirty); - layerInfo->set_is_protected(isProtected()); - layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace()))); - layerInfo->set_queued_frames(getQueuedFrameCount()); - layerInfo->set_curr_frame(mCurrentFrameNumber); - layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius); - layerInfo->set_corner_radius( - (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0); - layerInfo->set_background_blur_radius(getBackgroundBlurRadius()); - layerInfo->set_is_trusted_overlay(isTrustedOverlay()); - LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform()); - LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), - [&]() { return layerInfo->mutable_position(); }); - LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); - LayerProtoHelper::writeToProto(surfaceDamageRegion, - [&]() { return layerInfo->mutable_damage_region(); }); - - if (hasColorTransform()) { - LayerProtoHelper::writeToProto(getColorTransform(), layerInfo->mutable_color_transform()); - } - - LayerProtoHelper::writeToProto(mSourceBounds, - [&]() { return layerInfo->mutable_source_bounds(); }); - LayerProtoHelper::writeToProto(mScreenBounds, - [&]() { return layerInfo->mutable_screen_bounds(); }); - LayerProtoHelper::writeToProto(getRoundedCornerState().cropRect, - [&]() { return layerInfo->mutable_corner_radius_crop(); }); - layerInfo->set_shadow_radius(mEffectiveShadowRadius); -} - -void Layer::writeToProtoCommonState(perfetto::protos::LayerProto* layerInfo, - LayerVector::StateSet stateSet, uint32_t traceFlags) { - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; - const State& state = useDrawing ? mDrawingState : mDrawingState; - - ui::Transform requestedTransform = state.transform; - - layerInfo->set_id(sequence); - layerInfo->set_name(getName().c_str()); - layerInfo->set_type(getType()); - - for (const auto& child : children) { - layerInfo->add_children(child->sequence); - } - - for (const wp<Layer>& weakRelative : state.zOrderRelatives) { - sp<Layer> strongRelative = weakRelative.promote(); - if (strongRelative != nullptr) { - layerInfo->add_relatives(strongRelative->sequence); - } - } - - LayerProtoHelper::writeToProto(state.transparentRegionHint, - [&]() { return layerInfo->mutable_transparent_region(); }); - - layerInfo->set_layer_stack(getLayerStack().id); - layerInfo->set_z(state.z); - - LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() { - return layerInfo->mutable_requested_position(); - }); - - LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); }); - - layerInfo->set_is_opaque(isOpaque(state)); - - layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat())); - LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); }); - LayerProtoHelper::writeToProto(state.color, - [&]() { return layerInfo->mutable_requested_color(); }); - layerInfo->set_flags(state.flags); - - LayerProtoHelper::writeToProtoDeprecated(requestedTransform, - layerInfo->mutable_requested_transform()); - - auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); - if (parent != nullptr) { - layerInfo->set_parent(parent->sequence); - } - - auto zOrderRelativeOf = state.zOrderRelativeOf.promote(); - if (zOrderRelativeOf != nullptr) { - layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence); - } - - layerInfo->set_is_relative_of(state.isRelativeOf); - - layerInfo->set_owner_uid(mOwnerUid); - - if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) { - WindowInfo info; - if (useDrawing) { - info = fillInputInfo( - InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true}); - } else { - info = state.inputInfo; - } - - LayerProtoHelper::writeToProto(info, state.touchableRegionCrop, - [&]() { return layerInfo->mutable_input_window_info(); }); - } - - if (traceFlags & LayerTracing::TRACE_EXTRA) { - auto protoMap = layerInfo->mutable_metadata(); - for (const auto& entry : state.metadata.mMap) { - (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend()); - } - } - - LayerProtoHelper::writeToProto(state.destinationFrame, - [&]() { return layerInfo->mutable_destination_frame(); }); -} - -bool Layer::isRemovedFromCurrentState() const { - return mRemovedFromDrawingState; -} - -// Applies the given transform to the region, while protecting against overflows caused by any -// offsets. If applying the offset in the transform to any of the Rects in the region would result -// in an overflow, they are not added to the output Region. -static Region transformTouchableRegionSafely(const ui::Transform& t, const Region& r, - const std::string& debugWindowName) { - // Round the translation using the same rounding strategy used by ui::Transform. - const auto tx = static_cast<int32_t>(t.tx() + 0.5); - const auto ty = static_cast<int32_t>(t.ty() + 0.5); - - ui::Transform transformWithoutOffset = t; - transformWithoutOffset.set(0.f, 0.f); - - const Region transformed = transformWithoutOffset.transform(r); - - // Apply the translation to each of the Rects in the region while discarding any that overflow. - Region ret; - for (const auto& rect : transformed) { - Rect newRect; - if (__builtin_add_overflow(rect.left, tx, &newRect.left) || - __builtin_add_overflow(rect.top, ty, &newRect.top) || - __builtin_add_overflow(rect.right, tx, &newRect.right) || - __builtin_add_overflow(rect.bottom, ty, &newRect.bottom)) { - ALOGE("Applying transform to touchable region of window '%s' resulted in an overflow.", - debugWindowName.c_str()); - continue; - } - ret.orSelf(newRect); - } - return ret; -} - -void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) { - auto [inputBounds, inputBoundsValid] = getInputBounds(/*fillParentBounds=*/false); - if (!inputBoundsValid) { - info.touchableRegion.clear(); - } - - info.frame = getInputBoundsInDisplaySpace(inputBounds, screenToDisplay); - - ui::Transform inputToLayer; - inputToLayer.set(inputBounds.left, inputBounds.top); - const ui::Transform layerToScreen = getInputTransform(); - const ui::Transform inputToDisplay = screenToDisplay * layerToScreen * inputToLayer; - - // InputDispatcher expects a display-to-input transform. - info.transform = inputToDisplay.inverse(); - - // The touchable region is specified in the input coordinate space. Change it to display space. - info.touchableRegion = - transformTouchableRegionSafely(inputToDisplay, info.touchableRegion, mName); -} - -void Layer::fillTouchOcclusionMode(WindowInfo& info) { - sp<Layer> p = sp<Layer>::fromExisting(this); - while (p != nullptr && !p->hasInputInfo()) { - p = p->mDrawingParent.promote(); - } - if (p != nullptr) { - info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode; - } -} - -gui::DropInputMode Layer::getDropInputMode() const { - gui::DropInputMode mode = mDrawingState.dropInputMode; - if (mode == gui::DropInputMode::ALL) { - return mode; - } - sp<Layer> parent = mDrawingParent.promote(); - if (parent) { - gui::DropInputMode parentMode = parent->getDropInputMode(); - if (parentMode != gui::DropInputMode::NONE) { - return parentMode; - } - } - return mode; -} - -void Layer::handleDropInputMode(gui::WindowInfo& info) const { - if (mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { - return; - } - - // Check if we need to drop input unconditionally - gui::DropInputMode dropInputMode = getDropInputMode(); - if (dropInputMode == gui::DropInputMode::ALL) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; - ALOGV("Dropping input for %s as requested by policy.", getDebugName()); - return; - } - - // Check if we need to check if the window is obscured by parent - if (dropInputMode != gui::DropInputMode::OBSCURED) { - return; - } - - // Check if the parent has set an alpha on the layer - sp<Layer> parent = mDrawingParent.promote(); - if (parent && parent->getAlpha() != 1.0_hf) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; - ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(), - static_cast<float>(getAlpha())); - } - - // Check if the parent has cropped the buffer - Rect bufferSize = getCroppedBufferSize(getDrawingState()); - if (!bufferSize.isValid()) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; - return; - } - - // Screenbounds are the layer bounds cropped by parents, transformed to screenspace. - // To check if the layer has been cropped, we take the buffer bounds, apply the local - // layer crop and apply the same set of transforms to move to screenspace. If the bounds - // match then the layer has not been cropped by its parents. - Rect bufferInScreenSpace(getTransform().transform(bufferSize)); - bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds}; - - if (croppedByParent) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; - ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent", - getDebugName()); - } else { - // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop - // input if the window is obscured. This check should be done in surfaceflinger but the - // logic currently resides in inputflinger. So pass the if_obscured check to input to only - // drop input events if the window is obscured. - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED; - } -} - -WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) { - if (!hasInputInfo()) { - mDrawingState.inputInfo.name = getName(); - mDrawingState.inputInfo.ownerUid = gui::Uid{mOwnerUid}; - mDrawingState.inputInfo.ownerPid = gui::Pid{mOwnerPid}; - mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL; - mDrawingState.inputInfo.displayId = toLogicalDisplayId(getLayerStack()); - } - - const ui::Transform& displayTransform = - displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform; - - WindowInfo info = mDrawingState.inputInfo; - info.id = sequence; - info.displayId = toLogicalDisplayId(getLayerStack()); - - fillInputFrameInfo(info, displayTransform); - - if (displayArgs.transform == nullptr) { - // Do not let the window receive touches if it is not associated with a valid display - // transform. We still allow the window to receive keys and prevent ANRs. - info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE; - } - - info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput()); - - info.alpha = getAlpha(); - fillTouchOcclusionMode(info); - handleDropInputMode(info); - - // If the window will be blacked out on a display because the display does not have the secure - // flag and the layer has the secure flag set, then drop input. - if (!displayArgs.isSecure && isSecure()) { - info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; - } - - sp<Layer> cropLayer = mDrawingState.touchableRegionCrop.promote(); - if (info.replaceTouchableRegionWithCrop) { - Rect inputBoundsInDisplaySpace; - if (!cropLayer) { - FloatRect inputBounds = getInputBounds(/*fillParentBounds=*/true).first; - inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(inputBounds, displayTransform); - } else { - FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first; - inputBoundsInDisplaySpace = - cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform); - } - info.touchableRegion = Region(inputBoundsInDisplaySpace); - } else if (cropLayer != nullptr) { - FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first; - Rect inputBoundsInDisplaySpace = - cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform); - info.touchableRegion = info.touchableRegion.intersect(inputBoundsInDisplaySpace); - } - - // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state - // if it was set by WM for a known system overlay - if (isTrustedOverlay()) { - info.inputConfig |= WindowInfo::InputConfig::TRUSTED_OVERLAY; - } - - // If the layer is a clone, we need to crop the input region to cloned root to prevent - // touches from going outside the cloned area. - if (isClone()) { - info.inputConfig |= WindowInfo::InputConfig::CLONE; - if (const sp<Layer> clonedRoot = getClonedRoot()) { - const Rect rect = displayTransform.transform(Rect{clonedRoot->mScreenBounds}); - info.touchableRegion = info.touchableRegion.intersect(rect); - } - } - - Rect bufferSize = getBufferSize(getDrawingState()); - info.contentSize = Size(bufferSize.width(), bufferSize.height()); - - return info; -} - -Rect Layer::getInputBoundsInDisplaySpace(const FloatRect& inputBounds, - const ui::Transform& screenToDisplay) { - // InputDispatcher works in the display device's coordinate space. Here, we calculate the - // frame and transform used for the layer, which determines the bounds and the coordinate space - // within which the layer will receive input. - - // Coordinate space definitions: - // - display: The display device's coordinate space. Correlates to pixels on the display. - // - screen: The post-rotation coordinate space for the display, a.k.a. logical display space. - // - layer: The coordinate space of this layer. - // - input: The coordinate space in which this layer will receive input events. This could be - // different than layer space if a surfaceInset is used, which changes the origin - // of the input space. - - // Crop the input bounds to ensure it is within the parent's bounds. - const FloatRect croppedInputBounds = mBounds.intersect(inputBounds); - const ui::Transform layerToScreen = getInputTransform(); - const ui::Transform layerToDisplay = screenToDisplay * layerToScreen; - return Rect{layerToDisplay.transform(croppedInputBounds)}; -} - -sp<Layer> Layer::getClonedRoot() { - if (mClonedChild != nullptr) { - return sp<Layer>::fromExisting(this); - } - if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) { - return nullptr; - } - return mDrawingParent.promote()->getClonedRoot(); -} - -bool Layer::hasInputInfo() const { - return mDrawingState.inputInfo.token != nullptr || - mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); -} - compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( const DisplayDevice* display) const { if (!display) return nullptr; @@ -2329,215 +668,27 @@ Region Layer::getVisibleRegion(const DisplayDevice* display) const { return outputLayer ? outputLayer->getState().visibleRegion : Region(); } -void Layer::updateCloneBufferInfo() { - if (!isClone() || !isClonedFromAlive()) { - return; - } - - sp<Layer> clonedFrom = getClonedFrom(); - mBufferInfo = clonedFrom->mBufferInfo; - mSidebandStream = clonedFrom->mSidebandStream; - surfaceDamageRegion = clonedFrom->surfaceDamageRegion; - mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load(); - mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber; - - // After buffer info is updated, the drawingState from the real layer needs to be copied into - // the cloned. This is because some properties of drawingState can change when latchBuffer is - // called. However, copying the drawingState would also overwrite the cloned layer's relatives - // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in - // the cloned drawingState again. - wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; - SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives; - wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop; - WindowInfo tmpInputInfo = mDrawingState.inputInfo; - - cloneDrawingState(clonedFrom.get()); - - mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop; - mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; - mDrawingState.zOrderRelatives = tmpZOrderRelatives; - mDrawingState.inputInfo = tmpInputInfo; -} - -bool Layer::updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates) { - if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) { - // If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false, - // it means that there is a clone, but the layer it was cloned from has been destroyed. In - // that case, we want to delete the reference to the clone since we want it to get - // destroyed. The root, this layer, will still be around since the client can continue - // to hold a reference, but no cloned layers will be displayed. - mClonedChild = nullptr; - return true; - } - - std::map<sp<Layer>, sp<Layer>> clonedLayersMap; - // If the real layer exists and is in current state, add the clone as a child of the root. - // There's no need to remove from drawingState when the layer is offscreen since currentState is - // copied to drawingState for the root layer. So the clonedChild is always removed from - // drawingState and then needs to be added back each traversal. - if (!mClonedChild->getClonedFrom()->isRemovedFromCurrentState()) { - addChildToDrawing(mClonedChild); - } - - mClonedChild->updateClonedDrawingState(clonedLayersMap); - mClonedChild->updateClonedChildren(sp<Layer>::fromExisting(this), clonedLayersMap); - mClonedChild->updateClonedRelatives(clonedLayersMap); - - for (Layer* root : cloneRootsPendingUpdates) { - if (clonedLayersMap.find(sp<Layer>::fromExisting(root)) != clonedLayersMap.end()) { - return false; - } - } - return true; -} - -void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - // If the layer the clone was cloned from is alive, copy the content of the drawingState - // to the clone. If the real layer is no longer alive, continue traversing the children - // since we may be able to pull out other children that are still alive. - if (isClonedFromAlive()) { - sp<Layer> clonedFrom = getClonedFrom(); - cloneDrawingState(clonedFrom.get()); - clonedLayersMap.emplace(clonedFrom, sp<Layer>::fromExisting(this)); - } - - // The clone layer may have children in drawingState since they may have been created and - // added from a previous request to updateMirorInfo. This is to ensure we don't recreate clones - // that already exist, since we can just re-use them. - // The drawingChildren will not get overwritten by the currentChildren since the clones are - // not updated in the regular traversal. They are skipped since the root will lose the - // reference to them when it copies its currentChildren to drawing. - for (sp<Layer>& child : mDrawingChildren) { - child->updateClonedDrawingState(clonedLayersMap); - } -} - -void Layer::updateClonedChildren(const sp<Layer>& mirrorRoot, - std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - mDrawingChildren.clear(); - - if (!isClonedFromAlive()) { - return; - } - - sp<Layer> clonedFrom = getClonedFrom(); - for (sp<Layer>& child : clonedFrom->mDrawingChildren) { - if (child == mirrorRoot) { - // This is to avoid cyclical mirroring. - continue; - } - sp<Layer> clonedChild = clonedLayersMap[child]; - if (clonedChild == nullptr) { - clonedChild = child->createClone(); - clonedLayersMap[child] = clonedChild; - } - addChildToDrawing(clonedChild); - clonedChild->updateClonedChildren(mirrorRoot, clonedLayersMap); - } -} - -void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - auto cropLayer = mDrawingState.touchableRegionCrop.promote(); - if (cropLayer != nullptr) { - if (clonedLayersMap.count(cropLayer) == 0) { - // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to - // self as crop layer to avoid going outside bounds. - mDrawingState.touchableRegionCrop = wp<Layer>::fromExisting(this); - } else { - const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer); - mDrawingState.touchableRegionCrop = clonedCropLayer; - } - } - // Cloned layers shouldn't handle watch outside since their z order is not determined by - // WM or the client. - mDrawingState.inputInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, false); -} - -void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { - mDrawingState.zOrderRelativeOf = wp<Layer>(); - mDrawingState.zOrderRelatives.clear(); - - if (!isClonedFromAlive()) { - return; - } - - const sp<Layer>& clonedFrom = getClonedFrom(); - for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) { - const sp<Layer>& relative = relativeWeak.promote(); - if (clonedLayersMap.count(relative) > 0) { - auto& clonedRelative = clonedLayersMap.at(relative); - mDrawingState.zOrderRelatives.add(clonedRelative); - } - } - - // Check if the relativeLayer for the real layer is part of the cloned hierarchy. - // It's possible that the layer it's relative to is outside the requested cloned hierarchy. - // In that case, we treat the layer as if the relativeOf has been removed. This way, it will - // still traverse the children, but the layer with the missing relativeOf will not be shown - // on screen. - const sp<Layer>& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); - if (clonedLayersMap.count(relativeOf) > 0) { - const sp<Layer>& clonedRelativeOf = clonedLayersMap.at(relativeOf); - mDrawingState.zOrderRelativeOf = clonedRelativeOf; - } - - updateClonedInputInfo(clonedLayersMap); - - for (sp<Layer>& child : mDrawingChildren) { - child->updateClonedRelatives(clonedLayersMap); - } -} - -void Layer::addChildToDrawing(const sp<Layer>& layer) { - mDrawingChildren.add(layer); - layer->mDrawingParent = sp<Layer>::fromExisting(this); -} - -bool Layer::isInternalDisplayOverlay() const { - const State& s(mDrawingState); - if (s.flags & layer_state_t::eLayerSkipScreenshot) { - return true; - } - - sp<Layer> parent = mDrawingParent.promote(); - return parent && parent->isInternalDisplayOverlay(); -} - -void Layer::setClonedChild(const sp<Layer>& clonedChild) { - mClonedChild = clonedChild; - mHadClonedChild = true; - mFlinger->mLayerMirrorRoots.push_back(this); -} - -bool Layer::setDropInputMode(gui::DropInputMode mode) { - if (mDrawingState.dropInputMode == mode) { - return false; - } - mDrawingState.dropInputMode = mode; - return true; -} - -void Layer::cloneDrawingState(const Layer* from) { - mDrawingState = from->mDrawingState; - // Skip callback info since they are not applicable for cloned layers. - mDrawingState.releaseBufferListener = nullptr; - // TODO (b/238781169) currently broken for mirror layers because we do not - // track release fences for mirror layers composed on other displays - mDrawingState.callbackHandles = {}; -} - void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, const sp<GraphicBuffer>& buffer, uint64_t framenumber, const sp<Fence>& releaseFence) { - if (!listener) { + if (!listener && !mBufferReleaseChannel) { return; } + SFTRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64, getDebugName(), framenumber); + + ReleaseCallbackId callbackId{buffer->getId(), framenumber}; + const sp<Fence>& fence = releaseFence ? releaseFence : Fence::NO_FENCE; uint32_t currentMaxAcquiredBufferCount = mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); - listener->onReleaseBuffer({buffer->getId(), framenumber}, - releaseFence ? releaseFence : Fence::NO_FENCE, - currentMaxAcquiredBufferCount); + + if (listener) { + listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount); + } + + if (mBufferReleaseChannel) { + mBufferReleaseChannel->writeReleaseFence(callbackId, fence, currentMaxAcquiredBufferCount); + } } sp<CallbackHandle> Layer::findCallbackHandle() { @@ -2655,6 +806,7 @@ void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) { for (const auto& handle : mDrawingState.callbackHandles) { + handle->bufferReleaseChannel = mBufferReleaseChannel; handle->transformHint = mTransformHint; handle->dequeueReadyTime = dequeueReadyTime; handle->currentMaxAcquiredBufferCount = @@ -2674,17 +826,9 @@ void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) { mDrawingState.callbackHandles = {}; } -bool Layer::willPresentCurrentTransaction() const { - // Returns true if the most recent Transaction applied to CurrentState will be presented. - return (getSidebandStreamChanged() || getAutoRefresh() || - (mDrawingState.modified && - (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr))); -} - bool Layer::setTransform(uint32_t transform) { if (mDrawingState.bufferTransform == transform) return false; mDrawingState.bufferTransform = transform; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -2693,108 +837,7 @@ bool Layer::setTransformToDisplayInverse(bool transformToDisplayInverse) { if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false; mDrawingState.sequence++; mDrawingState.transformToDisplayInverse = transformToDisplayInverse; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setBufferCrop(const Rect& bufferCrop) { - if (mDrawingState.bufferCrop == bufferCrop) return false; - - mDrawingState.sequence++; - mDrawingState.bufferCrop = bufferCrop; - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setDestinationFrame(const Rect& destinationFrame) { - if (mDrawingState.destinationFrame == destinationFrame) return false; - - mDrawingState.sequence++; - mDrawingState.destinationFrame = destinationFrame; - - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -// Translate destination frame into scale and position. If a destination frame is not set, use the -// provided scale and position -bool Layer::updateGeometry() { - if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) || - mDrawingState.destinationFrame.isEmpty()) { - // If destination frame is not set, use the requested transform set via - // Layer::setPosition and Layer::setMatrix. - return assignTransform(&mDrawingState.transform, mRequestedTransform); - } - - Rect destRect = mDrawingState.destinationFrame; - int32_t destW = destRect.width(); - int32_t destH = destRect.height(); - if (destRect.left < 0) { - destRect.left = 0; - destRect.right = destW; - } - if (destRect.top < 0) { - destRect.top = 0; - destRect.bottom = destH; - } - - if (!mDrawingState.buffer) { - ui::Transform t; - t.set(destRect.left, destRect.top); - return assignTransform(&mDrawingState.transform, t); - } - - uint32_t bufferWidth = mDrawingState.buffer->getWidth(); - uint32_t bufferHeight = mDrawingState.buffer->getHeight(); - // Undo any transformations on the buffer. - if (mDrawingState.bufferTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags(); - if (mDrawingState.transformToDisplayInverse) { - if (invTransform & ui::Transform::ROT_90) { - std::swap(bufferWidth, bufferHeight); - } - } - - float sx = destW / static_cast<float>(bufferWidth); - float sy = destH / static_cast<float>(bufferHeight); - ui::Transform t; - t.set(sx, 0, 0, sy); - t.set(destRect.left, destRect.top); - return assignTransform(&mDrawingState.transform, t); -} - -bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) { - if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy && - mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) { - return false; - } - - mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy); - - mDrawingState.sequence++; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - - return true; -} - -bool Layer::setPosition(float x, float y) { - if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) { - return false; - } - - mRequestedTransform.set(x, y); - - mDrawingState.sequence++; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); - return true; } @@ -2861,7 +904,6 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, mDrawingState.isAutoTimestamp = isAutoTimestamp; mDrawingState.latchedVsyncId = info.vsyncId; mDrawingState.useVsyncIdForRefreshRateSelection = info.useForRefreshRateSelection; - mDrawingState.modified = true; if (!buffer) { resetDrawingStateBufferInfo(); setTransactionFlags(eTransactionNeeded); @@ -3009,7 +1051,6 @@ void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProp bool Layer::setDataspace(ui::Dataspace dataspace) { if (mDrawingState.dataspace == dataspace) return false; mDrawingState.dataspace = dataspace; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -3020,7 +1061,6 @@ bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRa return false; mDrawingState.currentHdrSdrRatio = currentBufferRatio; mDrawingState.desiredHdrSdrRatio = desiredRatio; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -3028,40 +1068,6 @@ bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRa bool Layer::setDesiredHdrHeadroom(float desiredRatio) { if (mDrawingState.desiredHdrSdrRatio == desiredRatio) return false; mDrawingState.desiredHdrSdrRatio = desiredRatio; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setCachingHint(gui::CachingHint cachingHint) { - if (mDrawingState.cachingHint == cachingHint) return false; - mDrawingState.cachingHint = cachingHint; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) { - if (mDrawingState.hdrMetadata == hdrMetadata) return false; - mDrawingState.hdrMetadata = hdrMetadata; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) { - if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false; - mDrawingState.surfaceDamageRegion = surfaceDamage; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - setIsSmallDirty(surfaceDamage, getTransform()); - return true; -} - -bool Layer::setApi(int32_t api) { - if (mDrawingState.api == api) return false; - mDrawingState.api = api; - mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); return true; } @@ -3077,7 +1083,6 @@ bool Layer::setSidebandStream(const sp<NativeHandle>& sidebandStream, const Fram } mDrawingState.sidebandStream = sidebandStream; - mDrawingState.modified = true; if (sidebandStream != nullptr && mDrawingState.buffer != nullptr) { releasePreviousBuffer(); resetDrawingStateBufferInfo(); @@ -3174,14 +1179,6 @@ Rect Layer::getBufferSize(const State& /*s*/) const { return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight)); } -FloatRect Layer::computeSourceBounds(const FloatRect& parentBounds) const { - if (mBufferInfo.mBuffer == nullptr) { - return parentBounds; - } - - return getBufferSize(getDrawingState()).toFloatRect(); -} - bool Layer::fenceHasSignaled() const { if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) { return true; @@ -3203,37 +1200,21 @@ void Layer::onPreComposition(nsecs_t refreshStartTime) { } } -void Layer::setAutoRefresh(bool autoRefresh) { - mDrawingState.autoRefresh = autoRefresh; -} - bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) { - // We need to update the sideband stream if the layer has both a buffer and a sideband stream. - auto* snapshot = editLayerSnapshot(); - snapshot->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get(); - if (mSidebandStreamChanged.exchange(false)) { const State& s(getDrawingState()); // mSidebandStreamChanged was true mSidebandStream = s.sidebandStream; - snapshot->sidebandStream = mSidebandStream; if (mSidebandStream != nullptr) { setTransactionFlags(eTransactionNeeded); mFlinger->setTransactionFlags(eTraversalNeeded); } recomputeVisibleRegions = true; - return true; } return false; } -bool Layer::hasFrameUpdate() const { - const State& c(getDrawingState()); - return (mDrawingStateModified || mDrawingState.modified) && - (c.buffer != nullptr || c.bgColorLayer != nullptr); -} - void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) { const State& s(getDrawingState()); @@ -3280,8 +1261,6 @@ void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) { mFlinger->getTransactionCallbackInvoker() .addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles); mDrawingState.callbackHandles = remainingHandles; - - mDrawingStateModified = false; } void Layer::gatherBufferInfo() { @@ -3305,7 +1284,6 @@ void Layer::gatherBufferInfo() { mBufferInfo.mFrameLatencyNeeded = true; mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime; mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence); - mBufferInfo.mFence = mDrawingState.acquireFence; mBufferInfo.mTransform = mDrawingState.bufferTransform; auto lastDataspace = mBufferInfo.mDataspace; mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace); @@ -3353,10 +1331,6 @@ void Layer::gatherBufferInfo() { mFlinger->mHdrLayerInfoChanged = true; } mBufferInfo.mCrop = computeBufferCrop(mDrawingState); - mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; - mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion; - mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata; - mBufferInfo.mApi = mDrawingState.api; mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse; } @@ -3372,13 +1346,6 @@ Rect Layer::computeBufferCrop(const State& s) { } } -sp<Layer> Layer::createClone() { - surfaceflinger::LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, - LayerMetadata()); - sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args); - return layer; -} - void Layer::decrementPendingBufferCount() { int32_t pendingBuffers = --mPendingBufferTransactions; tracePendingBufferCount(pendingBuffers); @@ -3388,294 +1355,6 @@ void Layer::tracePendingBufferCount(int32_t pendingBuffers) { SFTRACE_INT(mBlastTransactionName.c_str(), pendingBuffers); } -/* - * We don't want to send the layer's transform to input, but rather the - * parent's transform. This is because Layer's transform is - * information about how the buffer is placed on screen. The parent's - * transform makes more sense to send since it's information about how the - * layer is placed on screen. This transform is used by input to determine - * how to go from screen space back to window space. - */ -ui::Transform Layer::getInputTransform() const { - if (!hasBufferOrSidebandStream()) { - return getTransform(); - } - sp<Layer> parent = mDrawingParent.promote(); - if (parent == nullptr) { - return ui::Transform(); - } - - return parent->getTransform(); -} - -/** - * Returns the bounds used to fill the input frame and the touchable region. - * - * Similar to getInputTransform, we need to update the bounds to include the transform. - * This is because bounds don't include the buffer transform, where the input assumes - * that's already included. - */ -std::pair<FloatRect, bool> Layer::getInputBounds(bool fillParentBounds) const { - Rect croppedBufferSize = getCroppedBufferSize(getDrawingState()); - FloatRect inputBounds = croppedBufferSize.toFloatRect(); - if (hasBufferOrSidebandStream() && croppedBufferSize.isValid() && - mDrawingState.transform.getType() != ui::Transform::IDENTITY) { - inputBounds = mDrawingState.transform.transform(inputBounds); - } - - bool inputBoundsValid = croppedBufferSize.isValid(); - if (!inputBoundsValid) { - /** - * Input bounds are based on the layer crop or buffer size. But if we are using - * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then - * we can use the parent bounds as the input bounds if the layer does not have buffer - * or a crop. We want to unify this logic but because of compat reasons we cannot always - * use the parent bounds. A layer without a buffer can get input. So when a window is - * initially added, its touchable region can fill its parent layer bounds and that can - * have negative consequences. - */ - inputBounds = fillParentBounds ? mBounds : FloatRect{}; - } - - // Clamp surface inset to the input bounds. - const float inset = static_cast<float>(mDrawingState.inputInfo.surfaceInset); - const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f); - const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f); - - // Apply the insets to the input bounds. - inputBounds.left += xSurfaceInset; - inputBounds.top += ySurfaceInset; - inputBounds.right -= xSurfaceInset; - inputBounds.bottom -= ySurfaceInset; - - return {inputBounds, inputBoundsValid}; -} - -bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const { - const uint64_t requiredFlags = layer_state_t::eBufferChanged; - - const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged | - layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged | - layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged | - layer_state_t::eLayerStackChanged | layer_state_t::eReparent | - (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed() - ? 0 - : layer_state_t::eAutoRefreshChanged); - - if ((s.what & requiredFlags) != requiredFlags) { - SFTRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__, - (s.what | requiredFlags) & ~s.what); - return false; - } - - if (s.what & deniedFlags) { - SFTRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__, - s.what & deniedFlags); - return false; - } - - if (s.what & layer_state_t::ePositionChanged) { - if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) { - SFTRACE_FORMAT_INSTANT("%s: false [ePositionChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eAlphaChanged) { - if (mDrawingState.color.a != s.color.a) { - SFTRACE_FORMAT_INSTANT("%s: false [eAlphaChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eColorTransformChanged) { - if (mDrawingState.colorTransform != s.colorTransform) { - SFTRACE_FORMAT_INSTANT("%s: false [eColorTransformChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBackgroundColorChanged) { - if (mDrawingState.bgColorLayer || s.bgColor.a != 0) { - SFTRACE_FORMAT_INSTANT("%s: false [eBackgroundColorChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eMatrixChanged) { - if (mRequestedTransform.dsdx() != s.matrix.dsdx || - mRequestedTransform.dtdy() != s.matrix.dtdy || - mRequestedTransform.dtdx() != s.matrix.dtdx || - mRequestedTransform.dsdy() != s.matrix.dsdy) { - SFTRACE_FORMAT_INSTANT("%s: false [eMatrixChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eCornerRadiusChanged) { - if (mDrawingState.cornerRadius != s.cornerRadius) { - SFTRACE_FORMAT_INSTANT("%s: false [eCornerRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) { - if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) { - SFTRACE_FORMAT_INSTANT("%s: false [eBackgroundBlurRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBufferTransformChanged) { - if (mDrawingState.bufferTransform != s.bufferTransform) { - SFTRACE_FORMAT_INSTANT("%s: false [eBufferTransformChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eTransformToDisplayInverseChanged) { - if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) { - SFTRACE_FORMAT_INSTANT("%s: false [eTransformToDisplayInverseChanged changed]", - __func__); - return false; - } - } - - if (s.what & layer_state_t::eCropChanged) { - if (mDrawingState.crop != s.crop) { - SFTRACE_FORMAT_INSTANT("%s: false [eCropChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDataspaceChanged) { - if (mDrawingState.dataspace != s.dataspace) { - SFTRACE_FORMAT_INSTANT("%s: false [eDataspaceChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eHdrMetadataChanged) { - if (mDrawingState.hdrMetadata != s.hdrMetadata) { - SFTRACE_FORMAT_INSTANT("%s: false [eHdrMetadataChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eSidebandStreamChanged) { - if (mDrawingState.sidebandStream != s.sidebandStream) { - SFTRACE_FORMAT_INSTANT("%s: false [eSidebandStreamChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eColorSpaceAgnosticChanged) { - if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) { - SFTRACE_FORMAT_INSTANT("%s: false [eColorSpaceAgnosticChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eShadowRadiusChanged) { - if (mDrawingState.shadowRadius != s.shadowRadius) { - SFTRACE_FORMAT_INSTANT("%s: false [eShadowRadiusChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eFixedTransformHintChanged) { - if (mDrawingState.fixedTransformHint != s.fixedTransformHint) { - SFTRACE_FORMAT_INSTANT("%s: false [eFixedTransformHintChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eTrustedOverlayChanged) { - if (mDrawingState.isTrustedOverlay != (s.trustedOverlay == gui::TrustedOverlay::ENABLED)) { - SFTRACE_FORMAT_INSTANT("%s: false [eTrustedOverlayChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eStretchChanged) { - StretchEffect temp = s.stretchEffect; - temp.sanitize(); - if (mDrawingState.stretchEffect != temp) { - SFTRACE_FORMAT_INSTANT("%s: false [eStretchChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eBufferCropChanged) { - if (mDrawingState.bufferCrop != s.bufferCrop) { - SFTRACE_FORMAT_INSTANT("%s: false [eBufferCropChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDestinationFrameChanged) { - if (mDrawingState.destinationFrame != s.destinationFrame) { - SFTRACE_FORMAT_INSTANT("%s: false [eDestinationFrameChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDimmingEnabledChanged) { - if (mDrawingState.dimmingEnabled != s.dimmingEnabled) { - SFTRACE_FORMAT_INSTANT("%s: false [eDimmingEnabledChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) { - if (mDrawingState.currentHdrSdrRatio != s.currentHdrSdrRatio || - mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) { - SFTRACE_FORMAT_INSTANT("%s: false [eExtendedRangeBrightnessChanged changed]", __func__); - return false; - } - } - - if (s.what & layer_state_t::eDesiredHdrHeadroomChanged) { - if (mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) { - SFTRACE_FORMAT_INSTANT("%s: false [eDesiredHdrHeadroomChanged changed]", __func__); - return false; - } - } - - return true; -} - -sp<LayerFE> Layer::getCompositionEngineLayerFE() const { - // There's no need to get a CE Layer if the layer isn't going to draw anything. - return hasSomethingToDraw() ? mLegacyLayerFE : nullptr; -} - -const LayerSnapshot* Layer::getLayerSnapshot() const { - return mSnapshot.get(); -} - -LayerSnapshot* Layer::editLayerSnapshot() { - return mSnapshot.get(); -} - -std::unique_ptr<frontend::LayerSnapshot> Layer::stealLayerSnapshot() { - return std::move(mSnapshot); -} - -void Layer::updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot) { - mSnapshot = std::move(snapshot); -} - -const compositionengine::LayerFECompositionState* Layer::getCompositionState() const { - return mSnapshot.get(); -} - -sp<LayerFE> Layer::copyCompositionEngineLayerFE() const { - auto result = mFlinger->getFactory().createLayerFE(mName, this); - result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot); - return result; -} - sp<LayerFE> Layer::getCompositionEngineLayerFE( const frontend::LayerHierarchy::TraversalPath& path) { for (auto& [p, layerFE] : mLayerFEs) { @@ -3688,55 +1367,6 @@ sp<LayerFE> Layer::getCompositionEngineLayerFE( return layerFE; } -void Layer::useSurfaceDamage() { - if (mFlinger->mForceFullDamage) { - surfaceDamageRegion = Region::INVALID_REGION; - } else { - surfaceDamageRegion = mBufferInfo.mSurfaceDamage; - } -} - -void Layer::useEmptyDamage() { - surfaceDamageRegion.clear(); -} - -bool Layer::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 (!hasSomethingToDraw()) { - return false; - } - - // if the layer has the opaque flag, then we're always opaque - if ((s.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque) { - return true; - } - - // If the buffer has no alpha channel, then we are opaque - if (hasBufferOrSidebandStream() && LayerSnapshot::isOpaqueFormat(getPixelFormat())) { - return true; - } - - // Lastly consider the layer opaque if drawing a color with alpha == 1.0 - return fillsColor() && getAlpha() == 1.0_hf; -} - -bool Layer::canReceiveInput() const { - return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f); -} - -bool Layer::isVisible() const { - if (!hasSomethingToDraw()) { - return false; - } - - if (isHiddenByPolicy()) { - return false; - } - - return getAlpha() > 0.0f || hasBlur(); -} - void Layer::onCompositionPresented(const DisplayDevice* display, const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, @@ -3825,15 +1455,6 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mBufferInfo.mFrameLatencyNeeded = false; } -bool Layer::willReleaseBufferOnLatch() const { - return !mDrawingState.buffer && mBufferInfo.mBuffer; -} - -bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { - const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr; - return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly); -} - bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) { SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(), getDrawingState().frameNumber); @@ -3855,7 +1476,6 @@ bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bo // Capture the old state of the layer for comparisons later BufferInfo oldBufferInfo = mBufferInfo; - const bool oldOpacity = isOpaque(mDrawingState); mPreviousFrameNumber = mCurrentFrameNumber; mCurrentFrameNumber = mDrawingState.frameNumber; gatherBufferInfo(); @@ -3880,7 +1500,6 @@ bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bo if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) || (mBufferInfo.mTransform != oldBufferInfo.mTransform) || - (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) || (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) { recomputeVisibleRegions = true; } @@ -3893,70 +1512,13 @@ bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bo recomputeVisibleRegions = true; } } - - if (oldOpacity != isOpaque(mDrawingState)) { - recomputeVisibleRegions = true; - } - return true; } -bool Layer::hasReadyFrame() const { - return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh(); -} - -bool Layer::isProtected() const { - return (mBufferInfo.mBuffer != nullptr) && - (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED); -} - -void Layer::latchAndReleaseBuffer() { - if (hasReadyFrame()) { - bool ignored = false; - latchBuffer(ignored, systemTime()); - } - releasePendingBuffer(systemTime()); -} - -PixelFormat Layer::getPixelFormat() const { - return mBufferInfo.mPixelFormat; -} - bool Layer::getTransformToDisplayInverse() const { return mBufferInfo.mTransformToDisplayInverse; } -Rect Layer::getBufferCrop() const { - // this is the crop rectangle that applies to the buffer - // itself (as opposed to the window) - if (!mBufferInfo.mCrop.isEmpty()) { - // if the buffer crop is defined, we use that - return mBufferInfo.mCrop; - } else if (mBufferInfo.mBuffer != nullptr) { - // otherwise we use the whole buffer - return mBufferInfo.mBuffer->getBounds(); - } else { - // if we don't have a buffer yet, we use an empty/invalid crop - return Rect(); - } -} - -uint32_t Layer::getBufferTransform() const { - return mBufferInfo.mTransform; -} - -ui::Dataspace Layer::getDataSpace() const { - return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace; -} - -bool Layer::isFrontBuffered() const { - if (mBufferInfo.mBuffer == nullptr) { - return false; - } - - return mBufferInfo.mBuffer->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER; -} - ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) { ui::Dataspace updatedDataspace = dataspace; // translate legacy dataspaces to modern dataspaces @@ -3992,84 +1554,6 @@ sp<GraphicBuffer> Layer::getBuffer() const { return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr; } -const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const { - return mBufferInfo.mBuffer; -} - -bool Layer::setColor(const half3& color) { - if (mDrawingState.color.rgb == color) { - return false; - } - - mDrawingState.sequence++; - mDrawingState.color.rgb = color; - mDrawingState.modified = true; - setTransactionFlags(eTransactionNeeded); - return true; -} - -bool Layer::fillsColor() const { - return !hasBufferOrSidebandStream() && mDrawingState.color.r >= 0.0_hf && - mDrawingState.color.g >= 0.0_hf && mDrawingState.color.b >= 0.0_hf; -} - -bool Layer::hasBlur() const { - return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0; -} - -void Layer::updateSnapshot(bool updateGeometry) { - if (!getCompositionEngineLayerFE()) { - return; - } - - auto* snapshot = editLayerSnapshot(); - if (updateGeometry) { - prepareBasicGeometryCompositionState(); - prepareGeometryCompositionState(); - snapshot->roundedCorner = getRoundedCornerState(); - snapshot->transformedBounds = mScreenBounds; - if (mEffectiveShadowRadius > 0.f) { - snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings; - - // Note: this preserves existing behavior of shadowing the entire layer and not cropping - // it if transparent regions are present. This may not be necessary since shadows are - // typically cast by layers without transparent regions. - snapshot->shadowSettings.boundaries = mBounds; - - const float casterAlpha = snapshot->alpha; - const bool casterIsOpaque = - ((mBufferInfo.mBuffer != nullptr) && isOpaque(mDrawingState)); - - // If the casting layer is translucent, we need to fill in the shadow underneath the - // layer. Otherwise the generated shadow will only be shown around the casting layer. - snapshot->shadowSettings.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f); - snapshot->shadowSettings.ambientColor *= casterAlpha; - snapshot->shadowSettings.spotColor *= casterAlpha; - } - snapshot->shadowSettings.length = mEffectiveShadowRadius; - } - snapshot->contentOpaque = isOpaque(mDrawingState); - snapshot->layerOpaqueFlagSet = - (mDrawingState.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque; - sp<Layer> p = mDrawingParent.promote(); - if (p != nullptr) { - snapshot->parentTransform = p->getTransform(); - } else { - snapshot->parentTransform.reset(); - } - snapshot->bufferSize = getBufferSize(mDrawingState); - snapshot->externalTexture = mBufferInfo.mBuffer; - snapshot->hasReadyFrame = hasReadyFrame(); - preparePerFrameCompositionState(); -} - -void Layer::updateChildrenSnapshots(bool updateGeometry) { - for (const sp<Layer>& child : mDrawingChildren) { - child->updateSnapshot(updateGeometry); - child->updateChildrenSnapshots(updateGeometry); - } -} - bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, TrustedPresentationListener const& listener) { bool hadTrustedPresentationListener = hasTrustedPresentationListener(); @@ -4093,39 +1577,41 @@ bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thre return haveTrustedPresentationListener; } +void Layer::setBufferReleaseChannel( + const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel) { + mBufferReleaseChannel = channel; +} + void Layer::updateLastLatchTime(nsecs_t latchTime) { mLastLatchTime = latchTime; } -void Layer::setIsSmallDirty(const Region& damageRegion, - const ui::Transform& layerToDisplayTransform) { - mSmallDirty = false; +void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) { if (!mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) { + snapshot->isSmallDirty = false; return; } if (mWindowType != WindowInfo::Type::APPLICATION && mWindowType != WindowInfo::Type::BASE_APPLICATION) { + snapshot->isSmallDirty = false; return; } - Rect bounds = damageRegion.getBounds(); + Rect bounds = snapshot->surfaceDamage.getBounds(); if (!bounds.isValid()) { + snapshot->isSmallDirty = false; return; } // Transform to screen space. - bounds = layerToDisplayTransform.transform(bounds); + bounds = snapshot->localTransform.transform(bounds); // If the damage region is a small dirty, this could give the hint for the layer history that // it could suppress the heuristic rate when calculating. - mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId, - bounds.getWidth() * bounds.getHeight()); -} - -void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) { - setIsSmallDirty(snapshot->surfaceDamage, snapshot->localTransform); - snapshot->isSmallDirty = mSmallDirty; + snapshot->isSmallDirty = + mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId, + bounds.getWidth() * bounds.getHeight()); } } // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index f6eed6332b..9bc557e917 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -43,9 +43,7 @@ #include <scheduler/Fps.h> #include <scheduler/Seamlessness.h> -#include <chrono> #include <cstdint> -#include <list> #include <optional> #include <vector> @@ -56,7 +54,6 @@ #include "LayerVector.h" #include "Scheduler/LayerInfo.h" #include "SurfaceFlinger.h" -#include "Tracing/LayerTracing.h" #include "TransactionCallbackInvoker.h" using namespace android::surfaceflinger; @@ -91,48 +88,15 @@ public: // Windows that are not in focus, but voted for a specific mode ID. static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2; - enum { // flags for doTransaction() - eDontUpdateGeometryState = 0x00000001, - eVisibleRegion = 0x00000002, - eInputInfoChanged = 0x00000004 - }; - - struct Geometry { - uint32_t w; - uint32_t h; - ui::Transform transform; - - inline bool operator==(const Geometry& rhs) const { - return (w == rhs.w && h == rhs.h) && (transform.tx() == rhs.transform.tx()) && - (transform.ty() == rhs.transform.ty()); - } - inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); } - }; - using FrameRate = scheduler::LayerInfo::FrameRate; using FrameRateCompatibility = scheduler::FrameRateCompatibility; using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy; struct State { - int32_t z; - ui::LayerStack layerStack; - uint32_t flags; int32_t sequence; // changes when visible regions can change - bool modified; // Crop is expressed in layer space coordinate. Rect crop; LayerMetadata metadata; - // If non-null, a Surface this Surface's Z-order is interpreted relative to. - wp<Layer> zOrderRelativeOf; - bool isRelativeOf{false}; - - // A list of surfaces whose Z-order is interpreted relative to ours. - SortedVector<wp<Layer>> zOrderRelatives; - half4 color; - float cornerRadius; - int backgroundBlurRadius; - gui::WindowInfo inputInfo; - wp<Layer> touchableRegionCrop; ui::Dataspace dataspace; @@ -154,52 +118,18 @@ public: std::shared_ptr<renderengine::ExternalTexture> buffer; sp<Fence> acquireFence; std::shared_ptr<FenceTime> acquireFenceTime; - HdrMetadata hdrMetadata; - Region surfaceDamageRegion; - int32_t api; sp<NativeHandle> sidebandStream; mat4 colorTransform; - bool hasColorTransform; - // pointer to background color layer that, if set, appears below the buffer state layer - // and the buffer state layer's children. Z order will be set to - // INT_MIN - sp<Layer> bgColorLayer; // The deque of callback handles for this frame. The back of the deque contains the most // recent callback handle. std::deque<sp<CallbackHandle>> callbackHandles; - bool colorSpaceAgnostic; nsecs_t desiredPresentTime = 0; bool isAutoTimestamp = true; - // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will - // be rendered around the layer. - float shadowRadius; - - // Layer regions that are made of custom materials, like frosted glass - std::vector<BlurRegion> blurRegions; - - // Priority of the layer assigned by Window Manager. - int32_t frameRateSelectionPriority; - - // Default frame rate compatibility used to set the layer refresh rate votetype. - FrameRateCompatibility defaultFrameRateCompatibility; - FrameRate frameRate; - // The combined frame rate of parents / children of this layer FrameRate frameRateForLayerTree; - FrameRateSelectionStrategy frameRateSelectionStrategy; - - // Set by window manager indicating the layer and all its children are - // in a different orientation than the display. The hint suggests that - // the graphic producers should receive a transform hint as if the - // display was in this orientation. When the display changes to match - // the layer orientation, the graphic producer may not need to allocate - // a buffer of a different size. ui::Transform::ROT_INVALID means the - // a fixed transform hint is not set. - ui::Transform::RotationFlags fixedTransformHint; - // The vsync info that was used to start the transaction FrameTimelineInfo frameTimelineInfo; @@ -219,21 +149,12 @@ public: // An arbitrary threshold for the number of BufferlessSurfaceFrames in the state. Used to // trigger a warning if the number of SurfaceFrames crosses the threshold. static constexpr uint32_t kStateSurfaceFramesThreshold = 25; - - // Stretch effect to apply to this layer - StretchEffect stretchEffect; - - // Whether or not this layer is a trusted overlay for input - bool isTrustedOverlay; Rect bufferCrop; Rect destinationFrame; sp<IBinder> releaseBufferEndpoint; - gui::DropInputMode dropInputMode; bool autoRefresh = false; - bool dimmingEnabled = true; float currentHdrSdrRatio = 1.f; float desiredHdrSdrRatio = -1.f; - gui::CachingHint cachingHint = gui::CachingHint::Enabled; int64_t latchedVsyncId = 0; bool useVsyncIdForRefreshRateSelection = false; }; @@ -244,69 +165,15 @@ public: static bool isLayerFocusedBasedOnPriority(int32_t priority); static void miniDumpHeader(std::string& result); - // Provide unique string for each class type in the Layer hierarchy - virtual const char* getType() const { return "Layer"; } - - // true if this layer is visible, false otherwise - virtual bool isVisible() const; - - virtual sp<Layer> createClone(); - - // 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); - // This second set of geometry attributes are controlled by // setGeometryAppliesWithResize, and their default mode is to be // immediate. If setGeometryAppliesWithResize is specified // while a resize is pending, then update of these attributes will // be delayed until the resize completes. - // setPosition operates in parent buffer space (pre parent-transform) or display - // space for top-level layers. - bool setPosition(float x, float y); // Buffer space bool setCrop(const Rect& crop); - // TODO(b/38182121): Could we eliminate the various latching modes by - // using the layer hierarchy? - // ----------------------------------------------------------------------- - virtual bool setLayer(int32_t z); - virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ); - - virtual bool setAlpha(float alpha); - bool setColor(const half3& /*color*/); - - // 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); - // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which - // is specified in pixels. - virtual bool setBackgroundBlurRadius(int backgroundBlurRadius); - virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions); - bool setTransparentRegionHint(const Region& transparent); - virtual bool setTrustedOverlay(bool); - virtual bool setFlags(uint32_t flags, uint32_t mask); - virtual bool setLayerStack(ui::LayerStack); - virtual ui::LayerStack getLayerStack( - LayerVector::StateSet state = LayerVector::StateSet::Drawing) const; - - virtual bool setMetadata(const LayerMetadata& data); - virtual void setChildrenDrawingParent(const sp<Layer>&); - virtual bool reparent(const sp<IBinder>& newParentHandle) REQUIRES(mFlinger->mStateLock); - virtual bool setColorTransform(const mat4& matrix); - virtual mat4 getColorTransform() const; - virtual bool hasColorTransform() const; - virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; } - virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; } - float getDesiredHdrSdrRatio() const { return getDrawingState().desiredHdrSdrRatio; } - float getCurrentHdrSdrRatio() const { return getDrawingState().currentHdrSdrRatio; } - gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; } - bool setTransform(uint32_t /*transform*/); bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/); bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */, @@ -317,116 +184,24 @@ public: bool setDataspace(ui::Dataspace /*dataspace*/); bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio); bool setDesiredHdrHeadroom(float desiredRatio); - bool setCachingHint(gui::CachingHint cachingHint); - bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/); - bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/); - bool setApi(int32_t /*api*/); bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/, const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */, gui::GameMode gameMode); bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/, bool willPresent); - virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace) - REQUIRES(mFlinger->mStateLock); - virtual bool setColorSpaceAgnostic(const bool agnostic); - virtual bool setDimmingEnabled(const bool dimmingEnabled); - virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint); - void setAutoRefresh(bool /* autoRefresh */); - bool setDropInputMode(gui::DropInputMode); - ui::Dataspace getDataSpace() const; - - virtual bool isFrontBuffered() const; - - virtual sp<LayerFE> getCompositionEngineLayerFE() const; - virtual sp<LayerFE> copyCompositionEngineLayerFE() const; sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); - sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); - - const frontend::LayerSnapshot* getLayerSnapshot() const; - frontend::LayerSnapshot* editLayerSnapshot(); - std::unique_ptr<frontend::LayerSnapshot> stealLayerSnapshot(); - void updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot); // 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(); Region getVisibleRegion(const DisplayDevice*) const; void updateLastLatchTime(nsecs_t latchtime); - /* - * isOpaque - true if this surface is opaque - * - * This takes into account the buffer format (i.e. whether or not the - * pixel format includes an alpha channel) and the "opaque" flag set - * on the layer. It does not examine the current plane alpha value. - */ - bool isOpaque(const Layer::State&) const; - - /* - * Returns whether this layer can receive input. - */ - bool canReceiveInput() const; - - /* - * Whether or not the layer should be considered visible for input calculations. - */ - virtual bool isVisibleForInput() const { - // For compatibility reasons we let layers which can receive input - // receive input before they have actually submitted a buffer. Because - // of this we use canReceiveInput instead of isVisible to check the - // policy-visibility, ignoring the buffer state. However for layers with - // hasInputInfo()==false we can use the real visibility state. - // We are just using these layers for occlusion detection in - // InputDispatcher, and obviously if they aren't visible they can't occlude - // anything. - return hasInputInfo() ? canReceiveInput() : isVisible(); - } - - /* - * isProtected - true if the layer may contain protected contents in the - * GRALLOC_USAGE_PROTECTED sense. - */ - bool isProtected() const; - - /* - * isFixedSize - true if content has a fixed size - */ - virtual bool isFixedSize() const { return true; } - - /* - * usesSourceCrop - true if content should use a source crop - */ - bool usesSourceCrop() const { return hasBufferOrSidebandStream(); } - - // Most layers aren't created from the main thread, and therefore need to - // grab the SF state lock to access HWC, but ContainerLayer does, so we need - // to avoid grabbing the lock again to avoid deadlock - virtual bool isCreatedFromMainThread() const { return false; } - - ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; } - Region getActiveTransparentRegion(const Layer::State& s) const { - return s.transparentRegionHint; - } Rect getCrop(const Layer::State& s) const { return s.crop; } - bool needsFiltering(const DisplayDevice*) const; - - // True if this layer requires filtering - // This method is distinct from needsFiltering() in how the filter - // requirement is computed. needsFiltering() compares displayFrame and crop, - // where as this method transforms the displayFrame to layer-stack space - // first. This method should be used if there is no physical display to - // project onto when taking screenshots, as the filtering requirements are - // different. - // If the parent transform needs to be undone when capturing the layer, then - // the inverse parent transform is also required. - bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const; // from graphics API static ui::Dataspace translateDataspace(ui::Dataspace dataspace); - void updateCloneBufferInfo(); uint64_t mPreviousFrameNumber = 0; void onCompositionPresented(const DisplayDevice*, @@ -443,46 +218,10 @@ 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. */ - bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/); - bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/, bool bgColorOnly); - /* - * Returns true if the currently presented buffer will be released when this layer state - * is latched. This will return false if there is no buffer currently presented. - */ - bool willReleaseBufferOnLatch() const; - - /* - * Calls latchBuffer if the buffer has a frame queued and then releases the buffer. - * This is used if the buffer is just latched and releases to free up the buffer - * and will not be shown on screen. - * Should only be called on the main thread. - */ - void latchAndReleaseBuffer(); - - /* - * returns the rectangle that crops the content of the layer and scales it - * to the layer's size. - */ - Rect getBufferCrop() const; - - /* - * Returns the transform applied to the buffer. - */ - uint32_t getBufferTransform() const; - sp<GraphicBuffer> getBuffer() const; - const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const; - - /* - * Returns if a frame is ready - */ - bool hasReadyFrame() const; - - virtual int32_t getQueuedFrameCount() const { return 0; } - /** * Returns active buffer size in the correct orientation. Buffer size is determined by undoing * any buffer transformations. Returns Rect::INVALID_RECT if the layer has no buffer or the @@ -490,33 +229,10 @@ public: */ Rect getBufferSize(const Layer::State&) const; - /** - * Returns the source bounds. If the bounds are not defined, it is inferred from the - * buffer size. Failing that, the bounds are determined from the passed in parent bounds. - * For the root layer, this is the display viewport size. - */ - FloatRect computeSourceBounds(const FloatRect& parentBounds) const; - virtual FrameRate getFrameRateForLayerTree() const; + FrameRate getFrameRateForLayerTree() const; bool getTransformToDisplayInverse() const; - // Returns how rounded corners should be drawn for this layer. - // A layer can override its parent's rounded corner settings if the parent's rounded - // corner crop does not intersect with its own rounded corner crop. - virtual frontend::RoundedCornerState getRoundedCornerState() const; - - bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); } - - PixelFormat getPixelFormat() const; - /** - * Return whether this layer needs an input info. We generate InputWindowHandles for all - * non-cursor buffered layers regardless of whether they have an InputChannel. This is to enable - * the InputDispatcher to do PID based occlusion detection. - */ - bool needsInputInfo() const { - return (hasInputInfo() || hasBufferOrSidebandStream()) && !mPotentialCursor; - } - // Implements RefBase. void onFirstRef() override; @@ -527,25 +243,18 @@ public: uint32_t mTransform{0}; ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN}; Rect mCrop; - uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE}; - Region mSurfaceDamage; - HdrMetadata mHdrMetadata; - int mApi; PixelFormat mPixelFormat{PIXEL_FORMAT_NONE}; bool mTransformToDisplayInverse{false}; - std::shared_ptr<renderengine::ExternalTexture> mBuffer; uint64_t mFrameNumber; sp<IBinder> mReleaseBufferEndpoint; - bool mFrameLatencyNeeded{false}; float mDesiredHdrSdrRatio = -1.f; }; BufferInfo mBufferInfo; + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseChannel; - // implements compositionengine::LayerFE - const compositionengine::LayerFECompositionState* getCompositionState() const; bool fenceHasSignaled() const; void onPreComposition(nsecs_t refreshStartTime); void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack, @@ -566,17 +275,6 @@ public: const char* getDebugName() const; - bool setShadowRadius(float shadowRadius); - - // Before color management is introduced, contents on Android have to be - // desaturated in order to match what they appears like visually. - // With color management, these contents will appear desaturated, thus - // needed to be saturated so that they match what they are designed for - // visually. - bool isLegacyDataSpace() const; - - uint32_t getTransactionFlags() const { return mTransactionFlags; } - static bool computeTrustedPresentationState(const FloatRect& bounds, const FloatRect& sourceBounds, const Region& coveredRegion, @@ -594,17 +292,6 @@ public: // Sets the masked bits. void setTransactionFlags(uint32_t mask); - // Clears and returns the masked bits. - uint32_t clearTransactionFlags(uint32_t mask); - - FloatRect getBounds(const Region& activeTransparentRegion) const; - FloatRect getBounds() const; - Rect getInputBoundsInDisplaySpace(const FloatRect& insetBounds, - const ui::Transform& displayTransform); - - // Compute bounds for the layer and cache the results. - void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius); - int32_t getSequence() const { return sequence; } // For tracing. @@ -615,159 +302,21 @@ public: // only used within a single layer. uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; } - /* - * isSecure - true if this surface is secure, that is if it prevents - * screenshots or VNC servers. A surface can be set to be secure by the - * application, being secure doesn't mean the surface has DRM contents. - */ - bool isSecure() const; - - /* - * isHiddenByPolicy - true if this layer has been forced invisible. - * just because this is false, doesn't mean isVisible() is true. - * 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; - - // True if the layer should be skipped in screenshots, screen recordings, - // and mirroring to external or virtual displays. - bool isInternalDisplayOverlay() const; - - ui::LayerFilter getOutputFilter() const { - return {getLayerStack(), isInternalDisplayOverlay()}; - } - - bool isRemovedFromCurrentState() const; - - perfetto::protos::LayerProto* writeToProto(perfetto::protos::LayersProto& layersProto, - uint32_t traceFlags); void writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto, ui::LayerStack layerStack); - // Write states that are modified by the main thread. This includes drawing - // state as well as buffer data. This should be called in the main or tracing - // thread. - void writeToProtoDrawingState(perfetto::protos::LayerProto* layerInfo); - // Write drawing or current state. If writing current state, the caller should hold the - // external mStateLock. If writing drawing state, this function should be called on the - // main or tracing thread. - void writeToProtoCommonState(perfetto::protos::LayerProto* layerInfo, LayerVector::StateSet, - uint32_t traceFlags = LayerTracing::TRACE_ALL); - - gui::WindowInfo::Type getWindowType() const { return mWindowType; } - - bool updateMirrorInfo(const std::deque<Layer*>& cloneRootsPendingUpdates); - - /* - * doTransaction - process the transaction. This is a good place to figure - * out which attributes of the surface have changed. - */ - virtual uint32_t doTransaction(uint32_t transactionFlags); - - /* - * Remove relative z for the layer if its relative parent is not part of the - * provided layer tree. - */ - void removeRelativeZ(const std::vector<Layer*>& layersInTree); - - /* - * Remove from current state and mark for removal. - */ - void removeFromCurrentState() REQUIRES(mFlinger->mStateLock); - - /* - * called with the state lock from a binder thread when the layer is - * removed from the current list to the pending removal list - */ - void onRemovedFromCurrentState() REQUIRES(mFlinger->mStateLock); - - /* - * Called when the layer is added back to the current state list. - */ - void addToCurrentState(); - inline const State& getDrawingState() const { return mDrawingState; } inline State& getDrawingState() { return mDrawingState; } void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const; void dumpFrameStats(std::string& result) const; - void dumpOffscreenDebugInfo(std::string& result) const; void clearFrameStats(); void logFrameStats(); void getFrameStats(FrameStats* outStats) const; void onDisconnect(); - ui::Transform getTransform() const; - bool isTransformValid() const; - - // 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; - int32_t getBackgroundBlurRadius() const; - bool drawShadows() const { return mEffectiveShadowRadius > 0.f; }; - - // Returns the transform hint set by Window Manager on the layer or one of its parents. - // This traverses the current state because the data is needed when creating - // the layer(off drawing thread) and the hint should be available before the producer - // is ready to acquire a buffer. - ui::Transform::RotationFlags getFixedTransformHint() const; - - /** - * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder - * which will not emit children who have relativeZOrder to another layer, this method - * just directly emits all children. It also emits them in no particular order. - * So this method is not suitable for graphical operations, as it doesn't represent - * the scene state, but it's also more efficient than traverseInZOrder and so useful for - * book-keeping. - */ - void traverse(LayerVector::StateSet, const LayerVector::Visitor&); - void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&); - void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&); - void traverseChildren(const LayerVector::Visitor&); - - /** - * Traverse only children in z order, ignoring relative layers that are not children of the - * parent. - */ - void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&); - - size_t getDescendantCount() const; - size_t getChildrenCount() const { return mDrawingChildren.size(); } - bool isHandleAlive() const { return mHandleAlive; } bool onHandleDestroyed() { return mHandleAlive = false; } - // ONLY CALL THIS FROM THE LAYER DTOR! - // See b/141111965. We need to add current children to offscreen layers in - // the layer dtor so as not to dangle layers. Since the layer has not - // committed its transaction when the layer is destroyed, we must add - // current children. This is safe in the dtor as we will no longer update - // the current state, but should not be called anywhere else! - LayerVector& getCurrentChildren() { return mCurrentChildren; } - - void addChild(const sp<Layer>&); - // Returns index if removed, or negative value otherwise - // for symmetry with Vector::remove - ssize_t removeChild(const sp<Layer>& layer); - sp<Layer> getParent() const { return mCurrentParent.promote(); } - - // Should be called with the surfaceflinger statelock held - bool isAtRoot() const { return mIsAtRoot; } - void setIsAtRoot(bool isAtRoot) { mIsAtRoot = isAtRoot; } - - bool hasParent() const { return getParent() != nullptr; } - Rect getScreenBounds(bool reduceTransparentRegion = true) const; - bool setChildLayer(const sp<Layer>& childLayer, int32_t z); - bool setChildRelativeLayer(const sp<Layer>& childLayer, - const sp<IBinder>& relativeToHandle, int32_t relativeZ); - - // Copy the current list of children to the drawing state. Called by - // SurfaceFlinger to complete a transaction. - void commitChildList(); - int32_t getZ(LayerVector::StateSet) const; - /** * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return * INVALID_RECT if the layer has no buffer and no crop. @@ -776,7 +325,6 @@ public: */ Rect getCroppedBufferSize(const Layer::State& s) const; - virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {} void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime, gui::GameMode gameMode); void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info, @@ -798,45 +346,16 @@ public: bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, TrustedPresentationListener const& listener); + void setBufferReleaseChannel( + const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel); // Creates a new handle each time, so we only expect // this to be called once. sp<IBinder> getHandle(); const std::string& getName() const { return mName; } - bool getPremultipledAlpha() const; - void setInputInfo(const gui::WindowInfo& info); - - struct InputDisplayArgs { - const ui::Transform* transform = nullptr; - bool isSecure = false; - }; - gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs); - - /** - * Returns whether this layer has an explicitly set input-info. - */ - bool hasInputInfo() const; virtual uid_t getOwnerUid() const { return mOwnerUid; } - pid_t getOwnerPid() { return mOwnerPid; } - - int32_t getOwnerAppId() { return mOwnerAppId; } - - // This layer is not a clone, but it's the parent to the cloned hierarchy. The - // variable mClonedChild represents the top layer that will be cloned so this - // layer will be the parent of mClonedChild. - // The layers in the cloned hierarchy will match the lifetime of the real layers. That is - // if the real layer is destroyed, then the clone layer will also be destroyed. - sp<Layer> mClonedChild; - bool mHadClonedChild = false; - void setClonedChild(const sp<Layer>& mClonedChild); - - mutable bool contentDirty{false}; - Region surfaceDamageRegion; - - // True when the surfaceDamageRegion is recognized as a small area update. - bool mSmallDirty{false}; // Used to check if mUsedVsyncIdForRefreshRateSelection should be expired when it stop updating. nsecs_t mMaxTimeForUseVsyncId = 0; // True when DrawState.useVsyncIdForRefreshRateSelection previously set to true during updating @@ -848,42 +367,10 @@ public: // the same. const int32_t sequence; - bool mPendingHWCDestroy{false}; - - bool backpressureEnabled() const { - return mDrawingState.flags & layer_state_t::eEnableBackpressure; - } - - bool setStretchEffect(const StretchEffect& effect); - StretchEffect getStretchEffect() const; - - bool setBufferCrop(const Rect& /* bufferCrop */); - bool setDestinationFrame(const Rect& /* destinationFrame */); // See mPendingBufferTransactions void decrementPendingBufferCount(); std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; } std::string getPendingBufferCounterName() { return mBlastTransactionName; } - bool updateGeometry(); - - bool isSimpleBufferUpdate(const layer_state_t& s) const; - - static bool isOpaqueFormat(PixelFormat format); - - // Updates the LayerSnapshot. This must be called prior to sending layer data to - // CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or - // LayerFE::prepareClientComposition). - // - // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through - // CompositionEngine to create a single path for composing layers. - void updateSnapshot(bool updateGeometry); - void updateChildrenSnapshots(bool updateGeometry); - sp<Layer> getClonedFrom() const { - return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; - } - bool isClone() { return mClonedFrom != nullptr; } - - bool willPresentCurrentTransaction() const; - void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, const sp<GraphicBuffer>& buffer, uint64_t framenumber, const sp<Fence>& releaseFence); @@ -929,7 +416,6 @@ public: const sp<SurfaceFlinger> mFlinger; // Check if the damage region is a small dirty. - void setIsSmallDirty(const Region& damageRegion, const ui::Transform& layerToDisplayTransform); void setIsSmallDirty(frontend::LayerSnapshot* snapshot); protected: @@ -941,62 +427,16 @@ protected: friend class TransactionFrameTracerTest; friend class TransactionSurfaceFrameTest; - void preparePerFrameCompositionState(); - void preparePerFrameBufferCompositionState(); - void preparePerFrameEffectsCompositionState(); void gatherBufferInfo(); - bool isClonedFromAlive() { return getClonedFrom() != nullptr; } - - void cloneDrawingState(const Layer* from); - void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void updateClonedChildren(const sp<Layer>& mirrorRoot, - std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void addChildToDrawing(const sp<Layer>&); - void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - - void prepareBasicGeometryCompositionState(); - void prepareGeometryCompositionState(); - void prepareCursorCompositionState(); - - uint32_t getEffectiveUsage(uint32_t usage) const; - - /** - * Setup rounded corners coordinates of this layer, taking into account the layer bounds and - * crop coordinates, transforming them into layer space. - */ - void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const; - void setParent(const sp<Layer>&); - LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers); - void addZOrderRelative(const wp<Layer>& relative); - void removeZOrderRelative(const wp<Layer>& relative); compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const; compositionengine::OutputLayer* findOutputLayerForDisplay( const DisplayDevice*, const frontend::LayerHierarchy::TraversalPath& path) const; - bool usingRelativeZ(LayerVector::StateSet) const; - virtual ui::Transform getInputTransform() const; - /** - * Get the bounds in layer space within which this layer can receive input. - * - * These bounds are used to: - * - Determine the input frame for the layer to be used for occlusion detection; and - * - Determine the coordinate space within which the layer will receive input. The top-left of - * this rect will be the origin of the coordinate space that the input events sent to the - * layer will be in (prior to accounting for surface insets). - * - * The layer can still receive touch input if these bounds are invalid if - * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input - * in this layer's space, regardless of the specified crop layer. - */ - std::pair<FloatRect, bool> getInputBounds(bool fillParentBounds) const; - - bool mPremultipliedAlpha{true}; const std::string mName; const std::string mTransactionName{"TX - " + mName}; - // These are only accessed by the main thread or the tracing thread. + // These are only accessed by the main thread. State mDrawingState; TrustedPresentationThresholds mTrustedPresentationThresholds; @@ -1006,44 +446,22 @@ protected: int64_t mEnteredTrustedPresentationStateTime = -1; uint32_t mTransactionFlags{0}; - // Updated in doTransaction, used to track the last sequence number we - // committed. Currently this is really only used for updating visible - // regions. - int32_t mLastCommittedTxSequence = -1; // Timestamp history for UIAutomation. Thread safe. FrameTracker mFrameTracker; // main thread sp<NativeHandle> mSidebandStream; - // False if the buffer and its contents have been previously used for GPU - // composition, true otherwise. - bool mIsActiveBufferUpdatedForGpu = true; // We encode unset as -1. std::atomic<uint64_t> mCurrentFrameNumber{0}; - // Whether filtering is needed b/c of the drawingstate - bool mNeedsFiltering{false}; - - std::atomic<bool> mRemovedFromDrawingState{false}; - - // page-flip thread (currently main thread) - bool mProtectedByApp{false}; // application requires protected path to external sink // protected by mLock mutable Mutex mLock; - const wp<Client> mClientRef; - // This layer can be a cursor on some displays. bool mPotentialCursor{false}; - LayerVector mCurrentChildren{LayerVector::StateSet::Current}; - LayerVector mDrawingChildren{LayerVector::StateSet::Drawing}; - - wp<Layer> mCurrentParent; - wp<Layer> mDrawingParent; - // Window types from WindowManager.LayoutParams const gui::WindowInfo::Type mWindowType; @@ -1061,8 +479,6 @@ protected: // Used in buffer stuffing analysis in FrameTimeline. nsecs_t mLastLatchTime = 0; - mutable bool mDrawingStateModified = false; - sp<Fence> mLastClientCompositionFence; bool mClearClientCompositionFenceOnLayerDisplayed = false; private: @@ -1074,61 +490,20 @@ private: friend class TransactionFrameTracerTest; friend class TransactionSurfaceFrameTest; - bool getAutoRefresh() const { return mDrawingState.autoRefresh; } bool getSidebandStreamChanged() const { return mSidebandStreamChanged; } std::atomic<bool> mSidebandStreamChanged{false}; - // Returns true if the layer can draw shadows on its border. - virtual bool canDrawShadows() const { return true; } - aidl::android::hardware::graphics::composer3::Composition getCompositionType( const DisplayDevice&) const; aidl::android::hardware::graphics::composer3::Composition getCompositionType( const compositionengine::OutputLayer*) const; - /** - * Returns an unsorted vector of all layers that are part of this tree. - * That includes the current layer and all its descendants. - */ - std::vector<Layer*> getLayersInTree(LayerVector::StateSet); - /** - * Traverses layers that are part of this tree in the correct z order. - * layersInTree must be sorted before calling this method. - */ - void traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree, - LayerVector::StateSet, const LayerVector::Visitor&); - LayerVector makeChildrenTraversalList(LayerVector::StateSet, - const std::vector<Layer*>& layersInTree); - - bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren, - bool* transactionNeeded); - void setZOrderRelativeOf(const wp<Layer>& relativeOf); - bool isTrustedOverlay() const; - gui::DropInputMode getDropInputMode() const; - void handleDropInputMode(gui::WindowInfo& info) const; - - // Find the root of the cloned hierarchy, this means the first non cloned parent. - // This will return null if first non cloned parent is not found. - sp<Layer> getClonedRoot(); - - // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is - // null. - sp<Layer> getRootLayer(); - - // Fills in the touch occlusion mode of the first parent (including this layer) that - // hasInputInfo() or no-op if no such parent is found. - void fillTouchOcclusionMode(gui::WindowInfo& info); - - // Fills in the frame and transform info for the gui::WindowInfo. - void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay); inline void tracePendingBufferCount(int32_t pendingBuffers); // Latch sideband stream and returns true if the dirty region should be updated. bool latchSidebandStream(bool& recomputeVisibleRegions); - bool hasFrameUpdate() const; - void updateTexImage(nsecs_t latchTime, bool bgColorOnly = false); // Crop that applies to the buffer @@ -1139,15 +514,6 @@ private: const sp<Fence>& releaseFence, uint32_t currentMaxAcquiredBufferCount); - // Returns true if the transformed buffer size does not match the layer size and we need - // to apply filtering. - bool bufferNeedsFiltering() const; - - // Returns true if there is a valid color to fill. - bool fillsColor() const; - // Returns true if this layer has a blur value. - bool hasBlur() const; - bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); } bool hasBufferOrSidebandStream() const { return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr)); } @@ -1156,41 +522,8 @@ private: return ((mDrawingState.sidebandStream != nullptr) || (mDrawingState.buffer != nullptr)); } - bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); } - - bool shouldOverrideChildrenFrameRate() const { - return getDrawingState().frameRateSelectionStrategy == - FrameRateSelectionStrategy::OverrideChildren; - } - - bool shouldPropagateFrameRate() const { - return getDrawingState().frameRateSelectionStrategy != FrameRateSelectionStrategy::Self; - } - - // Cached properties computed from drawing state - // Effective transform taking into account parent transforms and any parent scaling, which is - // a transform from the current layer coordinate space to display(screen) coordinate space. - ui::Transform mEffectiveTransform; - - // Bounds of the layer before any transformation is applied and before it has been cropped - // by its parents. - FloatRect mSourceBounds; - - // Bounds of the layer in layer space. This is the mSourceBounds cropped by its layer crop and - // its parent bounds. - FloatRect mBounds; - - // Layer bounds in screen space. - FloatRect mScreenBounds; - bool mGetHandleCalled = false; - // The current layer is a clone of mClonedFrom. This means that this layer will update it's - // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers, - // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children, - // and relatives, this layer will update as well. - wp<Layer> mClonedFrom; - // The inherited shadow radius after taking into account the layer hierarchy. This is the // final shadow radius for this layer. If a shadow is specified for a layer, then effective // shadow radius is the set shadow radius, otherwise its the parent's shadow radius. @@ -1199,15 +532,10 @@ private: // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats. gui::GameMode mGameMode = gui::GameMode::Unsupported; - // A list of regions on this layer that should have blurs. - const std::vector<BlurRegion> getBlurRegions() const; - bool mIsAtRoot = false; uint32_t mLayerCreationFlags; - bool findInHierarchy(const sp<Layer>&); - void releasePreviousBuffer(); void resetDrawingStateBufferInfo(); @@ -1233,17 +561,14 @@ private: // You can understand the trace this way: // - If the integer increases, a buffer arrived at the server. // - If the integer decreases in latchBuffer, that buffer was latched - // - If the integer decreases in setBuffer or doTransaction, a buffer was dropped + // - If the integer decreases in setBuffer, a buffer was dropped std::atomic<int32_t> mPendingBufferTransactions{0}; // Contains requested position and matrix updates. This will be applied if the client does // not specify a destination frame. ui::Transform mRequestedTransform; - sp<LayerFE> mLegacyLayerFE; std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs; - std::unique_ptr<frontend::LayerSnapshot> mSnapshot = - std::make_unique<frontend::LayerSnapshot>(); bool mHandleAlive = false; }; diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 885c3d3460..5eea45b436 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -178,7 +178,7 @@ void LayerProtoHelper::writeToProto( } void LayerProtoHelper::writeToProto( - const WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds, + const WindowInfo& inputInfo, std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto) { if (inputInfo.token == nullptr) { return; @@ -208,13 +208,6 @@ void LayerProtoHelper::writeToProto( proto->set_global_scale_factor(inputInfo.globalScaleFactor); LayerProtoHelper::writeToProtoDeprecated(inputInfo.transform, proto->mutable_transform()); proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop); - auto cropLayer = touchableRegionBounds.promote(); - if (cropLayer != nullptr) { - proto->set_crop_layer_id(cropLayer->sequence); - LayerProtoHelper::writeToProto(cropLayer->getScreenBounds( - false /* reduceTransparentRegion */), - [&]() { return proto->mutable_touchable_region_crop(); }); - } } void LayerProtoHelper::writeToProto(const mat4 matrix, @@ -482,7 +475,7 @@ void LayerProtoHelper::writeSnapshotToProto(perfetto::protos::LayerProto* layerI layerInfo->set_owner_uid(requestedState.ownerUid.val()); if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) { - LayerProtoHelper::writeToProto(snapshot.inputInfo, {}, + LayerProtoHelper::writeToProto(snapshot.inputInfo, [&]() { return layerInfo->mutable_input_window_info(); }); } diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index c0198b62ec..41ea68420f 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -62,7 +62,7 @@ public: const renderengine::ExternalTexture& buffer, std::function<perfetto::protos::ActiveBufferProto*()> getActiveBufferProto); static void writeToProto( - const gui::WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds, + const gui::WindowInfo& inputInfo, std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto); static void writeToProto(const mat4 matrix, perfetto::protos::ColorTransformProto* colorTransformProto); diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp index f52e60deda..13e054e249 100644 --- a/services/surfaceflinger/LayerVector.cpp +++ b/services/surfaceflinger/LayerVector.cpp @@ -45,51 +45,12 @@ int LayerVector::do_compare(const void* lhs, const void* rhs) const const auto& lState = l->getDrawingState(); const auto& rState = r->getDrawingState(); - const auto ls = lState.layerStack; - const auto 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; - if (l->sequence == r->sequence) return 0; return (l->sequence > r->sequence) ? 1 : -1; } -void LayerVector::traverseInZOrder(StateSet stateSet, const Visitor& visitor) const { - for (size_t i = 0; i < size(); i++) { - const auto& layer = (*this)[i]; - auto& state = layer->getDrawingState(); - if (state.isRelativeOf) { - continue; - } - layer->traverseInZOrder(stateSet, visitor); - } -} - -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 = layer->getDrawingState(); - if (state.isRelativeOf) { - continue; - } - layer->traverseInReverseZOrder(stateSet, visitor); - } -} - -void LayerVector::traverse(const Visitor& visitor) const { - for (auto i = static_cast<int64_t>(size()) - 1; i >= 0; i--) { - const auto& layer = (*this)[i]; - layer->traverse(mStateSet, visitor); - } -} - } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/LayerVector.h b/services/surfaceflinger/LayerVector.h index a531f4fd95..38dc11d3bc 100644 --- a/services/surfaceflinger/LayerVector.h +++ b/services/surfaceflinger/LayerVector.h @@ -46,11 +46,8 @@ public: // Sorts layer by layer-stack, Z order, and finally creation order (sequence). int do_compare(const void* lhs, const void* rhs) const override; - using Visitor = std::function<void(Layer*)>; - void traverseInReverseZOrder(StateSet stateSet, const Visitor& visitor) const; - void traverseInZOrder(StateSet stateSet, const Visitor& visitor) const; - void traverse(const Visitor& visitor) const; + private: const StateSet mStateSet; }; diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 7712d38f43..06c2f26a6d 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -346,6 +346,7 @@ void RegionSamplingThread::captureSample() { constexpr bool kRegionSampling = true; constexpr bool kGrayscale = false; constexpr bool kIsProtected = false; + constexpr bool kAttachGainmap = false; SurfaceFlinger::RenderAreaBuilderVariant renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds, @@ -358,15 +359,15 @@ void RegionSamplingThread::captureSample() { std::vector<sp<LayerFE>> layerFEs; auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs); - fenceResult = - mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale, - kIsProtected, nullptr, displayState, layerFEs) - .get(); + fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, + kGrayscale, kIsProtected, kAttachGainmap, nullptr, + displayState, layerFEs) + .get(); } else { - fenceResult = - mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, buffer, - kRegionSampling, kGrayscale, kIsProtected, nullptr) - .get(); + fenceResult = mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, + buffer, kRegionSampling, kGrayscale, + kIsProtected, kAttachGainmap, nullptr) + .get(); } if (fenceResult.ok()) { fenceResult.value()->waitForever(LOG_TAG); diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index 034e467be9..aa66ccf172 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -39,21 +39,6 @@ public: mReqDataSpace(reqDataSpace), mCaptureFill(captureFill) {} - static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda( - std::function<void(const LayerVector::Visitor&)> traverseLayers) { - return [traverseLayers = std::move(traverseLayers)]() { - std::vector<std::pair<Layer*, sp<LayerFE>>> layers; - traverseLayers([&](Layer* layer) { - // Layer::prepareClientComposition uses the layer's snapshot to populate the - // resulting LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings - // are generated with the layer's current buffer and geometry. - layer->updateSnapshot(true /* updateGeometry */); - layers.emplace_back(layer, layer->copyCompositionEngineLayerFE()); - }); - return layers; - }; - } - virtual ~RenderArea() = default; // Returns true if the render area is secure. A secure layer should be diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index d31fceab7e..218c56ef3d 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -320,7 +320,8 @@ void EventThread::setDuration(std::chrono::nanoseconds workDuration, mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .lastVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns(), + .committedVsyncOpt = mLastCommittedVsyncTime.ns()}); } sp<EventThreadConnection> EventThread::createEventConnection( @@ -527,10 +528,11 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { } if (mState == State::VSync) { - const auto scheduleResult = - mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), - .readyDuration = mReadyDuration.count(), - .lastVsync = mLastVsyncCallbackTime.ns()}); + const auto scheduleResult = mVsyncRegistration.schedule( + {.workDuration = mWorkDuration.get().count(), + .readyDuration = mReadyDuration.count(), + .lastVsync = mLastVsyncCallbackTime.ns(), + .committedVsyncOpt = mLastCommittedVsyncTime.ns()}); LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback"); } else { mVsyncRegistration.cancel(); @@ -725,8 +727,9 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, } if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC && FlagManager::getInstance().vrr_config()) { - mCallback.onExpectedPresentTimePosted( - TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime())); + mLastCommittedVsyncTime = + TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime()); + mCallback.onExpectedPresentTimePosted(mLastCommittedVsyncTime); } } @@ -744,9 +747,12 @@ void EventThread::dump(std::string& result) const { const auto relativeLastCallTime = ticks<std::milli, float>(mLastVsyncCallbackTime - TimePoint::now()); + const auto relativeLastCommittedTime = + ticks<std::milli, float>(mLastCommittedVsyncTime - TimePoint::now()); StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ", mWorkDuration.get().count() / 1e6f, mReadyDuration.count() / 1e6f); StringAppendF(&result, "%.2fms relative to now\n", relativeLastCallTime); + StringAppendF(&result, " with vsync committed at %.2fms", relativeLastCommittedTime); StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size()); for (const auto& event : mPendingEvents) { @@ -794,7 +800,8 @@ scheduler::VSyncCallbackRegistration EventThread::onNewVsyncScheduleInternal( if (reschedule) { mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), .readyDuration = mReadyDuration.count(), - .lastVsync = mLastVsyncCallbackTime.ns()}); + .lastVsync = mLastVsyncCallbackTime.ns(), + .committedVsyncOpt = mLastCommittedVsyncTime.ns()}); } return oldRegistration; } diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index f772126349..bbe4f9d899 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -220,6 +220,7 @@ private: std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex); std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule GUARDED_BY(mMutex); TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now(); + TimePoint mLastCommittedVsyncTime GUARDED_BY(mMutex) = TimePoint::now(); scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index dbc458cb7f..ff1926e03f 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -595,8 +595,7 @@ bool LayerInfo::FrameRate::isVoteValidForMrr(bool isVrrDevice) const { return true; } - if (FlagManager::getInstance().view_set_requested_frame_rate_mrr() && - category == FrameRateCategory::NoPreference && vote.rate.isValid() && + if (category == FrameRateCategory::NoPreference && vote.rate.isValid() && vote.type == FrameRateCompatibility::ExactOrMultiple) { return true; } diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index 9f6eab288b..ab9014e418 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -841,7 +841,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return score.overallScore == 0; }); - if (policy->primaryRangeIsSingleRate()) { + // TODO(b/364651864): Evaluate correctness of primaryRangeIsSingleRate. + if (!mIsVrrDevice.load() && policy->primaryRangeIsSingleRate()) { // If we never scored any layers, then choose the rate from the primary // range instead of picking a random score from the app range. if (noLayerScore) { @@ -887,8 +888,8 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; - if (scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { - ALOGV("Touch Boost"); + if (scores.front().frameRateMode.fps <= touchRefreshRates.front().frameRateMode.fps) { + ALOGV("Touch Boost [late]"); SFTRACE_FORMAT_INSTANT("%s (Touch Boost [late])", to_string(touchRefreshRates.front().frameRateMode.fps).c_str()); return {touchRefreshRates, GlobalSignals{.touch = true}}; @@ -1394,13 +1395,14 @@ auto RefreshRateSelector::setPolicy(const PolicyVariant& policy) -> SetPolicyRes const auto& idleScreenConfigOpt = getCurrentPolicyLocked()->idleScreenConfigOpt; if (idleScreenConfigOpt != oldPolicy.idleScreenConfigOpt) { if (!idleScreenConfigOpt.has_value()) { - // fallback to legacy timer if existed, otherwise pause the old timer - LOG_ALWAYS_FATAL_IF(!mIdleTimer); - if (mConfig.legacyIdleTimerTimeout > 0ms) { - mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout); - mIdleTimer->resume(); - } else { - mIdleTimer->pause(); + if (mIdleTimer) { + // fallback to legacy timer if existed, otherwise pause the old timer + if (mConfig.legacyIdleTimerTimeout > 0ms) { + mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout); + mIdleTimer->resume(); + } else { + mIdleTimer->pause(); + } } } else if (idleScreenConfigOpt->timeoutMillis > 0) { // create a new timer or reconfigure diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index be00079b9c..5e131548aa 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -38,7 +38,6 @@ #include <FrameTimeline/FrameTimeline.h> #include <scheduler/interface/ICompositor.h> -#include <algorithm> #include <cinttypes> #include <cstdint> #include <functional> @@ -46,16 +45,15 @@ #include <numeric> #include <common/FlagManager.h> -#include "../Layer.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" #include "FrontEnd/LayerHandle.h" +#include "Layer.h" #include "OneShotTimer.h" #include "RefreshRateStats.h" #include "SurfaceFlingerFactory.h" #include "SurfaceFlingerProperties.h" #include "TimeStats/TimeStats.h" -#include "VSyncTracker.h" #include "VsyncConfiguration.h" #include "VsyncController.h" #include "VsyncSchedule.h" @@ -361,10 +359,8 @@ void Scheduler::createEventThread(Cycle cycle, frametimeline::TokenManager* toke if (cycle == Cycle::Render) { mRenderEventThread = std::move(eventThread); - mRenderEventConnection = mRenderEventThread->createEventConnection(); } else { mLastCompositeEventThread = std::move(eventThread); - mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection(); } } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 1367ec385e..c88b563805 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -145,10 +145,6 @@ public: Cycle, EventRegistrationFlags eventRegistration = {}, const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock); - const sp<EventThreadConnection>& getEventConnection(Cycle cycle) const { - return cycle == Cycle::Render ? mRenderEventConnection : mLastCompositeEventConnection; - } - enum class Hotplug { Connected, Disconnected }; void dispatchHotplug(PhysicalDisplayId, Hotplug); @@ -467,10 +463,7 @@ private: void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock); std::unique_ptr<EventThread> mRenderEventThread; - sp<EventThreadConnection> mRenderEventConnection; - std::unique_ptr<EventThread> mLastCompositeEventThread; - sp<EventThreadConnection> mLastCompositeEventConnection; std::atomic<nsecs_t> mLastResyncTime = 0; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index 0c43ffbc04..8993c38d37 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -93,6 +93,8 @@ public: * readyDuration will typically be 0. * @lastVsync: The targeted display time. This will be snapped to the closest * predicted vsync time after lastVsync. + * @committedVsyncOpt: The display time that is committed to the callback as the + * target vsync time. * * callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync * event. @@ -101,10 +103,11 @@ public: nsecs_t workDuration = 0; nsecs_t readyDuration = 0; nsecs_t lastVsync = 0; + std::optional<nsecs_t> committedVsyncOpt; bool operator==(const ScheduleTiming& other) const { return workDuration == other.workDuration && readyDuration == other.readyDuration && - lastVsync == other.lastVsync; + lastVsync == other.lastVsync && committedVsyncOpt == other.committedVsyncOpt; } bool operator!=(const ScheduleTiming& other) const { return !(*this == other); } diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 900bce0aa9..1925f1165c 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -103,7 +103,8 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync, now + timing.workDuration + timing.readyDuration), - timing.lastVsync); + timing.committedVsyncOpt.value_or( + timing.lastVsync)); auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; bool const wouldSkipAVsyncTarget = @@ -208,9 +209,12 @@ void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration; const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration; const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync; + const auto lastCommittedVsyncDelta = + mWorkloadUpdateInfo->committedVsyncOpt.value_or(mWorkloadUpdateInfo->lastVsync) - + mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync); SFTRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64 - " lastVsyncDelta=%" PRId64, - workDelta, readyDelta, lastVsyncDelta); + " lastVsyncDelta=%" PRId64 " committedVsyncDelta=%" PRId64, + workDelta, readyDelta, lastVsyncDelta, lastCommittedVsyncDelta); mScheduleTiming = *mWorkloadUpdateInfo; mWorkloadUpdateInfo.reset(); } @@ -261,10 +265,14 @@ void VSyncDispatchTimerQueueEntry::dump(std::string& result) const { StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(), mRunning ? "(in callback function)" : "", armedInfo.c_str()); StringAppendF(&result, - "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative " - "to now\n", + "\t\t\tworkDuration: %.2fms readyDuration: %.2fms " + "lastVsync: %.2fms relative to now " + "committedVsync: %.2fms relative to now\n", mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f, - (mScheduleTiming.lastVsync - systemTime()) / 1e6f); + (mScheduleTiming.lastVsync - systemTime()) / 1e6f, + (mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync) - + systemTime()) / + 1e6f); if (mLastDispatchTime) { StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n", diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 4a7cff58dd..6e36f02463 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -695,9 +695,12 @@ std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTime if (lastFrameMissed) { // If the last frame missed is the last vsync, we already shifted the timeline. Depends // on whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a - // different fixup. There is no need to to shift the vsync timeline again. - vsyncTime += missedVsync.fixup.ns(); - SFTRACE_FORMAT_INSTANT("lastFrameMissed"); + // different fixup if we are violating the minFramePeriod. + // There is no need to shift the vsync timeline again. + if (vsyncTime - missedVsync.vsync.ns() < minFramePeriodOpt->ns()) { + vsyncTime += missedVsync.fixup.ns(); + SFTRACE_FORMAT_INSTANT("lastFrameMissed"); + } } else if (mightBackpressure && lastVsyncOpt) { if (!FlagManager::getInstance().vrr_bugfix_24q4()) { // lastVsyncOpt does not need to be corrected with the new rate, and diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp index fa377e9323..3c5f68c4c2 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp +++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp @@ -21,7 +21,6 @@ #include "VsyncModulator.h" -#include <android-base/properties.h> #include <common/trace.h> #include <log/log.h> @@ -37,8 +36,7 @@ const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms; VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now) : mVsyncConfigSet(config), - mNow(now), - mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {} + mNow(now) {} VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) { std::lock_guard<std::mutex> lock(mMutex); @@ -71,10 +69,6 @@ VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule(Transactio break; } - if (mTraceDetailedInfo) { - SFTRACE_INT("mEarlyWakeup", static_cast<int>(mEarlyWakeupRequests.size())); - } - if (mEarlyWakeupRequests.empty() && schedule == Schedule::EarlyEnd) { mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES; mEarlyTransactionStartTime = mNow(); @@ -167,15 +161,19 @@ VsyncConfig VsyncModulator::updateVsyncConfigLocked() { const VsyncConfig& offsets = getNextVsyncConfig(); mVsyncConfig = offsets; - if (mTraceDetailedInfo) { - const bool isEarly = &offsets == &mVsyncConfigSet.early; - const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu; - const bool isLate = &offsets == &mVsyncConfigSet.late; + // Trace config type + SFTRACE_INT("Vsync-Early", &mVsyncConfig == &mVsyncConfigSet.early); + SFTRACE_INT("Vsync-EarlyGpu", &mVsyncConfig == &mVsyncConfigSet.earlyGpu); + SFTRACE_INT("Vsync-Late", &mVsyncConfig == &mVsyncConfigSet.late); - SFTRACE_INT("Vsync-EarlyOffsetsOn", isEarly); - SFTRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu); - SFTRACE_INT("Vsync-LateOffsetsOn", isLate); - } + // Trace early vsync conditions + SFTRACE_INT("EarlyWakeupRequests", + static_cast<int>(mEarlyWakeupRequests.size())); + SFTRACE_INT("EarlyTransactionFrames", mEarlyTransactionFrames); + SFTRACE_INT("RefreshRateChangePending", mRefreshRateChangePending); + + // Trace early gpu conditions + SFTRACE_INT("EarlyGpuFrames", mEarlyGpuFrames); return offsets; } @@ -183,7 +181,6 @@ VsyncConfig VsyncModulator::updateVsyncConfigLocked() { void VsyncModulator::binderDied(const wp<IBinder>& who) { std::lock_guard<std::mutex> lock(mMutex); mEarlyWakeupRequests.erase(who); - static_cast<void>(updateVsyncConfigLocked()); } diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h index be0d3348b5..d0a793535c 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.h +++ b/services/surfaceflinger/Scheduler/VsyncModulator.h @@ -105,7 +105,6 @@ private: std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint(); const Now mNow; - const bool mTraceDetailedInfo; }; } // namespace android::scheduler diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp index 8bb72b8470..41a9a1bb22 100644 --- a/services/surfaceflinger/ScreenCaptureOutput.cpp +++ b/services/surfaceflinger/ScreenCaptureOutput.cpp @@ -93,6 +93,12 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp if (mEnableLocalTonemapping) { clientCompositionDisplay.tonemapStrategy = renderengine::DisplaySettings::TonemapStrategy::Local; + if (static_cast<ui::PixelFormat>(buffer->getPixelFormat()) == ui::PixelFormat::RGBA_FP16) { + clientCompositionDisplay.targetHdrSdrRatio = + getState().displayBrightnessNits / getState().sdrWhitePointNits; + } else { + clientCompositionDisplay.targetHdrSdrRatio = 1.f; + } } return clientCompositionDisplay; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index a2322287ea..65a0ed3065 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -532,9 +532,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false); // These are set by the HWC implementation to indicate that they will use the workarounds. - mIsHotplugErrViaNegVsync = - base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false); - mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false); } @@ -1006,6 +1003,8 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { // which we maintain for backwards compatibility. config.cacheUltraHDR = base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false); + config.cacheEdgeExtension = + base::GetBoolProperty("debug.sf.edge_extension_shader"s, true); return getRenderEngine().primeCache(config); }); @@ -2172,21 +2171,13 @@ void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t t std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { if (FlagManager::getInstance().connected_display() && timestamp < 0 && vsyncPeriod.has_value()) { - // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32 - if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) { - const auto errorCode = static_cast<int32_t>(-timestamp); - ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId); - mScheduler->dispatchHotplugError(errorCode); - return; - } - if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) { const int32_t value = static_cast<int32_t>(-timestamp); // one byte is good enough to encode android.hardware.drm.HdcpLevel const int32_t maxLevel = (value >> 8) & 0xFF; const int32_t connectedLevel = value & 0xFF; - ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for display %" PRIu64, __func__, - connectedLevel, maxLevel, hwcDisplayId); + ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64, + __func__, connectedLevel, maxLevel, hwcDisplayId); updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel); return; } @@ -2226,7 +2217,7 @@ void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, if (FlagManager::getInstance().hotplug2()) { // TODO(b/311403559): use enum type instead of int const auto errorCode = static_cast<int32_t>(event); - ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId); + ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId); mScheduler->dispatchHotplugError(errorCode); } } @@ -2276,6 +2267,18 @@ void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData })); } +void SurfaceFlinger::onComposerHalHdcpLevelsChanged(hal::HWDisplayId hwcDisplayId, + const HdcpLevels& levels) { + if (FlagManager::getInstance().hdcp_level_hal()) { + // TODO(b/362270040): propagate enum constants + const int32_t maxLevel = static_cast<int32_t>(levels.maxLevel); + const int32_t connectedLevel = static_cast<int32_t>(levels.connectedLevel); + ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64, __func__, + connectedLevel, maxLevel, hwcDisplayId); + updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel); + } +} + void SurfaceFlinger::configure() { Mutex::Autolock lock(mStateLock); if (configureLocked()) { @@ -2371,7 +2374,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, { // TODO(b/238781169) lockless queue this and keep order. std::scoped_lock<std::mutex> lock(mCreatedLayersLock); - update.layerCreatedStates = std::move(mCreatedLayers); + update.legacyLayers = std::move(mCreatedLayers); mCreatedLayers.clear(); update.newLayers = std::move(mNewLayers); mNewLayers.clear(); @@ -2390,11 +2393,8 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, } mLayerLifecycleManager.applyTransactions(update.transactions); mLayerLifecycleManager.onHandlesDestroyed(update.destroyedHandles); - for (auto& legacyLayer : update.layerCreatedStates) { - sp<Layer> layer = legacyLayer.layer.promote(); - if (layer) { - mLegacyLayers[layer->sequence] = layer; - } + for (auto& legacyLayer : update.legacyLayers) { + mLegacyLayers[legacyLayer->sequence] = legacyLayer; } mLayerHierarchyBuilder.update(mLayerLifecycleManager); } @@ -2451,7 +2451,7 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool newDataLatched = false; SFTRACE_NAME("DisplayCallbackAndStatsUpdates"); - mustComposite |= applyTransactionsLocked(update.transactions, vsyncId); + mustComposite |= applyTransactionsLocked(update.transactions); traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); }); const nsecs_t latchTime = systemTime(); bool unused = false; @@ -2741,7 +2741,8 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( if (!FlagManager::getInstance().ce_fence_promise()) { refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); for (auto& [layer, _] : mLayersWithQueuedFrames) { - if (const auto& layerFE = layer->getCompositionEngineLayerFE()) + if (const auto& layerFE = layer->getCompositionEngineLayerFE( + {static_cast<uint32_t>(layer->sequence)})) refreshArgs.layersWithQueuedFrames.push_back(layerFE); } } @@ -2817,7 +2818,8 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); for (auto& [layer, _] : mLayersWithQueuedFrames) { - if (const auto& layerFE = layer->getCompositionEngineLayerFE()) { + if (const auto& layerFE = layer->getCompositionEngineLayerFE( + {static_cast<uint32_t>(layer->sequence)})) { refreshArgs.layersWithQueuedFrames.push_back(layerFE); // Some layers are not displayed and do not yet have a future release fence if (layerFE->getReleaseFencePromiseStatus() == @@ -2848,9 +2850,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( for (auto [layer, layerFE] : layers) { CompositionResult compositionResult{layerFE->stealCompositionResult()}; for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) { - Layer* clonedFrom = layer->getClonedFrom().get(); - auto owningLayer = clonedFrom ? clonedFrom : layer; - owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack); + layer->onLayerDisplayed(std::move(releaseFence), layerStack); } if (compositionResult.lastClientCompositionFence) { layer->setWasClientComposed(compositionResult.lastClientCompositionFence); @@ -3819,7 +3819,8 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, mDisplays.erase(displayToken); if (const auto& physical = currentState.physical) { - getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id); + getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id, + /*physicalSize=*/std::nullopt); } processDisplayAdded(displayToken, currentState); @@ -3915,7 +3916,6 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { // Commit display transactions. const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded; mFrontEndDisplayInfosChanged = displayTransactionNeeded; - mForceTransactionDisplayChange = displayTransactionNeeded; if (mSomeChildrenChanged) { mVisibleRegionsDirty = true; @@ -4236,6 +4236,8 @@ void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId display if (data.hintStatus.compare_exchange_strong(scheduleHintOnTx, NotifyExpectedPresentHintStatus::Sent)) { sendHint(); + constexpr bool kAllowToEnable = true; + mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable); } })); } @@ -4336,55 +4338,8 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { void SurfaceFlinger::doCommitTransactions() { SFTRACE_CALL(); - - if (!mLayersPendingRemoval.isEmpty()) { - // Notify removed layers now that they can't be drawn from - for (const auto& l : mLayersPendingRemoval) { - // Ensure any buffers set to display on any children are released. - if (l->isRemovedFromCurrentState()) { - l->latchAndReleaseBuffer(); - } - - // If a layer has a parent, we allow it to out-live it's handle - // with the idea that the parent holds a reference and will eventually - // be cleaned up. However no one cleans up the top-level so we do so - // here. - if (l->isAtRoot()) { - l->setIsAtRoot(false); - mCurrentState.layersSortedByZ.remove(l); - } - } - mLayersPendingRemoval.clear(); - } - mDrawingState = mCurrentState; mCurrentState.colorMatrixChanged = false; - - if (mVisibleRegionsDirty) { - for (const auto& rootLayer : mDrawingState.layersSortedByZ) { - rootLayer->commitChildList(); - } - } - - if (mLayerMirrorRoots.size() > 0) { - std::deque<Layer*> pendingUpdates; - pendingUpdates.insert(pendingUpdates.end(), mLayerMirrorRoots.begin(), - mLayerMirrorRoots.end()); - std::vector<Layer*> needsUpdating; - for (Layer* cloneRoot : mLayerMirrorRoots) { - pendingUpdates.pop_front(); - if (cloneRoot->isRemovedFromCurrentState()) { - continue; - } - if (cloneRoot->updateMirrorInfo(pendingUpdates)) { - } else { - needsUpdating.push_back(cloneRoot); - } - } - for (Layer* cloneRoot : needsUpdating) { - cloneRoot->updateMirrorInfo({}); - } - } } void SurfaceFlinger::invalidateLayerStack(const ui::LayerFilter& layerFilter, const Region& dirty) { @@ -4427,7 +4382,7 @@ status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinde args.layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote()); { std::scoped_lock<std::mutex> lock(mCreatedLayersLock); - mCreatedLayers.emplace_back(layer, parent, args.addToRoot); + mCreatedLayers.emplace_back(layer); mNewLayers.emplace_back(std::make_unique<frontend::RequestedLayerState>(args)); args.mirrorLayerHandle.clear(); args.parentHandle.clear(); @@ -4622,20 +4577,18 @@ void SurfaceFlinger::addTransactionReadyFilters() { } // For tests only -bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) { +bool SurfaceFlinger::flushTransactionQueues() { mTransactionHandler.collectTransactions(); std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions(); - return applyTransactions(transactions, vsyncId); + return applyTransactions(transactions); } -bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions, - VsyncId vsyncId) { +bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) { Mutex::Autolock lock(mStateLock); - return applyTransactionsLocked(transactions, vsyncId); + return applyTransactionsLocked(transactions); } -bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions, - VsyncId vsyncId) { +bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions) { bool needsTraversal = false; // Now apply all transactions. for (auto& transaction : transactions) { @@ -5115,6 +5068,10 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f } } + if (what & layer_state_t::eBufferReleaseChannelChanged) { + layer->setBufferReleaseChannel(s.bufferReleaseChannel); + } + const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence()); bool willPresentCurrentTransaction = requestedLayerState && (requestedLayerState->hasReadyFrame() || @@ -5153,8 +5110,6 @@ status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args, if (result != NO_ERROR) { return result; } - - mirrorLayer->setClonedChild(mirrorFrom->createClone()); } outResult.layerId = mirrorLayer->sequence; @@ -5268,33 +5223,22 @@ status_t SurfaceFlinger::createEffectLayer(const LayerCreationArgs& args, sp<IBi return NO_ERROR; } -void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) { - mLayersPendingRemoval.add(layer); - mLayersRemoved = true; - setTransactionFlags(eTransactionNeeded); -} - void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) { { - std::scoped_lock<std::mutex> lock(mCreatedLayersLock); - mDestroyedHandles.emplace_back(layerId, layer->getDebugName()); - } - - { // Used to remove stalled transactions which uses an internal lock. ftl::FakeGuard guard(kMainThreadContext); mTransactionHandler.onLayerDestroyed(layerId); } - JankTracker::flushJankData(layerId); - Mutex::Autolock lock(mStateLock); - markLayerPendingRemovalLocked(layer); + std::scoped_lock<std::mutex> lock(mCreatedLayersLock); + mDestroyedHandles.emplace_back(layerId, layer->getDebugName()); + + Mutex::Autolock stateLock(mStateLock); layer->onHandleDestroyed(); mBufferCountTracker.remove(handle); layer.clear(); - - setTransactionFlags(eTransactionFlushNeeded); + setTransactionFlags(eTransactionFlushNeeded | eTransactionNeeded); } void SurfaceFlinger::initializeDisplays() { @@ -7022,7 +6966,8 @@ void SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, displayWeak, options), getLayerSnapshotsFn, reqSize, static_cast<ui::PixelFormat>(captureArgs.pixelFormat), - captureArgs.allowProtected, captureArgs.grayscale, captureListener); + captureArgs.allowProtected, captureArgs.grayscale, + captureArgs.attachGainmap, captureListener); } void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args, @@ -7079,7 +7024,7 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args static_cast<ui::Dataspace>(args.dataspace), displayWeak, options), getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat), - kAllowProtected, kGrayscale, captureListener); + kAllowProtected, kGrayscale, args.attachGainmap, captureListener); } ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) { @@ -7190,7 +7135,8 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, options), getLayerSnapshotsFn, reqSize, static_cast<ui::PixelFormat>(captureArgs.pixelFormat), - captureArgs.allowProtected, captureArgs.grayscale, captureListener); + captureArgs.allowProtected, captureArgs.grayscale, + captureArgs.attachGainmap, captureListener); } // Creates a Future release fence for a layer and keeps track of it in a list to @@ -7199,9 +7145,7 @@ void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, void SurfaceFlinger::attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack) { ftl::Future<FenceResult> futureFence = layerFE->createReleaseFenceFuture(); - Layer* clonedFrom = layer->getClonedFrom().get(); - auto owningLayer = clonedFrom ? clonedFrom : layer; - owningLayer->prepareReleaseCallbacks(std::move(futureFence), layerStack); + layer->prepareReleaseCallbacks(std::move(futureFence), layerStack); } // Loop over all visible layers to see whether there's any protected layer. A protected layer is @@ -7243,7 +7187,7 @@ std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapsho void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, ui::Size bufferSize, ui::PixelFormat reqPixelFormat, - bool allowProtected, bool grayscale, + bool allowProtected, bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>& captureListener) { SFTRACE_CALL(); @@ -7289,9 +7233,9 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), renderengine::impl::ExternalTexture::Usage:: WRITEABLE); - auto futureFence = - captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale, - isProtected, captureListener, displayState, layerFEs); + auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, + grayscale, isProtected, attachGainmap, captureListener, + displayState, layerFEs); futureFence.get(); } else { @@ -7326,7 +7270,7 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil WRITEABLE); auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture, false /* regionSampling */, grayscale, - isProtected, captureListener); + isProtected, attachGainmap, captureListener); futureFence.get(); } } @@ -7380,7 +7324,8 @@ std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs( ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener, + bool grayscale, bool isProtected, bool attachGainmap, + const sp<IScreenCaptureListener>& captureListener, std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) { SFTRACE_CALL(); @@ -7397,19 +7342,87 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( } return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); } + float displayBrightnessNits = displayState.value().displayBrightnessNits; + float sdrWhitePointNits = displayState.value().sdrWhitePointNits; // Empty vector needed to pass into renderScreenImpl for legacy path std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers; ftl::SharedFuture<FenceResult> renderFuture = - renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected, - captureResults, displayState, layers, layerFEs); + renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, + attachGainmap, captureResults, displayState, layers, layerFEs); + + if (captureResults.capturedHdrLayers && attachGainmap && + FlagManager::getInstance().true_hdr_screenshots()) { + sp<GraphicBuffer> hdrBuffer = + getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(), + HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */, + buffer->getUsage(), "screenshot-hdr"); + sp<GraphicBuffer> gainmapBuffer = + getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(), + buffer->getPixelFormat(), 1 /* layerCount */, + buffer->getUsage(), "screenshot-gainmap"); + + const status_t bufferStatus = hdrBuffer->initCheck(); + const status_t gainmapBufferStatus = gainmapBuffer->initCheck(); + + if (bufferStatus != OK) { + ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", __func__, + bufferStatus); + } else if (gainmapBufferStatus != OK) { + ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.", + __func__, gainmapBufferStatus); + } else { + captureResults.optionalGainMap = gainmapBuffer; + const auto hdrTexture = std::make_shared< + renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(), + renderengine::impl::ExternalTexture:: + Usage::WRITEABLE); + const auto gainmapTexture = std::make_shared< + renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(), + renderengine::impl::ExternalTexture:: + Usage::WRITEABLE); + ScreenCaptureResults unusedResults; + ftl::SharedFuture<FenceResult> hdrRenderFuture = + renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale, + isProtected, attachGainmap, unusedResults, displayState, + layers, layerFEs); + + renderFuture = + ftl::Future(std::move(renderFuture)) + .then([&, hdrRenderFuture = std::move(hdrRenderFuture), + displayBrightnessNits, sdrWhitePointNits, + dataspace = captureResults.capturedDataspace, buffer, hdrTexture, + gainmapTexture](FenceResult fenceResult) -> FenceResult { + if (!fenceResult.ok()) { + return fenceResult; + } + + auto hdrFenceResult = hdrRenderFuture.get(); + + if (!hdrFenceResult.ok()) { + return hdrFenceResult; + } + + return getRenderEngine() + .drawGainmap(buffer, fenceResult.value()->get(), hdrTexture, + hdrFenceResult.value()->get(), + displayBrightnessNits / sdrWhitePointNits, + static_cast<ui::Dataspace>(dataspace), + gainmapTexture) + .get(); + }) + .share(); + }; + } if (captureListener) { // Defer blocking on renderFuture back to the Binder thread. return ftl::Future(std::move(renderFuture)) - .then([captureListener, captureResults = std::move(captureResults)]( - FenceResult fenceResult) mutable -> FenceResult { + .then([captureListener, captureResults = std::move(captureResults), + displayBrightnessNits, + sdrWhitePointNits](FenceResult fenceResult) mutable -> FenceResult { captureResults.fenceResult = std::move(fenceResult); + captureResults.hdrSdrRatio = displayBrightnessNits / sdrWhitePointNits; captureListener->onScreenCaptureCompleted(captureResults); return base::unexpected(NO_ERROR); }) @@ -7421,7 +7434,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) { + bool grayscale, bool isProtected, bool attachGainmap, + const sp<IScreenCaptureListener>& captureListener) { SFTRACE_CALL(); auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES( @@ -7450,8 +7464,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( auto layerFEs = extractLayerFEs(layers); ftl::SharedFuture<FenceResult> renderFuture = - renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, - isProtected, captureResults, displayState, layers, layerFEs); + renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, + attachGainmap, captureResults, displayState, layers, layerFEs); if (captureListener) { // Defer blocking on renderFuture back to the Binder thread. @@ -7481,10 +7495,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( } ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( - std::unique_ptr<const RenderArea> renderArea, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, ScreenCaptureResults& captureResults, - std::optional<OutputCompositionState>& displayState, + const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer, + bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, + ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState, std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) { SFTRACE_CALL(); @@ -7669,18 +7682,6 @@ void SurfaceFlinger::traverseLegacyLayers(const LayerVector::Visitor& visitor) c // --------------------------------------------------------------------------- -void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const { - layersSortedByZ.traverse(visitor); -} - -void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const { - layersSortedByZ.traverseInZOrder(stateSet, visitor); -} - -void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& visitor) const { - layersSortedByZ.traverseInReverseZOrder(stateSet, visitor); -} - ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode( PhysicalDisplayId displayId, DisplayModeId defaultModeId) const { if (const auto schedulerMode = mScheduler->getPreferredDisplayMode(); @@ -7849,16 +7850,12 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo void SurfaceFlinger::onLayerFirstRef(Layer* layer) { mNumLayers++; - if (!layer->isRemovedFromCurrentState()) { - mScheduler->registerLayer(layer, scheduler::FrameRateCompatibility::Default); - } + mScheduler->registerLayer(layer, scheduler::FrameRateCompatibility::Default); } void SurfaceFlinger::onLayerDestroyed(Layer* layer) { mNumLayers--; - if (!layer->isRemovedFromCurrentState()) { - mScheduler->deregisterLayer(layer); - } + mScheduler->deregisterLayer(layer); if (mTransactionTracing) { mTransactionTracing->onLayerRemoved(layer->getSequence()); } @@ -8011,37 +8008,6 @@ int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) con return calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } -void SurfaceFlinger::handleLayerCreatedLocked(const LayerCreatedState& state, VsyncId vsyncId) { - sp<Layer> layer = state.layer.promote(); - if (!layer) { - ALOGD("Layer was destroyed soon after creation %p", state.layer.unsafe_get()); - return; - } - MUTEX_ALIAS(mStateLock, layer->mFlinger->mStateLock); - - sp<Layer> parent; - bool addToRoot = state.addToRoot; - if (state.initialParent != nullptr) { - parent = state.initialParent.promote(); - if (parent == nullptr) { - ALOGD("Parent was destroyed soon after creation %p", state.initialParent.unsafe_get()); - addToRoot = false; - } - } - - if (parent == nullptr && addToRoot) { - layer->setIsAtRoot(true); - mCurrentState.layersSortedByZ.add(layer); - } else if (parent == nullptr) { - layer->onRemovedFromCurrentState(); - } else if (parent->isRemovedFromCurrentState()) { - parent->addChild(layer); - layer->onRemovedFromCurrentState(); - } else { - parent->addChild(layer); - } -} - void SurfaceFlinger::sample() { if (!mLumaSampling || !mRegionSamplingThread) { return; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 873fac2c89..3eb72cc4c0 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -135,6 +135,7 @@ class FrameTracer; class ScreenCapturer; class WindowInfosListenerInvoker; +using ::aidl::android::hardware::drm::HdcpLevels; using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent; using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; using frontend::TransactionHandler; @@ -297,8 +298,6 @@ public: // the client can no longer modify this layer directly. void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId); - std::vector<Layer*> mLayerMirrorRoots; - TransactionCallbackInvoker& getTransactionCallbackInvoker() { return mTransactionCallbackInvoker; } @@ -389,11 +388,10 @@ private: class State { public: - explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {} + explicit State(LayerVector::StateSet set) : stateSet(set) {} State& operator=(const State& other) { // We explicitly don't copy stateSet so that, e.g., mDrawingState // always uses the Drawing StateSet. - layersSortedByZ = other.layersSortedByZ; displays = other.displays; colorMatrixChanged = other.colorMatrixChanged; if (colorMatrixChanged) { @@ -405,7 +403,6 @@ private: } const LayerVector::StateSet stateSet = LayerVector::StateSet::Invalid; - LayerVector layersSortedByZ; // TODO(b/241285876): Replace deprecated DefaultKeyedVector with ftl::SmallMap. DefaultKeyedVector<wp<IBinder>, DisplayDeviceState> displays; @@ -425,10 +422,6 @@ private: mat4 colorMatrix; ShadowSettings globalShadowSettings; - - void traverse(const LayerVector::Visitor& visitor) const; - void traverseInZOrder(const LayerVector::Visitor& visitor) const; - void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const; }; // Keeps track of pending buffers per layer handle in the transaction queue or current/drawing @@ -679,6 +672,7 @@ private: void onComposerHalSeamlessPossible(hal::HWDisplayId) override; void onComposerHalVsyncIdle(hal::HWDisplayId) override; void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override; + void onComposerHalHdcpLevelsChanged(hal::HWDisplayId, const HdcpLevels& levels) override; // ICompositor overrides: void configure() override REQUIRES(kMainThreadContext); @@ -791,9 +785,9 @@ private: REQUIRES(mStateLock, kMainThreadContext); // Flush pending transactions that were presented after desiredPresentTime. // For test only - bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext); + bool flushTransactionQueues() REQUIRES(kMainThreadContext); - bool applyTransactions(std::vector<TransactionState>&, VsyncId) REQUIRES(kMainThreadContext); + bool applyTransactions(std::vector<TransactionState>&) REQUIRES(kMainThreadContext); bool applyAndCommitDisplayTransactionStatesLocked(std::vector<TransactionState>& transactions) REQUIRES(kMainThreadContext, mStateLock); @@ -823,7 +817,7 @@ private: static LatchUnsignaledConfig getLatchUnsignaledConfig(); bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const; - bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId) + bool applyTransactionsLocked(std::vector<TransactionState>& transactions) REQUIRES(mStateLock, kMainThreadContext); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) @@ -847,8 +841,6 @@ private: status_t mirrorDisplay(DisplayId displayId, const LayerCreationArgs& args, gui::CreateSurfaceResult& outResult); - void markLayerPendingRemovalLocked(const sp<Layer>& layer) REQUIRES(mStateLock); - // add a layer to SurfaceFlinger status_t addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle, const sp<Layer>& layer, const wp<Layer>& parentLayer, @@ -869,7 +861,7 @@ private: void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction, ui::Size bufferSize, ui::PixelFormat, bool allowProtected, - bool grayscale, const sp<IScreenCaptureListener>&); + bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>&); std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder( RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext); @@ -883,20 +875,21 @@ private: ftl::SharedFuture<FenceResult> captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener, + bool grayscale, bool isProtected, bool attachGainmap, + const sp<IScreenCaptureListener>& captureListener, std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs); ftl::SharedFuture<FenceResult> captureScreenshotLegacy( RenderAreaBuilderVariant, GetLayerSnapshotsFunction, const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, - bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&); + bool grayscale, bool isProtected, bool attachGainmap, + const sp<IScreenCaptureListener>&); ftl::SharedFuture<FenceResult> renderScreenImpl( - std::unique_ptr<const RenderArea>, - const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, - bool grayscale, bool isProtected, ScreenCaptureResults&, - std::optional<OutputCompositionState>& displayState, + const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&, + bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, + ScreenCaptureResults&, std::optional<OutputCompositionState>& displayState, std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs); @@ -1199,7 +1192,6 @@ private: State mCurrentState{LayerVector::StateSet::Current}; std::atomic<int32_t> mTransactionFlags = 0; std::atomic<uint32_t> mUniqueTransactionId = 1; - SortedVector<sp<Layer>> mLayersPendingRemoval; // Buffers that have been discarded by clients and need to be evicted from per-layer caches so // the graphics memory can be immediately freed. @@ -1239,7 +1231,6 @@ private: // TODO: Also move visibleRegions over to a boolean system. bool mUpdateInputInfo = false; bool mSomeChildrenChanged; - bool mForceTransactionDisplayChange = false; bool mUpdateAttachedChoreographer = false; struct LayerIntHash { @@ -1270,7 +1261,6 @@ private: }; bool mIsHdcpViaNegVsync = false; - bool mIsHotplugErrViaNegVsync = false; std::mutex mHotplugMutex; std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex); @@ -1389,13 +1379,6 @@ private: std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners GUARDED_BY(mStateLock); - mutable std::mutex mCreatedLayersLock; - - // A temporay pool that store the created layers and will be added to current state in main - // thread. - std::vector<LayerCreatedState> mCreatedLayers GUARDED_BY(mCreatedLayersLock); - void handleLayerCreatedLocked(const LayerCreatedState&, VsyncId) REQUIRES(mStateLock); - std::atomic<ui::Transform::RotationFlags> mActiveDisplayTransformHint; // Must only be accessed on the main thread. @@ -1436,6 +1419,8 @@ private: frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext); frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext); + mutable std::mutex mCreatedLayersLock; + std::vector<sp<Layer>> mCreatedLayers GUARDED_BY(mCreatedLayersLock); std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles GUARDED_BY(mCreatedLayersLock); std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers GUARDED_BY(mCreatedLayersLock); diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index 881bf35b58..c6856aea75 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -149,6 +149,13 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& handle->transformHint, handle->currentMaxAcquiredBufferCount, eventStats, handle->previousReleaseCallbackId); + if (handle->bufferReleaseChannel && + handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) { + mBufferReleases.emplace_back(handle->bufferReleaseChannel, + handle->previousReleaseCallbackId, + handle->previousReleaseFence, + handle->currentMaxAcquiredBufferCount); + } } return NO_ERROR; } @@ -158,6 +165,12 @@ void TransactionCallbackInvoker::addPresentFence(sp<Fence> presentFence) { } void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { + for (const auto& bufferRelease : mBufferReleases) { + bufferRelease.channel->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence, + bufferRelease.currentMaxAcquiredBufferCount); + } + mBufferReleases.clear(); + // For each listener auto completedTransactionsItr = mCompletedTransactions.begin(); ftl::SmallVector<ListenerStats, 10> listenerStatsToSend; diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index 7853a9f359..14a7487156 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -16,18 +16,14 @@ #pragma once -#include <condition_variable> #include <deque> -#include <mutex> #include <optional> -#include <queue> -#include <thread> #include <unordered_map> -#include <unordered_set> #include <android-base/thread_annotations.h> #include <binder/IBinder.h> #include <ftl/future.h> +#include <gui/BufferReleaseChannel.h> #include <gui/ITransactionCompletedListener.h> #include <ui/Fence.h> #include <ui/FenceResult.h> @@ -59,6 +55,7 @@ public: uint64_t frameNumber = 0; uint64_t previousFrameNumber = 0; ReleaseCallbackId previousReleaseCallbackId = ReleaseCallbackId::INVALID_ID; + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel; }; class TransactionCallbackInvoker { @@ -86,6 +83,14 @@ private: std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash> mCompletedTransactions; + struct BufferRelease { + std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> channel; + ReleaseCallbackId callbackId; + sp<Fence> fence; + uint32_t currentMaxAcquiredBufferCount; + }; + std::vector<BufferRelease> mBufferReleases; + sp<Fence> mPresentFence; }; diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 07c720f670..12d6138675 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -119,7 +119,6 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(connected_display); DUMP_READ_ONLY_FLAG(enable_small_area_detection); DUMP_READ_ONLY_FLAG(frame_rate_category_mrr); - DUMP_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr); DUMP_READ_ONLY_FLAG(misc1); DUMP_READ_ONLY_FLAG(vrr_config); DUMP_READ_ONLY_FLAG(hotplug2); @@ -144,17 +143,20 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(ce_fence_promise); DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout); DUMP_READ_ONLY_FLAG(graphite_renderengine); + DUMP_READ_ONLY_FLAG(filter_frames_before_trace_starts); DUMP_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed); DUMP_READ_ONLY_FLAG(deprecate_vsync_sf); DUMP_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter); DUMP_READ_ONLY_FLAG(detached_mirror); DUMP_READ_ONLY_FLAG(commit_not_composited); + DUMP_READ_ONLY_FLAG(correct_dpi_with_display_size); DUMP_READ_ONLY_FLAG(local_tonemap_screenshots); DUMP_READ_ONLY_FLAG(override_trusted_overlay); DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache); DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine); DUMP_READ_ONLY_FLAG(single_hop_screenshot); DUMP_READ_ONLY_FLAG(trace_frame_rate_override); + DUMP_READ_ONLY_FLAG(true_hdr_screenshots); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -223,8 +225,6 @@ FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED, FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "") FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr") -FLAG_MANAGER_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr, - "debug.sf.view_set_requested_frame_rate_mrr") FLAG_MANAGER_READ_ONLY_FLAG(misc1, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config") FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "") @@ -249,16 +249,19 @@ FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, ""); FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "") FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, ""); FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite") +FLAG_MANAGER_READ_ONLY_FLAG(filter_frames_before_trace_starts, "") FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, ""); FLAG_MANAGER_READ_ONLY_FLAG(deprecate_vsync_sf, ""); FLAG_MANAGER_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter, ""); FLAG_MANAGER_READ_ONLY_FLAG(detached_mirror, ""); FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, ""); +FLAG_MANAGER_READ_ONLY_FLAG(correct_dpi_with_display_size, ""); FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots"); FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, ""); FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, ""); FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, ""); FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, ""); +FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots"); /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index a4b4a2b9f1..a1be19421b 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -56,7 +56,6 @@ public: /// Trunk stable readonly flags /// bool connected_display() const; bool frame_rate_category_mrr() const; - bool view_set_requested_frame_rate_mrr() const; bool enable_small_area_detection() const; bool misc1() const; bool vrr_config() const; @@ -82,17 +81,20 @@ public: bool ce_fence_promise() const; bool idle_screen_refresh_rate_timeout() const; bool graphite_renderengine() const; + bool filter_frames_before_trace_starts() const; bool latch_unsignaled_with_auto_refresh_changed() const; bool deprecate_vsync_sf() const; bool allow_n_vsyncs_in_targeter() const; bool detached_mirror() const; bool commit_not_composited() const; + bool correct_dpi_with_display_size() const; bool local_tonemap_screenshots() const; bool override_trusted_overlay() const; bool flush_buffer_slots_to_uncache() const; bool force_compile_graphite_renderengine() const; bool single_hop_screenshot() const; bool trace_frame_rate_override() const; + bool true_hdr_screenshots() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index 886167e9f2..102e2b643c 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -4,10 +4,10 @@ package: "com.android.graphics.surfaceflinger.flags" container: "system" flag { - name: "adpf_gpu_sf" - namespace: "game" - description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL" - bug: "284324521" + name: "adpf_gpu_sf" + namespace: "game" + description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL" + bug: "284324521" } # adpf_gpu_sf flag { @@ -21,18 +21,29 @@ flag { } } # ce_fence_promise - flag { - name: "commit_not_composited" - namespace: "core_graphics" - description: "mark frames as non janky if the transaction resulted in no composition" - bug: "340633280" - is_fixed_read_only: true - metadata { - purpose: PURPOSE_BUGFIX - } - } # commit_not_composited +flag { + name: "commit_not_composited" + namespace: "core_graphics" + description: "mark frames as non janky if the transaction resulted in no composition" + bug: "340633280" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # commit_not_composited - flag { +flag { + name: "correct_dpi_with_display_size" + namespace: "core_graphics" + description: "indicate whether missing or likely incorrect dpi should be corrected using the display size." + bug: "328425848" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # correct_dpi_with_display_size + +flag { name: "deprecate_vsync_sf" namespace: "core_graphics" description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere" @@ -43,7 +54,7 @@ flag { } } # deprecate_vsync_sf - flag { +flag { name: "detached_mirror" namespace: "window_surfaces" description: "Ignore local transform when mirroring a partial hierarchy" @@ -55,6 +66,17 @@ flag { } # detached_mirror flag { + name: "filter_frames_before_trace_starts" + namespace: "core_graphics" + description: "Do not trace FrameTimeline events for frames started before the trace started" + bug: "364194637" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # filter_frames_before_trace_starts + +flag { name: "flush_buffer_slots_to_uncache" namespace: "core_graphics" description: "Flush DisplayCommands for disabled displays in order to uncache requested buffers." @@ -96,11 +118,11 @@ flag { } # latch_unsignaled_with_auto_refresh_changed flag { - name: "local_tonemap_screenshots" - namespace: "core_graphics" - description: "Enables local tonemapping when capturing screenshots" - bug: "329464641" - is_fixed_read_only: true + name: "local_tonemap_screenshots" + namespace: "core_graphics" + description: "Enables local tonemapping when capturing screenshots" + bug: "329464641" + is_fixed_read_only: true } # local_tonemap_screenshots flag { @@ -114,6 +136,14 @@ flag { } } # single_hop_screenshot +flag { + name: "true_hdr_screenshots" + namespace: "core_graphics" + description: "Enables screenshotting display content in HDR, sans tone mapping" + bug: "329470026" + is_fixed_read_only: true +} # true_hdr_screenshots + flag { name: "override_trusted_overlay" namespace: "window_surfaces" @@ -126,11 +156,11 @@ flag { } # override_trusted_overlay flag { - name: "view_set_requested_frame_rate_mrr" - namespace: "core_graphics" - description: "Enable to use frame rate category NoPreference with fixed frame rate vote on MRR devices" - bug: "352206100" - is_fixed_read_only: true + name: "view_set_requested_frame_rate_mrr" + namespace: "core_graphics" + description: "Enable to use frame rate category NoPreference with fixed frame rate vote on MRR devices" + bug: "352206100" + is_fixed_read_only: true } # view_set_requested_frame_rate_mrr flag { diff --git a/services/surfaceflinger/tests/OWNERS b/services/surfaceflinger/tests/OWNERS index 56f2f1b07a..7857961ce4 100644 --- a/services/surfaceflinger/tests/OWNERS +++ b/services/surfaceflinger/tests/OWNERS @@ -4,5 +4,5 @@ per-file HdrSdrRatioOverlay_test.cpp = alecmouri@google.com, sallyqi@google.com, per-file Layer* = set noparent per-file Layer* = pdwilliams@google.com, vishnun@google.com, melodymhsu@google.com -per-file LayerHistoryTest.cpp = file:/services/surfaceflinger/OWNERS +per-file LayerHistoryIntegrationTest.cpp = file:/services/surfaceflinger/OWNERS per-file LayerInfoTest.cpp = file:/services/surfaceflinger/OWNERS
\ No newline at end of file diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h index 3104dd4720..ae380ad459 100644 --- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -515,6 +515,23 @@ public: mLifecycleManager.applyTransactions(transactions); } + void setEdgeExtensionEffect(uint32_t id, int edge) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.what |= layer_state_t::eEdgeExtensionChanged; + transactions.back().states.front().state.edgeExtensionParameters = + gui::EdgeExtensionParameters(); + transactions.back().states.front().state.edgeExtensionParameters.extendLeft = edge & LEFT; + transactions.back().states.front().state.edgeExtensionParameters.extendRight = edge & RIGHT; + transactions.back().states.front().state.edgeExtensionParameters.extendTop = edge & TOP; + transactions.back().states.front().state.edgeExtensionParameters.extendBottom = + edge & BOTTOM; + mLifecycleManager.applyTransactions(transactions); + } + private: LayerLifecycleManager& mLifecycleManager; }; diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index fa31643df1..9b10c94bcd 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -64,17 +64,6 @@ DisplayTransactionTest::~DisplayTransactionTest() { void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) { LOG_ALWAYS_FATAL_IF(mFlinger.scheduler()); - - EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*mEventThread, createEventConnection(_, _)) - .WillOnce(Return( - sp<EventThreadConnection>::make(mEventThread, mock::EventThread::kCallingUid))); - - EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*mSFEventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(mSFEventThread, - mock::EventThread::kCallingUid))); - mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(), std::make_shared<mock::VSyncTracker>(), std::unique_ptr<EventThread>(mEventThread), diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 9be0fc38b3..0dfbd6185e 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -84,9 +84,11 @@ public: void SetUp() override { constexpr bool kUseBootTimeClock = true; + constexpr bool kFilterFramesBeforeTraceStarts = false; mTimeStats = std::make_shared<mock::TimeStats>(); mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid, - kTestThresholds, !kUseBootTimeClock); + kTestThresholds, !kUseBootTimeClock, + kFilterFramesBeforeTraceStarts); mFrameTimeline->registerDataSource(); mTokenManager = &mFrameTimeline->mTokenManager; mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter; diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 2cff2f2929..e0753a3cfb 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -58,6 +58,7 @@ using namespace std::chrono_literals; using Hwc2::Config; +using ::aidl::android::hardware::drm::HdcpLevels; using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent; using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData; using hal::IComposerClient; @@ -165,6 +166,7 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { expectHotplugConnect(kHwcDisplayId); const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); ASSERT_TRUE(info); + ASSERT_TRUE(info->preferredDetailedTimingDescriptor.has_value()); EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false)); @@ -178,6 +180,10 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { constexpr int32_t kHeight = 720; constexpr int32_t kConfigGroup = 1; constexpr int32_t kVsyncPeriod = 16666667; + constexpr float kMmPerInch = 25.4f; + const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; + const float expectedDpiX = (kWidth * kMmPerInch / size.width); + const float expectedDpiY = (kHeight * kMmPerInch / size.height); EXPECT_CALL(*mHal, getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH, @@ -217,8 +223,13 @@ TEST_F(HWComposerTest, getModesWithLegacyDisplayConfigs) { EXPECT_EQ(modes.front().height, kHeight); EXPECT_EQ(modes.front().configGroup, kConfigGroup); EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod); - EXPECT_EQ(modes.front().dpiX, -1); - EXPECT_EQ(modes.front().dpiY, -1); + if (!FlagManager::getInstance().correct_dpi_with_display_size()) { + EXPECT_EQ(modes.front().dpiX, -1); + EXPECT_EQ(modes.front().dpiY, -1); + } else { + EXPECT_EQ(modes.front().dpiX, expectedDpiX); + EXPECT_EQ(modes.front().dpiY, expectedDpiY); + } // Optional parameters are supported constexpr int32_t kDpi = 320; @@ -270,6 +281,10 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) { constexpr int32_t kHeight = 720; constexpr int32_t kConfigGroup = 1; constexpr int32_t kVsyncPeriod = 16666667; + constexpr float kMmPerInch = 25.4f; + const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; + const float expectedDpiX = (kWidth * kMmPerInch / size.width); + const float expectedDpiY = (kHeight * kMmPerInch / size.height); EXPECT_CALL(*mHal, getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH, @@ -309,8 +324,13 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_OFF) { EXPECT_EQ(modes.front().height, kHeight); EXPECT_EQ(modes.front().configGroup, kConfigGroup); EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod); - EXPECT_EQ(modes.front().dpiX, -1); - EXPECT_EQ(modes.front().dpiY, -1); + if (!FlagManager::getInstance().correct_dpi_with_display_size()) { + EXPECT_EQ(modes.front().dpiX, -1); + EXPECT_EQ(modes.front().dpiY, -1); + } else { + EXPECT_EQ(modes.front().dpiX, expectedDpiX); + EXPECT_EQ(modes.front().dpiY, expectedDpiY); + } // Optional parameters are supported constexpr int32_t kDpi = 320; @@ -360,6 +380,10 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { constexpr int32_t kHeight = 720; constexpr int32_t kConfigGroup = 1; constexpr int32_t kVsyncPeriod = 16666667; + constexpr float kMmPerInch = 25.4f; + const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm; + const float expectedDpiX = (kWidth * kMmPerInch / size.width); + const float expectedDpiY = (kHeight * kMmPerInch / size.height); const hal::VrrConfig vrrConfig = hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(), .notifyExpectedPresentConfig = hal::VrrConfig:: @@ -386,8 +410,13 @@ TEST_F(HWComposerTest, getModesWithDisplayConfigurations_VRR_ON) { EXPECT_EQ(modes.front().configGroup, kConfigGroup); EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod); EXPECT_EQ(modes.front().vrrConfig, vrrConfig); - EXPECT_EQ(modes.front().dpiX, -1); - EXPECT_EQ(modes.front().dpiY, -1); + if (!FlagManager::getInstance().correct_dpi_with_display_size()) { + EXPECT_EQ(modes.front().dpiX, -1); + EXPECT_EQ(modes.front().dpiY, -1); + } else { + EXPECT_EQ(modes.front().dpiX, expectedDpiX); + EXPECT_EQ(modes.front().dpiY, expectedDpiY); + } // Supports optional dpi parameter constexpr int32_t kDpi = 320; @@ -454,6 +483,8 @@ struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId)); MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId)); MOCK_METHOD(void, onRefreshRateChangedDebug, (const RefreshRateChangedDebugData&), (override)); + MOCK_METHOD(void, onComposerHalHdcpLevelsChanged, (hal::HWDisplayId, const HdcpLevels&), + (override)); }; struct HWComposerSetCallbackTest : HWComposerTest { diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 7e84408f7d..de37b6342c 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -894,7 +894,6 @@ TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) { TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithFixedSourceAndNoPreferenceCategory) { SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false); - SET_FLAG_FOR_TEST(flags::view_set_requested_frame_rate_mrr, true); auto layer = createLegacyAndFrontedEndLayer(1); setFrameRate(1, (45.6_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp index b4efe0fe14..c7cc21ce07 100644 --- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -619,4 +619,14 @@ TEST_F(LayerLifecycleManagerTest, isSimpleBufferUpdate) { } } +TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) { + // By default the layer has no buffer, so it doesn't need an input info + EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo()); + + setBuffer(111); + mLifecycleManager.commitChanges(); + + EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 2860345538..75d2fa3c7f 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -27,6 +27,7 @@ #include "LayerHierarchyTest.h" #include "ui/GraphicTypes.h" +#include <com_android_graphics_libgui_flags.h> #include <com_android_graphics_surfaceflinger_flags.h> #define UPDATE_AND_VERIFY(BUILDER, ...) \ @@ -1761,4 +1762,177 @@ TEST_F(LayerSnapshotTest, hideLayerWithNanMatrix) { UPDATE_AND_VERIFY(mSnapshotBuilder, {2}); EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy()); } + +TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) { + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + setCrop(1, Rect(0, 0, 20, 20)); + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(20 /* width */, + 20 /* height */, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + setEdgeExtensionEffect(12, LEFT); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(LEFT)); + EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(LEFT)); + EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(LEFT)); + + setEdgeExtensionEffect(12, RIGHT); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(RIGHT)); + EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(RIGHT)); + EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(RIGHT)); + + setEdgeExtensionEffect(12, TOP); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(TOP)); + EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(TOP)); + EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(TOP)); + + setEdgeExtensionEffect(12, BOTTOM); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(BOTTOM)); + EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(BOTTOM)); + EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(BOTTOM)); +} + +TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The left bound is extended when shifting to the right + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + setCrop(1, Rect(0, 0, 20, 20)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = 5.0; + setPosition(12, translation, 0); + setEdgeExtensionEffect(12, LEFT); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation); + EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.left, translation); + EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.left, 0.0); +} + +TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The right bound is extended when shifting to the left + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + const int crop = 20; + setCrop(1, Rect(0, 0, crop, crop)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = -5.0; + setPosition(12, translation, 0); + setEdgeExtensionEffect(12, RIGHT); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.left, 0); + EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation); + EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.right, (float)crop); +} + +TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The top bound is extended when shifting to the bottom + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + setCrop(1, Rect(0, 0, 20, 20)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = 5.0; + setPosition(12, 0, translation); + setEdgeExtensionEffect(12, TOP); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize + translation); + EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.top, translation); + EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0.0); +} + +TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The bottom bound is extended when shifting to the top + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + const int crop = 20; + setCrop(1, Rect(0, 0, crop, crop)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = -5.0; + setPosition(12, 0, translation); + setEdgeExtensionEffect(12, BOTTOM); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.top, 0); + EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize - translation); + EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.bottom, (float)crop); +} + +TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) { + // The left bound is extended when shifting to the right + if (!com::android::graphics::libgui::flags::edge_extension_shader()) { + GTEST_SKIP() << "Skipping test because edge_extension_shader is off"; + } + const int crop = 20; + setCrop(1, Rect(0, 0, crop, crop)); + const int texSize = 10; + setBuffer(1221, + std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */, + texSize /* height*/, + 42ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + 0 /*usage*/)); + const float translation = 5.0; + setPosition(12, translation, translation); + setEdgeExtensionEffect(12, LEFT | RIGHT | TOP | BOTTOM); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation); + EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.right, (float)crop); + EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.left, translation); + EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.left, 0.0); + EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize + translation); + EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.bottom, (float)crop); + EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.top, translation); + EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0); +} + +TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) { + // By default the layer has no buffer, so we don't expect it to have an input info + EXPECT_FALSE(getSnapshot(111)->hasInputInfo()); + + setBuffer(111); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_TRUE(getSnapshot(111)->hasInputInfo()); + EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test( + gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL)); + EXPECT_FALSE(getSnapshot(2)->hasInputInfo()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 06c4e30989..9efe73d450 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1837,6 +1837,43 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_12 } } +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_vrrHighHintTouch_primaryRangeIsSingleRate) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + SET_FLAG_FOR_TEST(flags::vrr_config, true); + + auto selector = createSelector(kVrrMode_120, kModeId120); + selector.setActiveMode(kModeId120, 60_Hz); + + // Change primary physical range to be single rate, which on VRR device should not affect + // fps scoring. + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}})); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + layers[0].vote = LayerVoteType::ExplicitCategory; + layers[0].frameRateCategory = FrameRateCategory::HighHint; + layers[0].name = "ExplicitCategory HighHint"; + + auto actualRankedFrameRates = selector.getRankedFrameRates(layers); + // Expect late touch boost from HighHint. + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); + + layers[1].vote = LayerVoteType::ExplicitExactOrMultiple; + layers[1].desiredRefreshRate = 30_Hz; + layers[1].name = "ExplicitExactOrMultiple 30Hz"; + + actualRankedFrameRates = selector.getRankedFrameRates(layers); + // Expect late touch boost from HighHint. + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); +} + TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighHint) { auto selector = createSelector(makeModes(kMode24, kMode30, kMode60, kMode120), kModeId60); @@ -1955,7 +1992,7 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH // Gets touch boost EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); - EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); } TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_TouchBoost) { @@ -2049,7 +2086,7 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_Touch lr2.name = "Max"; actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true}); EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode); - EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); lr1.vote = LayerVoteType::ExplicitCategory; lr1.frameRateCategory = FrameRateCategory::Normal; diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 358f6b0cf5..ac09cbcea6 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -124,7 +124,7 @@ SchedulerTest::SchedulerTest() { // 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(_, _)) + EXPECT_CALL(*mEventThread, createEventConnection(_)) .WillRepeatedly(Return(mEventThreadConnection)); mScheduler->setEventThread(Cycle::Render, std::move(eventThread)); @@ -797,7 +797,7 @@ TEST_F(AttachedChoreographerTest, registerMultipleOnSameLayer) { const auto mockConnection1 = sp<MockEventThreadConnection>::make(mEventThread); const auto mockConnection2 = sp<MockEventThreadConnection>::make(mEventThread); - EXPECT_CALL(*mEventThread, createEventConnection(_, _)) + EXPECT_CALL(*mEventThread, createEventConnection(_)) .WillOnce(Return(mockConnection1)) .WillOnce(Return(mockConnection2)); @@ -884,7 +884,6 @@ TEST_F(AttachedChoreographerTest, removedWhenLayerIsGone) { mScheduler->createDisplayEventConnection(Cycle::Render, {}, layer->getHandle()); layer.clear(); - mFlinger.mutableLayersPendingRemoval().clear(); EXPECT_TRUE(mScheduler->mutableAttachedChoreographers().empty()); } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 4b0a7c3a04..86996214b5 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -187,16 +187,6 @@ void DisplayModeSwitchingTest::setupScheduler( mAppEventThread = eventThread.get(); auto sfEventThread = std::make_unique<mock::EventThread>(); - EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), - mock::EventThread::kCallingUid))); - - EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), - mock::EventThread::kCallingUid))); - auto vsyncController = std::make_unique<mock::VsyncController>(); auto vsyncTracker = std::make_shared<mock::VSyncTracker>(); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp index 933d03dac1..352000ef9a 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp @@ -239,7 +239,7 @@ void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { ASSERT_TRUE(displayId); const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; ASSERT_TRUE(hwcDisplayId); - mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId); + mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, std::nullopt); DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID) .setResolution(Case::Display::RESOLUTION) .setVsyncPeriod(DEFAULT_VSYNC_PERIOD) diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index df16b2e058..9de3346fb0 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -74,10 +74,8 @@ public: void setEventThread(Cycle cycle, std::unique_ptr<EventThread> eventThreadPtr) { if (cycle == Cycle::Render) { mRenderEventThread = std::move(eventThreadPtr); - mRenderEventConnection = mRenderEventThread->createEventConnection(); } else { mLastCompositeEventThread = std::move(eventThreadPtr); - mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection(); } } @@ -133,7 +131,9 @@ public: using Scheduler::resyncAllToHardwareVsync; auto& mutableLayerHistory() { return mLayerHistory; } - auto& mutableAttachedChoreographers() { return mAttachedChoreographers; } + auto& mutableAttachedChoreographers() NO_THREAD_SAFETY_ANALYSIS { + return mAttachedChoreographers; + } size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size(); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index a6a275804d..c043b880ec 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -16,7 +16,6 @@ #pragma once -#include <algorithm> #include <chrono> #include <memory> #include <variant> @@ -44,7 +43,6 @@ #include "Layer.h" #include "NativeWindowSurface.h" #include "RenderArea.h" -#include "Scheduler/MessageQueue.h" #include "Scheduler/RefreshRateSelector.h" #include "SurfaceFlinger.h" #include "TestableScheduler.h" @@ -60,7 +58,6 @@ #include "Scheduler/VSyncTracker.h" #include "Scheduler/VsyncController.h" -#include "mock/MockVSyncDispatch.h" #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" @@ -88,9 +85,7 @@ class Factory final : public surfaceflinger::Factory { public: ~Factory() = default; - std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { - return nullptr; - } + std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; } std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( Fps /*currentRefreshRate*/) override { @@ -276,17 +271,6 @@ public: auto eventThread = makeMock<mock::EventThread>(options.useNiceMock); auto sfEventThread = makeMock<mock::EventThread>(options.useNiceMock); - - EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(), - mock::EventThread::kCallingUid))); - - EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); - EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(), - mock::EventThread::kCallingUid))); - auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock); auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock); @@ -335,13 +319,6 @@ public: return mFlinger->mLegacyLayers[layerId]->findOutputLayerForDisplay(display.get()); } - static void setLayerSidebandStream(const sp<Layer>& layer, - const sp<NativeHandle>& sidebandStream) { - layer->mDrawingState.sidebandStream = sidebandStream; - layer->mSidebandStream = sidebandStream; - layer->editLayerSnapshot()->sidebandStream = sidebandStream; - } - void setLayerCompositionType(const sp<Layer>& layer, aidl::android::hardware::graphics::composer3::Composition type) { auto outputLayer = findOutputLayerForDisplay(static_cast<uint32_t>(layer->sequence), @@ -352,10 +329,6 @@ public: (*state.hwc).hwcCompositionType = type; } - static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) { - layer->mDrawingParent = drawingParent; - } - /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ @@ -501,9 +474,10 @@ public: auto layers = getLayerSnapshotsFn(); auto layerFEs = mFlinger->extractLayerFEs(layers); - return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling, + return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling, false /* grayscale */, false /* isProtected */, - captureResults, displayState, layers, layerFEs); + false /* attachGainmap */, captureResults, displayState, + layers, layerFEs); } auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) { @@ -512,12 +486,14 @@ public: } auto getDisplayNativePrimaries(const sp<IBinder>& displayToken, - ui::DisplayPrimaries &primaries) { + ui::DisplayPrimaries& primaries) { return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries); } - auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; } - auto& getPendingTransactionQueue() { + auto& getTransactionQueue() NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mTransactionHandler.mLocklessTransactionQueue; + } + auto& getPendingTransactionQueue() NO_THREAD_SAFETY_ANALYSIS { ftl::FakeGuard guard(kMainThreadContext); return mFlinger->mTransactionHandler.mPendingTransactionQueues; } @@ -547,7 +523,7 @@ public: } auto flushTransactionQueues() { - return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->flushTransactionQueues(kVsyncId)); + return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->flushTransactionQueues()); } auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -685,8 +661,10 @@ public: * post-conditions. */ - const auto& displays() const { return mFlinger->mDisplays; } - const auto& physicalDisplays() const { return mFlinger->mPhysicalDisplays; } + const auto& displays() const NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mDisplays; } + const auto& physicalDisplays() const NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mPhysicalDisplays; + } const auto& currentState() const { return mFlinger->mCurrentState; } const auto& drawingState() const { return mFlinger->mDrawingState; } const auto& transactionFlags() const { return mFlinger->mTransactionFlags; } @@ -699,13 +677,17 @@ public: auto& mutableDisplayModeController() { return mFlinger->mDisplayModeController; } auto& mutableCurrentState() { return mFlinger->mCurrentState; } auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; } - auto& mutableDisplays() { return mFlinger->mDisplays; } - auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; } + auto& mutableDisplays() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mDisplays; } + auto& mutablePhysicalDisplays() NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mPhysicalDisplays; + } auto& mutableDrawingState() { return mFlinger->mDrawingState; } auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; } auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } - auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; } + auto& mutablePendingHotplugEvents() NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mPendingHotplugEvents; + } auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; } auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; } @@ -713,7 +695,7 @@ public: auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; } auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; } auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; } - auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; } + auto& mutableActiveDisplayId() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mActiveDisplayId; } auto& mutablePreviouslyComposedLayers() { return mFlinger->mPreviouslyComposedLayers; } auto& mutableActiveDisplayRotationFlags() { @@ -721,8 +703,9 @@ public: } auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; } - auto& mutableLayersPendingRemoval() { return mFlinger->mLayersPendingRemoval; } - auto& mutableLayerSnapshotBuilder() { return mFlinger->mLayerSnapshotBuilder; }; + auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS { + return mFlinger->mLayerSnapshotBuilder; + } auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); } @@ -789,7 +772,6 @@ public: mutableDisplays().clear(); mutableCurrentState().displays.clear(); mutableDrawingState().displays.clear(); - mFlinger->mLayersPendingRemoval.clear(); mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); mFlinger->mRenderEngine = std::unique_ptr<renderengine::RenderEngine>(); diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp index 7bf167498b..f8f08c78fd 100644 --- a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp @@ -20,6 +20,7 @@ #include <gui/SurfaceComposerClient.h> #include <cstdint> #include "Client.h" +#include "Layer.h" #include <layerproto/LayerProtoHeader.h> #include "FrontEnd/LayerCreationArgs.h" diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp index 9f6065b683..e27af0e62c 100644 --- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp @@ -128,12 +128,10 @@ TEST_F(TunnelModeEnabledReporterTest, callsNewListenerWithFreshInformation) { NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM), false); layer->setSidebandStream(stream, FrameTimelineInfo{}, 20, gui::GameMode::Unsupported); - mFlinger.mutableCurrentState().layersSortedByZ.add(layer); mTunnelModeEnabledReporter->updateTunnelModeStatus(); mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener); EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled); mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener); - mFlinger.mutableCurrentState().layersSortedByZ.remove(layer); layer = nullptr; mTunnelModeEnabledReporter->updateTunnelModeStatus(); @@ -154,12 +152,9 @@ TEST_F(TunnelModeEnabledReporterTest, layerWithSidebandStreamTriggersUpdate) { layerWithSidebandStream->setSidebandStream(stream, FrameTimelineInfo{}, 20, gui::GameMode::Unsupported); - mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer); - mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream); mTunnelModeEnabledReporter->updateTunnelModeStatus(); EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled); - mFlinger.mutableCurrentState().layersSortedByZ.remove(layerWithSidebandStream); layerWithSidebandStream = nullptr; mTunnelModeEnabledReporter->updateTunnelModeStatus(); EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled); diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 7c678bd811..918107d270 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -915,7 +915,8 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { vrrTracker.onFrameBegin(TimePoint::fromNs(7000), {TimePoint::fromNs(6500), TimePoint::fromNs(6500)}); - EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000)); + EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 7000)); + EXPECT_EQ(9500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000)); } TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) { diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index e380e19797..f472d8fefe 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -180,8 +180,12 @@ public: MOCK_METHOD1(onHotplugDisconnect, void(Display)); MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool)); MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t)); - MOCK_METHOD(Error, getDisplayLuts, - (Display, std::vector<aidl::android::hardware::graphics::composer3::Lut>*)); + MOCK_METHOD( + Error, getRequestedLuts, + (Display, + std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)); + MOCK_METHOD(Error, setLayerLuts, + (Display, Layer, std::vector<aidl::android::hardware::graphics::composer3::Lut>&)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 1eda3586c5..5edd2cd9e3 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -35,6 +35,7 @@ public: (const, override)); MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override)); MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override)); + MOCK_METHOD(std::optional<ui::Size>, getPhysicalSizeInMm, (), (const override)); MOCK_METHOD(hal::Error, acceptChanges, (), (override)); MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (), @@ -109,8 +110,9 @@ public: MOCK_METHOD(hal::Error, getOverlaySupport, (aidl::android::hardware::graphics::composer3::OverlayProperties *), (const override)); - MOCK_METHOD(hal::Error, getDisplayLuts, - (std::vector<aidl::android::hardware::graphics::composer3::Lut>*), (override)); + MOCK_METHOD(hal::Error, getRequestedLuts, + (std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*), + (override)); }; class Layer : public HWC2::Layer { @@ -145,6 +147,8 @@ public: (const std::string &, bool, const std::vector<uint8_t> &), (override)); MOCK_METHOD(hal::Error, setBrightness, (float), (override)); MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override)); + MOCK_METHOD(hal::Error, setLuts, + (std::vector<aidl::android::hardware::graphics::composer3::Lut>&), (override)); }; } // namespace android::HWC2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 8dd1a34ec4..7398cbebe3 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -24,21 +24,11 @@ namespace android::mock { class EventThread : public android::EventThread { public: - static constexpr auto kCallingUid = static_cast<uid_t>(0); - EventThread(); ~EventThread() override; - // TODO(b/302035909): workaround otherwise gtest complains about - // error: no viable conversion from - // 'tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration> &&>' to 'const - // tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration>>' - sp<EventThreadConnection> createEventConnection(EventRegistrationFlags flags) const override { - return createEventConnection(false, flags); - } - MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (bool, EventRegistrationFlags), - (const)); - + MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags), + (const, override)); MOCK_METHOD(void, enableSyntheticVsync, (bool), (override)); MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override)); MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index fdb6f4dafe..45f86fa1a8 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -31,15 +31,11 @@ public: explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {} - MOCK_CONST_METHOD0(getType, const char*()); MOCK_METHOD0(getFrameSelectionPriority, int32_t()); - MOCK_CONST_METHOD0(isVisible, bool()); MOCK_METHOD0(createClone, sp<Layer>()); MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate()); MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility()); MOCK_CONST_METHOD0(getOwnerUid, uid_t()); - MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace()); - MOCK_METHOD(bool, isFrontBuffered, (), (const, override)); }; } // namespace android::mock diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp index 3d8124b81b..4ac16188fa 100644 --- a/services/vibratorservice/VibratorHalWrapper.cpp +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -96,6 +96,17 @@ Info HalWrapper::getInfo() { if (mInfoCache.mMaxAmplitudes.isFailed()) { mInfoCache.mMaxAmplitudes = getMaxAmplitudesInternal(); } + if (mInfoCache.mMaxEnvelopeEffectSize.isFailed()) { + mInfoCache.mMaxEnvelopeEffectSize = getMaxEnvelopeEffectSizeInternal(); + } + if (mInfoCache.mMinEnvelopeEffectControlPointDuration.isFailed()) { + mInfoCache.mMinEnvelopeEffectControlPointDuration = + getMinEnvelopeEffectControlPointDurationInternal(); + } + if (mInfoCache.mMaxEnvelopeEffectControlPointDuration.isFailed()) { + mInfoCache.mMaxEnvelopeEffectControlPointDuration = + getMaxEnvelopeEffectControlPointDurationInternal(); + } return mInfoCache.get(); } @@ -210,6 +221,23 @@ HalResult<std::vector<float>> HalWrapper::getMaxAmplitudesInternal() { ALOGV("Skipped getMaxAmplitudes because it's not available in Vibrator HAL"); return HalResult<std::vector<float>>::unsupported(); } +HalResult<int32_t> HalWrapper::getMaxEnvelopeEffectSizeInternal() { + ALOGV("Skipped getMaxEnvelopeEffectSizeInternal because it's not available " + "in Vibrator HAL"); + return HalResult<int32_t>::unsupported(); +} + +HalResult<milliseconds> HalWrapper::getMinEnvelopeEffectControlPointDurationInternal() { + ALOGV("Skipped getMinEnvelopeEffectControlPointDurationInternal because it's not " + "available in Vibrator HAL"); + return HalResult<milliseconds>::unsupported(); +} + +HalResult<milliseconds> HalWrapper::getMaxEnvelopeEffectControlPointDurationInternal() { + ALOGV("Skipped getMaxEnvelopeEffectControlPointDurationInternal because it's not " + "available in Vibrator HAL"); + return HalResult<milliseconds>::unsupported(); +} // ------------------------------------------------------------------------------------------------- @@ -441,6 +469,24 @@ HalResult<std::vector<float>> AidlHalWrapper::getMaxAmplitudesInternal() { return HalResultFactory::fromStatus<std::vector<float>>(std::move(status), amplitudes); } +HalResult<int32_t> AidlHalWrapper::getMaxEnvelopeEffectSizeInternal() { + int32_t size = 0; + auto status = getHal()->getPwleV2CompositionSizeMax(&size); + return HalResultFactory::fromStatus<int32_t>(std::move(status), size); +} + +HalResult<milliseconds> AidlHalWrapper::getMinEnvelopeEffectControlPointDurationInternal() { + int32_t durationMs = 0; + auto status = getHal()->getPwleV2PrimitiveDurationMinMillis(&durationMs); + return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs)); +} + +HalResult<milliseconds> AidlHalWrapper::getMaxEnvelopeEffectControlPointDurationInternal() { + int32_t durationMs = 0; + auto status = getHal()->getPwleV2PrimitiveDurationMaxMillis(&durationMs); + return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs)); +} + std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h index ae0d9ab411..4938b156e7 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -258,6 +258,9 @@ public: const HalResult<float> frequencyResolution; const HalResult<float> qFactor; const HalResult<std::vector<float>> maxAmplitudes; + const HalResult<int32_t> maxEnvelopeEffectSize; + const HalResult<std::chrono::milliseconds> minEnvelopeEffectControlPointDuration; + const HalResult<std::chrono::milliseconds> maxEnvelopeEffectControlPointDuration; void logFailures() const { logFailure<Capabilities>(capabilities, "getCapabilities"); @@ -276,6 +279,11 @@ public: logFailure<float>(frequencyResolution, "getFrequencyResolution"); logFailure<float>(qFactor, "getQFactor"); logFailure<std::vector<float>>(maxAmplitudes, "getMaxAmplitudes"); + logFailure<int32_t>(maxEnvelopeEffectSize, "getMaxEnvelopeEffectSize"); + logFailure<std::chrono::milliseconds>(minEnvelopeEffectControlPointDuration, + "getMinEnvelopeEffectControlPointDuration"); + logFailure<std::chrono::milliseconds>(maxEnvelopeEffectControlPointDuration, + "getMaxEnvelopeEffectControlPointDuration"); } bool shouldRetry() const { @@ -285,7 +293,10 @@ public: pwlePrimitiveDurationMax.shouldRetry() || compositionSizeMax.shouldRetry() || pwleSizeMax.shouldRetry() || minFrequency.shouldRetry() || resonantFrequency.shouldRetry() || frequencyResolution.shouldRetry() || - qFactor.shouldRetry() || maxAmplitudes.shouldRetry(); + qFactor.shouldRetry() || maxAmplitudes.shouldRetry() || + maxEnvelopeEffectSize.shouldRetry() || + minEnvelopeEffectControlPointDuration.shouldRetry() || + maxEnvelopeEffectControlPointDuration.shouldRetry(); } private: @@ -313,7 +324,10 @@ public: mResonantFrequency, mFrequencyResolution, mQFactor, - mMaxAmplitudes}; + mMaxAmplitudes, + mMaxEnvelopeEffectSize, + mMinEnvelopeEffectControlPointDuration, + mMaxEnvelopeEffectControlPointDuration}; } private: @@ -340,6 +354,11 @@ private: HalResult<float> mQFactor = HalResult<float>::transactionFailed(MSG); HalResult<std::vector<float>> mMaxAmplitudes = HalResult<std::vector<float>>::transactionFailed(MSG); + HalResult<int32_t> mMaxEnvelopeEffectSize = HalResult<int>::transactionFailed(MSG); + HalResult<std::chrono::milliseconds> mMinEnvelopeEffectControlPointDuration = + HalResult<std::chrono::milliseconds>::transactionFailed(MSG); + HalResult<std::chrono::milliseconds> mMaxEnvelopeEffectControlPointDuration = + HalResult<std::chrono::milliseconds>::transactionFailed(MSG); friend class HalWrapper; }; @@ -420,6 +439,9 @@ protected: virtual HalResult<float> getFrequencyResolutionInternal(); virtual HalResult<float> getQFactorInternal(); virtual HalResult<std::vector<float>> getMaxAmplitudesInternal(); + virtual HalResult<int32_t> getMaxEnvelopeEffectSizeInternal(); + virtual HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal(); + virtual HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal(); private: std::mutex mInfoMutex; @@ -495,6 +517,13 @@ protected: HalResult<float> getFrequencyResolutionInternal() override final; HalResult<float> getQFactorInternal() override final; HalResult<std::vector<float>> getMaxAmplitudesInternal() override final; + HalResult<int32_t> getMaxEnvelopeEffectSizeInternal() override final; + + HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal() + override final; + + HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal() + override final; private: const reconnect_fn mReconnectFn; diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp index ba7e1f07bf..17f384d320 100644 --- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp @@ -235,6 +235,9 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { constexpr int32_t PWLE_SIZE_MAX = 20; constexpr int32_t PRIMITIVE_DELAY_MAX = 100; constexpr int32_t PWLE_DURATION_MAX = 200; + constexpr int32_t PWLE_V2_COMPOSITION_SIZE_MAX = 16; + constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20; + constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000; std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK}; std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK}; std::vector<Braking> supportedBraking = {Braking::CLAB}; @@ -305,6 +308,21 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { .Times(Exactly(2)) .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) .WillOnce(DoAll(SetArgPointee<0>(amplitudes), Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2CompositionSizeMax(_)) + .Times(Exactly(2)) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_COMPOSITION_SIZE_MAX), + Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMinMillis(_)) + .Times(Exactly(2)) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS), + Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMaxMillis(_)) + .Times(Exactly(2)) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS), + Return(ndk::ScopedAStatus::ok()))); vibrator::Info failed = mWrapper->getInfo(); ASSERT_TRUE(failed.capabilities.isFailed()); @@ -321,6 +339,9 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { ASSERT_TRUE(failed.frequencyResolution.isFailed()); ASSERT_TRUE(failed.qFactor.isFailed()); ASSERT_TRUE(failed.maxAmplitudes.isFailed()); + ASSERT_TRUE(failed.maxEnvelopeEffectSize.isFailed()); + ASSERT_TRUE(failed.minEnvelopeEffectControlPointDuration.isFailed()); + ASSERT_TRUE(failed.maxEnvelopeEffectControlPointDuration.isFailed()); vibrator::Info successful = mWrapper->getInfo(); ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value()); @@ -338,6 +359,11 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoDoesNotCacheFailedResult) { ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value()); ASSERT_EQ(Q_FACTOR, successful.qFactor.value()); ASSERT_EQ(amplitudes, successful.maxAmplitudes.value()); + ASSERT_EQ(PWLE_V2_COMPOSITION_SIZE_MAX, successful.maxEnvelopeEffectSize.value()); + ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS), + successful.minEnvelopeEffectControlPointDuration.value()); + ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS), + successful.maxEnvelopeEffectControlPointDuration.value()); } TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { @@ -347,6 +373,9 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { constexpr int32_t PWLE_SIZE_MAX = 20; constexpr int32_t PRIMITIVE_DELAY_MAX = 100; constexpr int32_t PWLE_DURATION_MAX = 200; + constexpr int32_t PWLE_V2_COMPOSITION_SIZE_MAX = 16; + constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20; + constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000; std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK}; EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) @@ -391,6 +420,18 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_)) .Times(Exactly(1)) .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), getPwleV2CompositionSizeMax(_)) + .Times(Exactly(1)) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_COMPOSITION_SIZE_MAX), + Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMinMillis(_)) + .Times(Exactly(1)) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS), + Return(ndk::ScopedAStatus::ok()))); + EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMaxMillis(_)) + .Times(Exactly(1)) + .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS), + Return(ndk::ScopedAStatus::ok()))); std::vector<std::thread> threads; for (int i = 0; i < 10; i++) { @@ -414,6 +455,11 @@ TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) { ASSERT_TRUE(info.frequencyResolution.isUnsupported()); ASSERT_TRUE(info.qFactor.isUnsupported()); ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); + ASSERT_EQ(PWLE_V2_COMPOSITION_SIZE_MAX, info.maxEnvelopeEffectSize.value()); + ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS), + info.minEnvelopeEffectControlPointDuration.value()); + ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS), + info.maxEnvelopeEffectControlPointDuration.value()); } TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) { diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp index 83430d79a5..a09ddecf91 100644 --- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp @@ -220,6 +220,9 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoDoesNotCacheFailedResult) { ASSERT_TRUE(info.frequencyResolution.isUnsupported()); ASSERT_TRUE(info.qFactor.isUnsupported()); ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); + ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported()); + ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported()); + ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported()); } TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) { @@ -253,6 +256,9 @@ TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoCachesResult) { ASSERT_TRUE(info.frequencyResolution.isUnsupported()); ASSERT_TRUE(info.qFactor.isUnsupported()); ASSERT_TRUE(info.maxAmplitudes.isUnsupported()); + ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported()); + ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported()); + ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported()); } TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) { diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 436e6c6b7c..879d2d0fa7 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -27,9 +27,18 @@ ndk_library { symbol_file: "libvulkan.map.txt", first_version: "24", unversioned_until: "current", - export_header_libs: [ - "ndk_vulkan_headers", - ], +} + +aconfig_declarations { + name: "libvulkan_flags", + package: "com.android.graphics.libvulkan.flags", + container: "system", + srcs: ["libvulkan_flags.aconfig"], +} + +cc_aconfig_library { + name: "libvulkanflags", + aconfig_declarations: "libvulkan_flags", } cc_library_shared { @@ -113,5 +122,8 @@ cc_library_shared { "android.hardware.graphics.common@1.0", "libSurfaceFlingerProp", ], - static_libs: ["libgrallocusage"], + static_libs: [ + "libgrallocusage", + "libvulkanflags", + ], } diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp index a3fe33e67e..9ff0b46c0f 100644 --- a/vulkan/libvulkan/api_gen.cpp +++ b/vulkan/libvulkan/api_gen.cpp @@ -25,6 +25,9 @@ #undef VK_NO_PROTOTYPES #include "api.h" +/* + * This file is autogenerated by api_generator.py. Do not edit directly. + */ namespace vulkan { namespace api { diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h index 4998018882..b468a8911b 100644 --- a/vulkan/libvulkan/api_gen.h +++ b/vulkan/libvulkan/api_gen.h @@ -25,6 +25,9 @@ #include "driver_gen.h" +/* + * This file is autogenerated by api_generator.py. Do not edit directly. + */ namespace vulkan { namespace api { diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index ef213f0c7a..01436db4ae 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -41,10 +41,12 @@ #include <new> #include <vector> +#include <com_android_graphics_libvulkan_flags.h> #include "stubhal.h" using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; +using namespace com::android::graphics::libvulkan; extern "C" android_namespace_t* android_get_exported_namespace(const char*); @@ -688,6 +690,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::KHR_incremental_present: case ProcHook::KHR_shared_presentable_image: case ProcHook::KHR_swapchain: + case ProcHook::KHR_swapchain_mutable_format: case ProcHook::EXT_hdr_metadata: case ProcHook::EXT_swapchain_maintenance1: case ProcHook::ANDROID_external_memory_android_hardware_buffer: @@ -740,6 +743,7 @@ void CreateInfoWrapper::FilterExtension(const char* name) { break; case ProcHook::ANDROID_external_memory_android_hardware_buffer: case ProcHook::KHR_external_fence_fd: + case ProcHook::KHR_swapchain_mutable_format: case ProcHook::EXTENSION_UNKNOWN: // Extensions we don't need to do anything about at this level break; @@ -1251,6 +1255,15 @@ VkResult EnumerateDeviceExtensionProperties( VK_EXT_SWAPCHAIN_MAINTENANCE_1_SPEC_VERSION}); } + VkPhysicalDeviceProperties pDeviceProperties; + data.driver.GetPhysicalDeviceProperties(physicalDevice, &pDeviceProperties); + if (flags::swapchain_mutable_format_ext() && + pDeviceProperties.apiVersion >= VK_API_VERSION_1_2) { + loader_extensions.push_back( + {VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME, + VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION}); + } + // enumerate our extensions first if (!pLayerName && pProperties) { uint32_t count = std::min( diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index 8f090083f8..f741977d50 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -26,6 +26,9 @@ namespace vulkan { namespace driver { +/* + * This file is autogenerated by driver_generator.py. Do not edit directly. + */ namespace { // clang-format off @@ -613,6 +616,7 @@ ProcHook::Extension GetProcHookExtension(const char* name) { if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities; if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities; if (strcmp(name, "VK_KHR_external_fence_fd") == 0) return ProcHook::KHR_external_fence_fd; + if (strcmp(name, "VK_KHR_swapchain_mutable_format") == 0) return ProcHook::KHR_swapchain_mutable_format; // clang-format on return ProcHook::EXTENSION_UNKNOWN; } diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 4527214c3f..649c0f1a17 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -26,6 +26,9 @@ #include <optional> #include <vector> +/* + * This file is autogenerated by driver_generator.py. Do not edit directly. + */ namespace vulkan { namespace driver { @@ -59,6 +62,7 @@ struct ProcHook { KHR_external_semaphore_capabilities, KHR_external_fence_capabilities, KHR_external_fence_fd, + KHR_swapchain_mutable_format, EXTENSION_CORE_1_0, EXTENSION_CORE_1_1, diff --git a/vulkan/libvulkan/libvulkan_flags.aconfig b/vulkan/libvulkan/libvulkan_flags.aconfig new file mode 100644 index 0000000000..891bc0261b --- /dev/null +++ b/vulkan/libvulkan/libvulkan_flags.aconfig @@ -0,0 +1,10 @@ +package: "com.android.graphics.libvulkan.flags" +container: "system" + +flag { + name: "swapchain_mutable_format_ext" + namespace: "core_graphics" + description: "Enable the VK_KHR_swapchain_mutable_format vulkan extension" + bug: "341978292" + is_fixed_read_only: true +} diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index ba2b8887bc..09b0a145af 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -1472,6 +1472,12 @@ static VkResult getProducerUsage(const VkDevice& device, .flags = create_protected_swapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u, }; + // If supporting mutable format swapchain add the mutable format flag + if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { + image_format_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + image_format_info.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR; + } + VkAndroidHardwareBufferUsageANDROID ahb_usage; ahb_usage.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID; ahb_usage.pNext = nullptr; @@ -1890,6 +1896,11 @@ VkResult CreateSwapchainKHR(VkDevice device, num_images = 1; } + VkImageFormatListCreateInfo extra_mutable_formats = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, + }; + VkImageFormatListCreateInfo* extra_mutable_formats_ptr; + // Look through the create_info pNext chain passed to createSwapchainKHR // for an image compression control struct. // if one is found AND the appropriate extensions are enabled, create a @@ -1908,7 +1919,29 @@ VkResult CreateSwapchainKHR(VkDevice device, image_compression.pNext = nullptr; usage_info_pNext = &image_compression; } break; - + case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO: { + const VkImageFormatListCreateInfo* format_list = + reinterpret_cast<const VkImageFormatListCreateInfo*>( + create_infos); + if (create_info->flags & + VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { + if (format_list && format_list->viewFormatCount > 0 && + format_list->pViewFormats) { + extra_mutable_formats.viewFormatCount = + format_list->viewFormatCount; + extra_mutable_formats.pViewFormats = + format_list->pViewFormats; + extra_mutable_formats_ptr = &extra_mutable_formats; + } else { + ALOGE( + "vk_swapchain_create_mutable_format_bit_khr was " + "set during swapchain creation but no valid " + "vkimageformatlistcreateinfo was found in the " + "pnext chain"); + return VK_ERROR_INITIALIZATION_FAILED; + } + } + } break; default: // Ignore all other info structs break; @@ -2004,6 +2037,11 @@ VkResult CreateSwapchainKHR(VkDevice device, .pQueueFamilyIndices = create_info->pQueueFamilyIndices, }; + if (create_info->flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) { + image_create.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + image_create.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR; + } + // Note: don't do deferred allocation for shared present modes. There's only one buffer // involved so very little benefit. if ((create_info->flags & VK_SWAPCHAIN_CREATE_DEFERRED_MEMORY_ALLOCATION_BIT_EXT) && @@ -2013,7 +2051,7 @@ VkResult CreateSwapchainKHR(VkDevice device, // AcquireNextImage. VkImageSwapchainCreateInfoKHR image_swapchain_create = { .sType = VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR, - .pNext = nullptr, + .pNext = extra_mutable_formats_ptr, .swapchain = HandleFromSwapchain(swapchain), }; image_create.pNext = &image_swapchain_create; @@ -2065,6 +2103,11 @@ VkResult CreateSwapchainKHR(VkDevice device, ANativeWindowBuffer_getHardwareBuffer(img.buffer.get()); image_create.pNext = &image_native_buffer; + if (extra_mutable_formats_ptr) { + extra_mutable_formats_ptr->pNext = image_create.pNext; + image_create.pNext = extra_mutable_formats_ptr; + } + ATRACE_BEGIN("CreateImage"); result = dispatch.CreateImage(device, &image_create, nullptr, &img.image); diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index d34851e536..40a45af94e 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -24,6 +24,9 @@ using namespace null_driver; +/* + * This file is autogenerated by null_generator.py. Do not edit directly. + */ namespace { struct NameProc { diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h index fb3bd05e07..0d1e223226 100644 --- a/vulkan/nulldrv/null_driver_gen.h +++ b/vulkan/nulldrv/null_driver_gen.h @@ -22,6 +22,9 @@ #include <vulkan/vk_android_native_buffer.h> #include <vulkan/vulkan.h> +/* + * This file is autogenerated by null_generator.py. Do not edit directly. + */ namespace null_driver { PFN_vkVoidFunction GetGlobalProcAddr(const char* name); diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py index be24172eed..001af208a8 100644 --- a/vulkan/scripts/api_generator.py +++ b/vulkan/scripts/api_generator.py @@ -61,6 +61,9 @@ def gen_h(): #include "driver_gen.h" +/* + * This file is autogenerated by api_generator.py. Do not edit directly. + */ namespace vulkan { namespace api { @@ -283,6 +286,9 @@ def gen_cpp(): #undef VK_NO_PROTOTYPES #include "api.h" +/* + * This file is autogenerated by api_generator.py. Do not edit directly. + */ namespace vulkan { namespace api { diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py index 48c0ae9304..61595993f1 100644 --- a/vulkan/scripts/driver_generator.py +++ b/vulkan/scripts/driver_generator.py @@ -49,6 +49,7 @@ _KNOWN_EXTENSIONS = _INTERCEPTED_EXTENSIONS + [ 'VK_KHR_external_semaphore_capabilities', 'VK_KHR_external_fence_capabilities', 'VK_KHR_external_fence_fd', + 'VK_KHR_swapchain_mutable_format', ] # Functions needed at vulkan::driver level. @@ -224,6 +225,9 @@ def gen_h(): #include <optional> #include <vector> +/* + * This file is autogenerated by driver_generator.py. Do not edit directly. + */ namespace vulkan { namespace driver { @@ -503,6 +507,9 @@ def gen_cpp(): namespace vulkan { namespace driver { +/* + * This file is autogenerated by driver_generator.py. Do not edit directly. + */ namespace { // clang-format off\n\n""") diff --git a/vulkan/scripts/null_generator.py b/vulkan/scripts/null_generator.py index 3624c1d032..5c5bea316b 100644 --- a/vulkan/scripts/null_generator.py +++ b/vulkan/scripts/null_generator.py @@ -55,6 +55,9 @@ def gen_h(): #include <vulkan/vk_android_native_buffer.h> #include <vulkan/vulkan.h> +/* + * This file is autogenerated by null_generator.py. Do not edit directly. + */ namespace null_driver { PFN_vkVoidFunction GetGlobalProcAddr(const char* name); @@ -97,6 +100,9 @@ def gen_cpp(): using namespace null_driver; +/* + * This file is autogenerated by null_generator.py. Do not edit directly. + */ namespace { struct NameProc { |