diff options
357 files changed, 9174 insertions, 3340 deletions
diff --git a/Android.bp b/Android.bp index 9829c7fbad..298c01abba 100644 --- a/Android.bp +++ b/Android.bp @@ -1,3 +1,41 @@ +package { + default_applicable_licenses: ["frameworks_native_license"], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// +// large-scale-change filtered out the below license kinds as false-positives: +// SPDX-license-identifier-LGPL +// SPDX-license-identifier-LGPL-2.1 +// SPDX-license-identifier-LGPL-3.0 +// See: http://go/android-license-faq +license { + name: "frameworks_native_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + "SPDX-license-identifier-BSD", + "SPDX-license-identifier-MIT", + "SPDX-license-identifier-Unicode-DFS", + "legacy_notice", + ], + license_text: [ + "NOTICE", + ], +} + ndk_headers { name: "libandroid_headers", from: "include/android", @@ -43,3 +81,8 @@ filegroup { ":framework_native_aidl_gui", ], } + +cc_library_headers{ + name: "libandroid_headers_private", + export_include_dirs: ["include/private"], +}
\ No newline at end of file diff --git a/aidl/gui/android/hardware/display/IDeviceProductInfoConstants.aidl b/aidl/gui/android/hardware/display/IDeviceProductInfoConstants.aidl new file mode 100644 index 0000000000..7cc272adff --- /dev/null +++ b/aidl/gui/android/hardware/display/IDeviceProductInfoConstants.aidl @@ -0,0 +1,32 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +/** @hide */ +interface IDeviceProductInfoConstants { + /** The device connection to the display sink is unknown. */ + const int CONNECTION_TO_SINK_UNKNOWN = 0; + + /** The device is built-in in the display sink. */ + const int CONNECTION_TO_SINK_BUILT_IN = 1; + + /** The device is directly connected to the display sink. */ + const int CONNECTION_TO_SINK_DIRECT = 2; + + /** The device is transitively connected to the display sink. */ + const int CONNECTION_TO_SINK_TRANSITIVE = 3; +}
\ No newline at end of file diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp index e7d0ad0549..aa0ef253bf 100644 --- a/cmds/atrace/Android.bp +++ b/cmds/atrace/Android.bp @@ -1,5 +1,22 @@ // Copyright 2012 The Android Open Source Project +package { + default_applicable_licenses: ["frameworks_native_cmds_atrace_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_cmds_atrace_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_binary { name: "atrace", srcs: ["atrace.cpp"], diff --git a/cmds/bugreport/Android.bp b/cmds/bugreport/Android.bp index 24044a64b0..8262aed97b 100644 --- a/cmds/bugreport/Android.bp +++ b/cmds/bugreport/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "bugreport", srcs: ["bugreport.cpp"], diff --git a/cmds/bugreport/OWNERS b/cmds/bugreport/OWNERS index 1ba7cffa0e..2a9b681318 100644 --- a/cmds/bugreport/OWNERS +++ b/cmds/bugreport/OWNERS @@ -1,6 +1,4 @@ set noparent -felipeal@google.com nandana@google.com jsharkey@android.com -enh@google.com diff --git a/cmds/bugreportz/Android.bp b/cmds/bugreportz/Android.bp index 924a3a351b..332f858e92 100644 --- a/cmds/bugreportz/Android.bp +++ b/cmds/bugreportz/Android.bp @@ -1,5 +1,14 @@ // bugreportz // ========== +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "bugreportz", diff --git a/cmds/bugreportz/OWNERS b/cmds/bugreportz/OWNERS index 1ba7cffa0e..2a9b681318 100644 --- a/cmds/bugreportz/OWNERS +++ b/cmds/bugreportz/OWNERS @@ -1,6 +1,4 @@ set noparent -felipeal@google.com nandana@google.com jsharkey@android.com -enh@google.com diff --git a/cmds/cmd/Android.bp b/cmds/cmd/Android.bp index 8ea71cd048..c900a24e15 100644 --- a/cmds/cmd/Android.bp +++ b/cmds/cmd/Android.bp @@ -1,3 +1,20 @@ +package { + default_applicable_licenses: ["frameworks_native_cmds_cmd_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_cmds_cmd_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_static { name: "libcmd", diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 34e9a85af3..f48f1fb6f8 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -13,6 +13,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "dumpstate_cflag_defaults", cflags: [ diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS index 1ba7cffa0e..2a9b681318 100644 --- a/cmds/dumpstate/OWNERS +++ b/cmds/dumpstate/OWNERS @@ -1,6 +1,4 @@ set noparent -felipeal@google.com nandana@google.com jsharkey@android.com -enh@google.com diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp index f99588ffc8..91aa018451 100644 --- a/cmds/dumpsys/Android.bp +++ b/cmds/dumpsys/Android.bp @@ -1,3 +1,20 @@ +package { + default_applicable_licenses: ["frameworks_native_cmds_dumpsys_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_cmds_dumpsys_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "dumpsys_defaults", diff --git a/cmds/dumpsys/OWNERS b/cmds/dumpsys/OWNERS index 1ba7cffa0e..2a9b681318 100644 --- a/cmds/dumpsys/OWNERS +++ b/cmds/dumpsys/OWNERS @@ -1,6 +1,4 @@ set noparent -felipeal@google.com nandana@google.com jsharkey@android.com -enh@google.com diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp index e182b9d287..6854c7550e 100644 --- a/cmds/dumpsys/tests/Android.bp +++ b/cmds/dumpsys/tests/Android.bp @@ -1,4 +1,13 @@ // Build the unit tests for dumpsys +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_cmds_dumpsys_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_cmds_dumpsys_license"], +} + cc_test { name: "dumpsys_test", test_suites: ["device-tests"], diff --git a/cmds/flatland/Android.mk b/cmds/flatland/Android.mk index 7aa111c7ae..754a99caf6 100644 --- a/cmds/flatland/Android.mk +++ b/cmds/flatland/Android.mk @@ -11,6 +11,9 @@ LOCAL_SRC_FILES:= \ LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE:= flatland +LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS:= notice +LOCAL_NOTICE_FILE:= $(LOCAL_PATH)/../../NOTICE LOCAL_MODULE_TAGS := tests diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp index 72ffc94d22..1ebdc47ebc 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "idlcli-defaults", shared_libs: [ @@ -44,6 +53,8 @@ cc_library { "vibrator/CommandGetCompositionDelayMax.cpp", "vibrator/CommandGetCompositionSizeMax.cpp", "vibrator/CommandGetPrimitiveDuration.cpp", + "vibrator/CommandGetQFactor.cpp", + "vibrator/CommandGetResonantFrequency.cpp", "vibrator/CommandGetSupportedAlwaysOnEffects.cpp", "vibrator/CommandGetSupportedEffects.cpp", "vibrator/CommandGetSupportedPrimitives.cpp", diff --git a/cmds/idlcli/vibrator/CommandGetQFactor.cpp b/cmds/idlcli/vibrator/CommandGetQFactor.cpp new file mode 100644 index 0000000000..a2681e905b --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetQFactor.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandGetQFactor : public Command { + std::string getDescription() const override { return "Retrieves vibrator Q factor."; } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + float qFactor; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getQFactor, &qFactor); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Q Factor: " << qFactor << std::endl; + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetQFactor>("getQFactor"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp new file mode 100644 index 0000000000..81a6391de5 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetResonantFrequency.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandGetResonantFrequency : public Command { + std::string getDescription() const override { + return "Retrieves vibrator resonant frequency in Hz."; + } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + float resonantFrequencyHz; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getResonantFrequency, &resonantFrequencyHz); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Resonant Frequency: " << resonantFrequencyHz << " Hz" << std::endl; + + return ret; + } +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetResonantFrequency>( + "getResonantFrequency"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index cbe857aacb..5c2211ff9a 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "installd_defaults", diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 7c9e3b2e38..f67ab812fa 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -1,4 +1,13 @@ // Build the unit tests for installd +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "installd_utils_test", test_suites: ["device-tests"], @@ -157,4 +166,3 @@ cc_test { "libotapreoptparameters" ], } - diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk index e1e2204233..396ae9db04 100644 --- a/cmds/ip-up-vpn/Android.mk +++ b/cmds/ip-up-vpn/Android.mk @@ -21,6 +21,9 @@ LOCAL_SRC_FILES := ip-up-vpn.c LOCAL_CFLAGS := -Wall -Werror LOCAL_SHARED_LIBRARIES := libcutils liblog LOCAL_MODULE := ip-up-vpn +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp LOCAL_MODULE_TAGS := optional diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index 0cbb80feb2..649e53a8aa 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "liblshal", shared_libs: [ diff --git a/cmds/lshal/libprocpartition/Android.bp b/cmds/lshal/libprocpartition/Android.bp index 9592111796..cbfbdc9223 100644 --- a/cmds/lshal/libprocpartition/Android.bp +++ b/cmds/lshal/libprocpartition/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libprocpartition", shared_libs: [ diff --git a/cmds/rawbu/Android.bp b/cmds/rawbu/Android.bp index 363ffc1aaf..e34119d1d1 100644 --- a/cmds/rawbu/Android.bp +++ b/cmds/rawbu/Android.bp @@ -1,5 +1,22 @@ // Copyright 2009 The Android Open Source Project +package { + default_applicable_licenses: ["frameworks_native_cmds_rawbu_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_cmds_rawbu_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_binary { name: "rawbu", diff --git a/cmds/rss_hwm_reset/Android.bp b/cmds/rss_hwm_reset/Android.bp index 15f10efdee..cd335d44cc 100644 --- a/cmds/rss_hwm_reset/Android.bp +++ b/cmds/rss_hwm_reset/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "rss_hwm_reset", diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp index a5b1ac5c5f..3e8e3f67f8 100644 --- a/cmds/service/Android.bp +++ b/cmds/service/Android.bp @@ -1,3 +1,20 @@ +package { + default_applicable_licenses: ["frameworks_native_cmds_service_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_cmds_service_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_binary { name: "service", diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index b1392515a2..9de344a820 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "servicemanager_defaults", diff --git a/cmds/surfacereplayer/Android.bp b/cmds/surfacereplayer/Android.bp index d4c037ab7d..34fc8b10ea 100644 --- a/cmds/surfacereplayer/Android.bp +++ b/cmds/surfacereplayer/Android.bp @@ -1,4 +1,13 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + subdirs = [ "proto", "replayer", -]
\ No newline at end of file +] diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp index 71a5e23a9e..dae976e3df 100644 --- a/cmds/surfacereplayer/proto/Android.bp +++ b/cmds/surfacereplayer/proto/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libtrace_proto", srcs: [ diff --git a/cmds/surfacereplayer/replayer/Android.bp b/cmds/surfacereplayer/replayer/Android.bp index 7632311906..3985230f08 100644 --- a/cmds/surfacereplayer/replayer/Android.bp +++ b/cmds/surfacereplayer/replayer/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libsurfacereplayer", srcs: [ diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 58d6582e0b..3c53c025ff 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -28,7 +28,6 @@ #include <gui/Surface.h> #include <private/gui/ComposerService.h> -#include <ui/DisplayInfo.h> #include <utils/Log.h> #include <utils/String8.h> #include <utils/Trace.h> diff --git a/data/etc/android.hardware.keystore.app_attest_key.xml b/data/etc/android.hardware.keystore.app_attest_key.xml new file mode 100644 index 0000000000..8adc4399d3 --- /dev/null +++ b/data/etc/android.hardware.keystore.app_attest_key.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices that support app attestation keys (i.e. KeyMint 1.0). --> +<permissions> + <feature name="android.hardware.keystore.app_attest_key" /> +</permissions> diff --git a/headers/Android.bp b/headers/Android.bp index 8f41c2b75b..7481a230c3 100644 --- a/headers/Android.bp +++ b/headers/Android.bp @@ -1,3 +1,13 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "media_plugin_headers", vendor_available: true, diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 8039bb0d79..cc5420e239 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -127,6 +127,10 @@ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, * to listen directly to DisplayManager.DisplayListener#onDisplayChanged events * instead. * + * As of API level 31, this api is guaranteed to have a consistent view with DisplayManager; + * Display#getRefreshRate is guaranteed to not return a stale refresh rate when invoked from this + * callback. + * * Available since API level 30. */ void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, diff --git a/include/android/input.h b/include/android/input.h index b70d42427d..797348742d 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -859,7 +859,7 @@ enum { /** HDMI */ AINPUT_SOURCE_HDMI = 0x02000000 | AINPUT_SOURCE_CLASS_BUTTON, /** sensor */ - AINPUT_SOURCE_SENSOR = 0x04000000 | AINPUT_SOURCE_UNKNOWN, + AINPUT_SOURCE_SENSOR = 0x04000000 | AINPUT_SOURCE_CLASS_NONE, /** rotary encoder */ AINPUT_SOURCE_ROTARY_ENCODER = 0x00400000 | AINPUT_SOURCE_CLASS_NONE, diff --git a/include/android/permission_manager.h b/include/android/permission_manager.h index 2d5d060840..753b6d16de 100644 --- a/include/android/permission_manager.h +++ b/include/android/permission_manager.h @@ -14,6 +14,17 @@ * limitations under the License. */ +/** + * Structures and functions related to permission checks in native code. + * + * @addtogroup Permission + * @{ + */ + +/** + * @file permission_manager.h + */ + #ifndef ANDROID_PERMISSION_MANAGER_H #define ANDROID_PERMISSION_MANAGER_H @@ -48,8 +59,8 @@ enum { enum { /** * This is returned if the permission check completed without errors. - * The output result is valid and contains one of {PERMISSION_MANAGER_PERMISSION_GRANTED, - * PERMISSION_MANAGER_PERMISSION_DENIED}. + * The output result is valid and contains one of {::PERMISSION_MANAGER_PERMISSION_GRANTED, + * ::PERMISSION_MANAGER_PERMISSION_DENIED}. */ PERMISSION_MANAGER_STATUS_OK = 0, /** @@ -85,3 +96,5 @@ int32_t APermissionManager_checkPermission(const char* permission, __END_DECLS #endif // ANDROID_PERMISSION_MANAGER_H + +/** @} */ diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 98fd875968..b836581226 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -67,9 +67,21 @@ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* deb __INTRODUCED_IN(29); /** - * Releases the \a surface_control object. After releasing the ASurfaceControl the caller no longer - * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long - * as their parent remains on display. + * Acquires a reference on the given ASurfaceControl object. This prevents the object + * from being deleted until the reference is removed. + * + * To release the reference, use the ASurfaceControl_release function. + * + * Available since API level 31. + */ +void ASurfaceControl_acquire(ASurfaceControl* surface_control) __INTRODUCED_IN(31); + +/** + * Removes a reference that was previously acquired with one of the following functions: + * ASurfaceControl_createFromWindow + * ASurfaceControl_create + * ANativeWindow_acquire + * The surface and its children may remain on display as long as their parent remains on display. * * Available since API level 29. */ diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 2bd7bd2004..2deb99d154 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -100,6 +100,13 @@ enum class InputDeviceSensorReportingMode : int32_t { SPECIAL_TRIGGER = 3, }; +enum class InputDeviceLightType : int32_t { + SINGLE = 0, + PLAYER_ID = 1, + RGB = 2, + MULTI_COLOR = 3, +}; + struct InputDeviceSensorInfo { explicit InputDeviceSensorInfo(std::string name, std::string vendor, int32_t version, InputDeviceSensorType type, InputDeviceSensorAccuracy accuracy, @@ -156,6 +163,20 @@ struct InputDeviceSensorInfo { int32_t id; }; +struct InputDeviceLightInfo { + explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type, + int32_t ordinal) + : name(name), id(id), type(type), ordinal(ordinal) {} + // Name string of the light. + std::string name; + // Light id + int32_t id; + // Type of the light. + InputDeviceLightType type; + // Ordinal of the light + int32_t ordinal; +}; + /* * Describes the characteristics and capabilities of an input device. */ @@ -198,6 +219,7 @@ public: float min, float max, float flat, float fuzz, float resolution); void addMotionRange(const MotionRange& range); void addSensorInfo(const InputDeviceSensorInfo& info); + void addLightInfo(const InputDeviceLightInfo& info); inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } inline int32_t getKeyboardType() const { return mKeyboardType; } @@ -230,6 +252,10 @@ public: const std::vector<InputDeviceSensorType> getSensorTypes(); + const std::vector<int32_t> getLightIds(); + + const InputDeviceLightInfo* getLightInfo(int32_t id); + private: int32_t mId; int32_t mGeneration; @@ -248,6 +274,8 @@ private: std::vector<MotionRange> mMotionRanges; std::unordered_map<InputDeviceSensorType, InputDeviceSensorInfo> mSensors; + /* Map from light ID to light info */ + std::unordered_map<int32_t, InputDeviceLightInfo> mLights; }; /* Types of input device configuration files. */ diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 8744ef76c1..11b714fbaf 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -30,6 +30,7 @@ */ #include <string> +#include <unordered_map> #include <android-base/chrono_utils.h> @@ -155,6 +156,7 @@ struct InputMessage { struct Finished { uint32_t empty1; uint32_t handled; // actually a bool, but we must maintain 8-byte alignment + nsecs_t consumeTime; // The time when the event was consumed by the receiving end inline size_t size() const { return sizeof(Finished); } } finished; @@ -362,7 +364,8 @@ public: /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, - * and whether the consumer handled the message. + * whether the consumer handled the message, and the time the event was first read by the + * consumer. * * The returned sequence number is never 0 unless the operation failed. * @@ -371,7 +374,8 @@ public: * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); + status_t receiveFinishedSignal( + const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback); private: std::shared_ptr<InputChannel> mChannel; @@ -577,6 +581,13 @@ private: }; std::vector<SeqChain> mSeqChains; + // The time at which each event with the sequence number 'seq' was consumed. + // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency + // This collection is populated when the event is received, and the entries are erased when the + // events are finished. It should not grow infinitely because if an event is not ack'd, ANR + // will be raised for that connection, and no further events will be posted to that channel. + std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes; + status_t consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); status_t consumeSamples(InputEventFactoryInterface* factory, @@ -589,6 +600,8 @@ private: ssize_t findBatch(int32_t deviceId, int32_t source) const; ssize_t findTouchState(int32_t deviceId, int32_t source) const; + nsecs_t getConsumeTime(uint32_t seq) const; + void popConsumeTime(uint32_t seq); status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); static void rewriteMessage(TouchState& state, InputMessage& msg); diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h index 4fa2f86dc1..eda628e233 100644 --- a/include/input/TouchVideoFrame.h +++ b/include/input/TouchVideoFrame.h @@ -57,7 +57,7 @@ public: /** * Rotate the video frame. - * The rotation value is an enum from ui/DisplayInfo.h + * The rotation value is an enum from ui/Rotation.h */ void rotate(int32_t orientation); diff --git a/include/private/surface_control_private.h b/include/private/surface_control_private.h new file mode 100644 index 0000000000..37a476e679 --- /dev/null +++ b/include/private/surface_control_private.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_PRIVATE_NATIVE_SURFACE_CONTROL_H +#define ANDROID_PRIVATE_NATIVE_SURFACE_CONTROL_H + +#include <stdint.h> + +__BEGIN_DECLS + +struct ASurfaceControl; +struct ASurfaceControlStats; + +typedef struct ASurfaceControlStats ASurfaceControlStats; + +/** + * Callback to be notified when surface stats for a specific surface control are available. + */ +typedef void (*ASurfaceControl_SurfaceStatsListener)(void* context, + ASurfaceControl* control, ASurfaceControlStats* stats); + +/** + * Registers a callback to be invoked when surface stats from a specific surface are available. + * + * \param context Optional context provided by the client that is passed into + * the callback. + * + * \param control The surface to retrieve callbacks for. + * + * \param func The callback to be invoked when surface stats are available. + */ +void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context, + ASurfaceControl_SurfaceStatsListener func); + +/** + * Unregisters a callback to be invoked when surface stats from a specific surface are available. + * + * \param context The context passed into ASurfaceControl_registerSurfaceStatsListener + * + * \param func The callback passed into ASurfaceControl_registerSurfaceStatsListener + */ +void ASurfaceControl_unregisterSurfaceStatsListener(void* context, + ASurfaceControl_SurfaceStatsListener func); + +/** + * Returns the timestamp of when the buffer was acquired for a specific frame with frame number + * obtained from ASurfaceControlStats_getFrameNumber. + */ +int64_t ASurfaceControlStats_getAcquireTime(ASurfaceControlStats* stats); + +/** + * Returns the frame number of the surface stats object passed into the callback. + */ +uint64_t ASurfaceControlStats_getFrameNumber(ASurfaceControlStats* stats); + +__END_DECLS + +#endif //ANDROID_PRIVATE_NATIVE_SURFACE_CONTROL_H diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h deleted file mode 120000 index 9a195eaed4..0000000000 --- a/include/ui/DisplayInfo.h +++ /dev/null @@ -1 +0,0 @@ -../../libs/ui/include/ui/DisplayInfo.h
\ No newline at end of file diff --git a/include/ui/StaticDisplayInfo.h b/include/ui/StaticDisplayInfo.h new file mode 120000 index 0000000000..c58aae3020 --- /dev/null +++ b/include/ui/StaticDisplayInfo.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/StaticDisplayInfo.h
\ No newline at end of file diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp index 8883c0478a..16cded8141 100644 --- a/libs/adbd_auth/Android.bp +++ b/libs/adbd_auth/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libadbd_auth", cflags: [ diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp index cdd776480c..b74923cbfc 100644 --- a/libs/android_runtime_lazy/Android.bp +++ b/libs/android_runtime_lazy/Android.bp @@ -30,6 +30,15 @@ // instead of libandroid_runtime. When they are used by a vendor process, // depending on libandroid_runtime is meaningless. In this case, // they can depend on libandroid_runtime_lazy. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libandroid_runtime_lazy", vendor_available: true, diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 80aa8916da..bb40f5146e 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -12,6 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_libs_arect_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_libs_arect_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + ndk_headers { name: "libarect_headers_for_ndk", from: "include/android", diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index b585ad2c7b..e9d866b448 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libbinder_headers", export_include_dirs: ["include"], diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index bf4387a0a1..ddda024a40 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -228,8 +228,9 @@ status_t BpBinder::transact( : Stability::getLocalLevel(); if (CC_UNLIKELY(!Stability::check(category, required))) { - ALOGE("Cannot do a user transaction on a %s binder in a %s context.", + ALOGE("Cannot do a user transaction on a %s binder (%s) in a %s context.", category.debugString().c_str(), + String8(getInterfaceDescriptor()).c_str(), Stability::levelString(required).c_str()); return BAD_TYPE; } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index b9d00fe7ea..3773760f38 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -520,7 +520,7 @@ void Parcel::updateWorkSourceRequestHeaderPosition() const { } } -#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) +#if defined(__ANDROID_VNDK__) constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); #else constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); @@ -769,200 +769,116 @@ status_t Parcel::writeUtf8AsUtf16(const std::string& str) { return NO_ERROR; } -status_t Parcel::writeUtf8AsUtf16(const std::optional<std::string>& str) { - if (!str) { - return writeInt32(-1); - } - return writeUtf8AsUtf16(*str); -} - -status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) { - if (!str) { - return writeInt32(-1); - } - return writeUtf8AsUtf16(*str); -} - -status_t Parcel::writeByteVectorInternal(const int8_t* data, size_t size) { - if (size > std::numeric_limits<int32_t>::max()) { - return BAD_VALUE; - } - - status_t status = writeInt32(size); - if (status != OK) { - return status; - } - - return write(data, size); -} - -status_t Parcel::writeByteVector(const std::vector<int8_t>& val) { - return writeByteVectorInternal(val.data(), val.size()); -} - -status_t Parcel::writeByteVector(const std::optional<std::vector<int8_t>>& val) -{ - if (!val) return writeInt32(-1); - return writeByteVectorInternal(val->data(), val->size()); -} - -status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) -{ - if (!val) return writeInt32(-1); - return writeByteVectorInternal(val->data(), val->size()); -} - -status_t Parcel::writeByteVector(const std::vector<uint8_t>& val) { - return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size()); -} - -status_t Parcel::writeByteVector(const std::optional<std::vector<uint8_t>>& val) -{ - if (!val) return writeInt32(-1); - return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); -} - -status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val) -{ - if (!val) return writeInt32(-1); - return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); -} - -status_t Parcel::writeInt32Vector(const std::vector<int32_t>& val) -{ - return writeTypedVector(val, &Parcel::writeInt32); -} - -status_t Parcel::writeInt32Vector(const std::optional<std::vector<int32_t>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeInt32); -} - -status_t Parcel::writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeInt32); -} - -status_t Parcel::writeInt64Vector(const std::vector<int64_t>& val) -{ - return writeTypedVector(val, &Parcel::writeInt64); -} - -status_t Parcel::writeInt64Vector(const std::optional<std::vector<int64_t>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeInt64); -} - -status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeInt64); -} - -status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val) -{ - return writeTypedVector(val, &Parcel::writeUint64); -} - -status_t Parcel::writeUint64Vector(const std::optional<std::vector<uint64_t>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeUint64); -} - -status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeUint64); -} - -status_t Parcel::writeFloatVector(const std::vector<float>& val) -{ - return writeTypedVector(val, &Parcel::writeFloat); -} - -status_t Parcel::writeFloatVector(const std::optional<std::vector<float>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeFloat); -} - -status_t Parcel::writeFloatVector(const std::unique_ptr<std::vector<float>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeFloat); -} - -status_t Parcel::writeDoubleVector(const std::vector<double>& val) -{ - return writeTypedVector(val, &Parcel::writeDouble); -} - -status_t Parcel::writeDoubleVector(const std::optional<std::vector<double>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeDouble); -} - -status_t Parcel::writeDoubleVector(const std::unique_ptr<std::vector<double>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeDouble); -} - -status_t Parcel::writeBoolVector(const std::vector<bool>& val) -{ - return writeTypedVector(val, &Parcel::writeBool); -} - -status_t Parcel::writeBoolVector(const std::optional<std::vector<bool>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeBool); -} - -status_t Parcel::writeBoolVector(const std::unique_ptr<std::vector<bool>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeBool); -} - -status_t Parcel::writeCharVector(const std::vector<char16_t>& val) -{ - return writeTypedVector(val, &Parcel::writeChar); -} - -status_t Parcel::writeCharVector(const std::optional<std::vector<char16_t>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeChar); -} - -status_t Parcel::writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeChar); -} - -status_t Parcel::writeString16Vector(const std::vector<String16>& val) -{ - return writeTypedVector(val, &Parcel::writeString16); -} +status_t Parcel::writeUtf8AsUtf16(const std::optional<std::string>& str) { return writeData(str); } +status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) { return writeData(str); } + +status_t Parcel::writeString16(const std::optional<String16>& str) { return writeData(str); } +status_t Parcel::writeString16(const std::unique_ptr<String16>& str) { return writeData(str); } + +status_t Parcel::writeByteVector(const std::vector<int8_t>& val) { return writeData(val); } +status_t Parcel::writeByteVector(const std::optional<std::vector<int8_t>>& val) { return writeData(val); } +status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) { return writeData(val); } +status_t Parcel::writeByteVector(const std::vector<uint8_t>& val) { return writeData(val); } +status_t Parcel::writeByteVector(const std::optional<std::vector<uint8_t>>& val) { return writeData(val); } +status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val){ return writeData(val); } +status_t Parcel::writeInt32Vector(const std::vector<int32_t>& val) { return writeData(val); } +status_t Parcel::writeInt32Vector(const std::optional<std::vector<int32_t>>& val) { return writeData(val); } +status_t Parcel::writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val) { return writeData(val); } +status_t Parcel::writeInt64Vector(const std::vector<int64_t>& val) { return writeData(val); } +status_t Parcel::writeInt64Vector(const std::optional<std::vector<int64_t>>& val) { return writeData(val); } +status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val) { return writeData(val); } +status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val) { return writeData(val); } +status_t Parcel::writeUint64Vector(const std::optional<std::vector<uint64_t>>& val) { return writeData(val); } +status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) { return writeData(val); } +status_t Parcel::writeFloatVector(const std::vector<float>& val) { return writeData(val); } +status_t Parcel::writeFloatVector(const std::optional<std::vector<float>>& val) { return writeData(val); } +status_t Parcel::writeFloatVector(const std::unique_ptr<std::vector<float>>& val) { return writeData(val); } +status_t Parcel::writeDoubleVector(const std::vector<double>& val) { return writeData(val); } +status_t Parcel::writeDoubleVector(const std::optional<std::vector<double>>& val) { return writeData(val); } +status_t Parcel::writeDoubleVector(const std::unique_ptr<std::vector<double>>& val) { return writeData(val); } +status_t Parcel::writeBoolVector(const std::vector<bool>& val) { return writeData(val); } +status_t Parcel::writeBoolVector(const std::optional<std::vector<bool>>& val) { return writeData(val); } +status_t Parcel::writeBoolVector(const std::unique_ptr<std::vector<bool>>& val) { return writeData(val); } +status_t Parcel::writeCharVector(const std::vector<char16_t>& val) { return writeData(val); } +status_t Parcel::writeCharVector(const std::optional<std::vector<char16_t>>& val) { return writeData(val); } +status_t Parcel::writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val) { return writeData(val); } + +status_t Parcel::writeString16Vector(const std::vector<String16>& val) { return writeData(val); } status_t Parcel::writeString16Vector( - const std::optional<std::vector<std::optional<String16>>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeString16); -} - + const std::optional<std::vector<std::optional<String16>>>& val) { return writeData(val); } status_t Parcel::writeString16Vector( - const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeString16); -} - + const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val) { return writeData(val); } status_t Parcel::writeUtf8VectorAsUtf16Vector( - const std::optional<std::vector<std::optional<std::string>>>& val) { - return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16); -} - + const std::optional<std::vector<std::optional<std::string>>>& val) { return writeData(val); } status_t Parcel::writeUtf8VectorAsUtf16Vector( - const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) { - return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16); -} + const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) { return writeData(val); } +status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val) { return writeData(val); } + +status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<base::unique_fd>& val) { return writeData(val); } +status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) { return writeData(val); } +status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) { return writeData(val); } + +status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) { return writeData(val); } +status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) { return writeData(val); } +status_t Parcel::writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) { return writeData(val); } + +status_t Parcel::writeParcelable(const Parcelable& parcelable) { return writeData(parcelable); } + +status_t Parcel::readUtf8FromUtf16(std::optional<std::string>* str) const { return readData(str); } +status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const { return readData(str); } + +status_t Parcel::readString16(std::optional<String16>* pArg) const { return readData(pArg); } +status_t Parcel::readString16(std::unique_ptr<String16>* pArg) const { return readData(pArg); } + +status_t Parcel::readByteVector(std::vector<int8_t>* val) const { return readData(val); } +status_t Parcel::readByteVector(std::vector<uint8_t>* val) const { return readData(val); } +status_t Parcel::readByteVector(std::optional<std::vector<int8_t>>* val) const { return readData(val); } +status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const { return readData(val); } +status_t Parcel::readByteVector(std::optional<std::vector<uint8_t>>* val) const { return readData(val); } +status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const { return readData(val); } +status_t Parcel::readInt32Vector(std::optional<std::vector<int32_t>>* val) const { return readData(val); } +status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const { return readData(val); } +status_t Parcel::readInt32Vector(std::vector<int32_t>* val) const { return readData(val); } +status_t Parcel::readInt64Vector(std::optional<std::vector<int64_t>>* val) const { return readData(val); } +status_t Parcel::readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const { return readData(val); } +status_t Parcel::readInt64Vector(std::vector<int64_t>* val) const { return readData(val); } +status_t Parcel::readUint64Vector(std::optional<std::vector<uint64_t>>* val) const { return readData(val); } +status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const { return readData(val); } +status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const { return readData(val); } +status_t Parcel::readFloatVector(std::optional<std::vector<float>>* val) const { return readData(val); } +status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const { return readData(val); } +status_t Parcel::readFloatVector(std::vector<float>* val) const { return readData(val); } +status_t Parcel::readDoubleVector(std::optional<std::vector<double>>* val) const { return readData(val); } +status_t Parcel::readDoubleVector(std::unique_ptr<std::vector<double>>* val) const { return readData(val); } +status_t Parcel::readDoubleVector(std::vector<double>* val) const { return readData(val); } +status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const { return readData(val); } +status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const { return readData(val); } +status_t Parcel::readBoolVector(std::vector<bool>* val) const { return readData(val); } +status_t Parcel::readCharVector(std::optional<std::vector<char16_t>>* val) const { return readData(val); } +status_t Parcel::readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const { return readData(val); } +status_t Parcel::readCharVector(std::vector<char16_t>* val) const { return readData(val); } -status_t Parcel::writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val) { - return writeTypedVector(val, &Parcel::writeUtf8AsUtf16); -} +status_t Parcel::readString16Vector( + std::optional<std::vector<std::optional<String16>>>* val) const { return readData(val); } +status_t Parcel::readString16Vector( + std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const { return readData(val); } +status_t Parcel::readString16Vector(std::vector<String16>* val) const { return readData(val); } +status_t Parcel::readUtf8VectorFromUtf16Vector( + std::optional<std::vector<std::optional<std::string>>>* val) const { return readData(val); } +status_t Parcel::readUtf8VectorFromUtf16Vector( + std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const { return readData(val); } +status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const { return readData(val); } + +status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const { return readData(val); } +status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const { return readData(val); } +status_t Parcel::readUniqueFileDescriptorVector(std::vector<base::unique_fd>* val) const { return readData(val); } + +status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const { return readData(val); } +status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const { return readData(val); } +status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const { return readData(val); } + +status_t Parcel::readParcelable(Parcelable* parcelable) const { return readData(parcelable); } status_t Parcel::writeInt32(int32_t val) { @@ -1091,24 +1007,6 @@ status_t Parcel::writeString8(const char* str, size_t len) return err; } -status_t Parcel::writeString16(const std::optional<String16>& str) -{ - if (!str) { - return writeInt32(-1); - } - - return writeString16(*str); -} - -status_t Parcel::writeString16(const std::unique_ptr<String16>& str) -{ - if (!str) { - return writeInt32(-1); - } - - return writeString16(*str); -} - status_t Parcel::writeString16(const String16& str) { return writeString16(str.string(), str.size()); @@ -1138,32 +1036,6 @@ status_t Parcel::writeStrongBinder(const sp<IBinder>& val) return flattenBinder(val); } -status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) -{ - return writeTypedVector(val, &Parcel::writeStrongBinder); -} - -status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeStrongBinder); -} - -status_t Parcel::writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) -{ - return writeNullableTypedVector(val, &Parcel::writeStrongBinder); -} - -status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const { - return readNullableTypedVector(val, &Parcel::readNullableStrongBinder); -} - -status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const { - return readNullableTypedVector(val, &Parcel::readNullableStrongBinder); -} - -status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const { - return readTypedVector(val, &Parcel::readStrongBinder); -} status_t Parcel::writeRawNullableParcelable(const Parcelable* parcelable) { if (!parcelable) { @@ -1173,14 +1045,6 @@ status_t Parcel::writeRawNullableParcelable(const Parcelable* parcelable) { return writeParcelable(*parcelable); } -status_t Parcel::writeParcelable(const Parcelable& parcelable) { - status_t status = writeInt32(1); // parcelable is not null. - if (status != OK) { - return status; - } - return parcelable.writeToParcel(this); -} - status_t Parcel::writeNativeHandle(const native_handle* handle) { if (!handle || handle->version != sizeof(native_handle)) @@ -1251,18 +1115,6 @@ status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) { return writeDupFileDescriptor(fd.get()); } -status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<base::unique_fd>& val) { - return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor); -} - -status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) { - return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor); -} - -status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) { - return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor); -} - status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob) { if (len > INT32_MAX) { @@ -1477,31 +1329,6 @@ data_unsorted: goto data_sorted; } -status_t Parcel::readVectorSizeWithCoarseBoundCheck(int32_t *size) const { - int32_t requestedSize; - const status_t status = readInt32(&requestedSize); - if (status != NO_ERROR) return status; - - // We permit negative sizes, which indicate presence of a nullable vector, - // i.e. a vector embedded in std::optional, std::unique_ptr, or std::shared_ptr. - if (requestedSize > 0) { - // Check if there are fewer bytes than vector elements. - // A lower bound is 1 byte per element, satisfied by some enum and int8_t and uint8_t. - const size_t availableBytes = dataAvail(); - if (static_cast<size_t>(requestedSize) > availableBytes) { - // We have a size that is greater than the number of bytes available. - // On bounds failure we do not 'rewind' position by 4 bytes of the size already read. - ALOGW("%s: rejecting out of bounds vector size (requestedSize):%d " - "Parcel{dataAvail:%zu mDataSize:%zu mDataPos:%zu mDataCapacity:%zu}", - __func__, requestedSize, availableBytes, mDataSize, mDataPos, mDataCapacity); - return BAD_VALUE; - } - } - - *size = requestedSize; - return NO_ERROR; -} - status_t Parcel::read(void* outData, size_t len) const { if (len > INT32_MAX) { @@ -1605,236 +1432,6 @@ restart_write: return err; } -status_t Parcel::readByteVector(std::vector<int8_t>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - return readByteVectorInternal(val, size); -} - -status_t Parcel::readByteVector(std::vector<uint8_t>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - return readByteVectorInternal(val, size); -} - -status_t Parcel::readByteVector(std::optional<std::vector<int8_t>>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - if (!*val) { - // reserveOutVector does not create the out vector if size is < 0. - // This occurs when writing a null byte vector. - return OK; - } - return readByteVectorInternal(&**val, size); -} - -status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - if (val->get() == nullptr) { - // reserveOutVector does not create the out vector if size is < 0. - // This occurs when writing a null byte vector. - return OK; - } - return readByteVectorInternal(val->get(), size); -} - -status_t Parcel::readByteVector(std::optional<std::vector<uint8_t>>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - if (!*val) { - // reserveOutVector does not create the out vector if size is < 0. - // This occurs when writing a null byte vector. - return OK; - } - return readByteVectorInternal(&**val, size); -} - -status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - if (val->get() == nullptr) { - // reserveOutVector does not create the out vector if size is < 0. - // This occurs when writing a null byte vector. - return OK; - } - return readByteVectorInternal(val->get(), size); -} - -status_t Parcel::readInt32Vector(std::optional<std::vector<int32_t>>* val) const { - return readNullableTypedVector(val, &Parcel::readInt32); -} - -status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const { - return readNullableTypedVector(val, &Parcel::readInt32); -} - -status_t Parcel::readInt32Vector(std::vector<int32_t>* val) const { - return readTypedVector(val, &Parcel::readInt32); -} - -status_t Parcel::readInt64Vector(std::optional<std::vector<int64_t>>* val) const { - return readNullableTypedVector(val, &Parcel::readInt64); -} - -status_t Parcel::readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const { - return readNullableTypedVector(val, &Parcel::readInt64); -} - -status_t Parcel::readInt64Vector(std::vector<int64_t>* val) const { - return readTypedVector(val, &Parcel::readInt64); -} - -status_t Parcel::readUint64Vector(std::optional<std::vector<uint64_t>>* val) const { - return readNullableTypedVector(val, &Parcel::readUint64); -} - -status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const { - return readNullableTypedVector(val, &Parcel::readUint64); -} - -status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const { - return readTypedVector(val, &Parcel::readUint64); -} - -status_t Parcel::readFloatVector(std::optional<std::vector<float>>* val) const { - return readNullableTypedVector(val, &Parcel::readFloat); -} - -status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const { - return readNullableTypedVector(val, &Parcel::readFloat); -} - -status_t Parcel::readFloatVector(std::vector<float>* val) const { - return readTypedVector(val, &Parcel::readFloat); -} - -status_t Parcel::readDoubleVector(std::optional<std::vector<double>>* val) const { - return readNullableTypedVector(val, &Parcel::readDouble); -} - -status_t Parcel::readDoubleVector(std::unique_ptr<std::vector<double>>* val) const { - return readNullableTypedVector(val, &Parcel::readDouble); -} - -status_t Parcel::readDoubleVector(std::vector<double>* val) const { - return readTypedVector(val, &Parcel::readDouble); -} - -status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const { - const int32_t start = dataPosition(); - int32_t size; - status_t status = readVectorSizeWithCoarseBoundCheck(&size); - val->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - val->emplace(); - - status = readBoolVector(&**val); - - if (status != OK) { - val->reset(); - } - - return status; -} - -status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const { - const int32_t start = dataPosition(); - int32_t size; - status_t status = readVectorSizeWithCoarseBoundCheck(&size); - val->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - val->reset(new (std::nothrow) std::vector<bool>()); - - status = readBoolVector(val->get()); - - if (status != OK) { - val->reset(); - } - - return status; -} - -status_t Parcel::readBoolVector(std::vector<bool>* val) const { - int32_t size; - status_t status = readVectorSizeWithCoarseBoundCheck(&size); - - if (status != OK) { - return status; - } - - if (size < 0) { - return UNEXPECTED_NULL; - } - - val->resize(size); - - /* C++ bool handling means a vector of bools isn't necessarily addressable - * (we might use individual bits) - */ - bool data; - for (int32_t i = 0; i < size; ++i) { - status = readBool(&data); - (*val)[i] = data; - - if (status != OK) { - return status; - } - } - - return OK; -} - -status_t Parcel::readCharVector(std::optional<std::vector<char16_t>>* val) const { - return readNullableTypedVector(val, &Parcel::readChar); -} - -status_t Parcel::readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const { - return readNullableTypedVector(val, &Parcel::readChar); -} - -status_t Parcel::readCharVector(std::vector<char16_t>* val) const { - return readTypedVector(val, &Parcel::readChar); -} - -status_t Parcel::readString16Vector( - std::optional<std::vector<std::optional<String16>>>* val) const { - return readNullableTypedVector(val, &Parcel::readString16); -} - -status_t Parcel::readString16Vector( - std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const { - return readNullableTypedVector(val, &Parcel::readString16); -} - -status_t Parcel::readString16Vector(std::vector<String16>* val) const { - return readTypedVector(val, &Parcel::readString16); -} - -status_t Parcel::readUtf8VectorFromUtf16Vector( - std::optional<std::vector<std::optional<std::string>>>* val) const { - return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16); -} - -status_t Parcel::readUtf8VectorFromUtf16Vector( - std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const { - return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16); -} - -status_t Parcel::readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const { - return readTypedVector(val, &Parcel::readUtf8FromUtf16); -} - status_t Parcel::readInt32(int32_t *pArg) const { return readAligned(pArg); @@ -2007,36 +1604,6 @@ status_t Parcel::readUtf8FromUtf16(std::string* str) const { return NO_ERROR; } -status_t Parcel::readUtf8FromUtf16(std::optional<std::string>* str) const { - const int32_t start = dataPosition(); - int32_t size; - status_t status = readInt32(&size); - str->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - str->emplace(); - return readUtf8FromUtf16(&**str); -} - -status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const { - const int32_t start = dataPosition(); - int32_t size; - status_t status = readInt32(&size); - str->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - str->reset(new (std::nothrow) std::string()); - return readUtf8FromUtf16(str->get()); -} - const char* Parcel::readCString() const { if (mDataPos < mDataSize) { @@ -2103,51 +1670,6 @@ String16 Parcel::readString16() const return String16(); } -status_t Parcel::readString16(std::optional<String16>* pArg) const -{ - const int32_t start = dataPosition(); - int32_t size; - status_t status = readInt32(&size); - pArg->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - pArg->emplace(); - - status = readString16(&**pArg); - - if (status != OK) { - pArg->reset(); - } - - return status; -} - -status_t Parcel::readString16(std::unique_ptr<String16>* pArg) const -{ - const int32_t start = dataPosition(); - int32_t size; - status_t status = readInt32(&size); - pArg->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - pArg->reset(new (std::nothrow) String16()); - - status = readString16(pArg->get()); - - if (status != OK) { - pArg->reset(); - } - - return status; -} status_t Parcel::readString16(String16* pArg) const { @@ -2204,18 +1726,6 @@ sp<IBinder> Parcel::readStrongBinder() const return val; } -status_t Parcel::readParcelable(Parcelable* parcelable) const { - int32_t have_parcelable = 0; - status_t status = readInt32(&have_parcelable); - if (status != OK) { - return status; - } - if (!have_parcelable) { - return UNEXPECTED_NULL; - } - return parcelable->readFromParcel(this); -} - int32_t Parcel::readExceptionCode() const { binder::Status status; @@ -2334,18 +1844,6 @@ status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const return OK; } -status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const { - return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor); -} - -status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const { - return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor); -} - -status_t Parcel::readUniqueFileDescriptorVector(std::vector<base::unique_fd>* val) const { - return readTypedVector(val, &Parcel::readUniqueFileDescriptor); -} - status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const { int32_t blobType; diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index c38249ef7c..edadcd5c58 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -406,12 +406,6 @@ ProcessState::ProcessState(const char *driver) , mThreadPoolSeq(1) , mCallRestriction(CallRestriction::NONE) { - -// TODO(b/166468760): enforce in build system -#if defined(__ANDROID_APEX__) - LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable."); -#endif - if (mDriverFD >= 0) { // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp index 339c53833b..b56e09fa6e 100644 --- a/libs/binder/Stability.cpp +++ b/libs/binder/Stability.cpp @@ -76,18 +76,12 @@ void Stability::tryMarkCompilationUnit(IBinder* binder) { } Stability::Level Stability::getLocalLevel() { +#ifdef __ANDROID_APEX__ +#error APEX can't use libbinder (must use libbinder_ndk) +#endif + #ifdef __ANDROID_VNDK__ - #ifdef __ANDROID_APEX__ - // TODO(b/142684679) avoid use_vendor on system APEXes - #if !defined(__ANDROID_APEX_COM_ANDROID_MEDIA_SWCODEC__) \ - && !defined(__ANDROID_APEX_TEST_COM_ANDROID_MEDIA_SWCODEC__) - #error VNDK + APEX only defined for com.android.media.swcodec - #endif - // TODO(b/142684679) avoid use_vendor on system APEXes - return Level::SYSTEM; - #else - return Level::VENDOR; - #endif + return Level::VENDOR; #else // TODO(b/139325195): split up stability levels for system/APEX. return Level::SYSTEM; diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl index dc8d74c052..a7a6563958 100644 --- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -101,4 +101,17 @@ interface IPackageManagerNative { * This does nothing if this observer was not already registered. */ void unregisterPackageChangeObserver(in IPackageChangeObserver observer); + + /** + * Returns true if the package has the SHA 256 version of the signing certificate. + * @see PackageManager#hasSigningCertificate(String, byte[], int), where type + * has been set to {@link PackageManager#CERT_INPUT_SHA256}. + */ + boolean hasSha256SigningCertificate(in @utf8InCpp String packageName, in byte[] certificate); + + /** + * Returns the debug flag for the given package. + * Unknown packages will cause the call to fail. + */ + boolean isPackageDebuggable(in String packageName); } diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl index ce30050a89..2fabf947cd 100644 --- a/libs/binder/aidl/android/os/IServiceManager.aidl +++ b/libs/binder/aidl/android/os/IServiceManager.aidl @@ -43,9 +43,9 @@ interface IServiceManager { */ const int DUMP_FLAG_PRIORITY_DEFAULT = 1 << 3; - const int DUMP_FLAG_PRIORITY_ALL = 15; - // DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH - // | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT; + const int DUMP_FLAG_PRIORITY_ALL = + DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH + | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT; /* Allows services to dump sections in protobuf format. */ const int DUMP_FLAG_PROTO = 1 << 4; diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 54c49e4c0c..7b298f5b39 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -190,34 +190,47 @@ public: // Write an Enum vector with underlying type int8_t. // Does not use padding; each byte is contiguous. template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t writeEnumVector(const std::vector<T>& val); + status_t writeEnumVector(const std::vector<T>& val) + { return writeData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t writeEnumVector(const std::optional<std::vector<T>>& val); + status_t writeEnumVector(const std::optional<std::vector<T>>& val) + { return writeData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead"))); + status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead"))) + { return writeData(val); } // Write an Enum vector with underlying type != int8_t. template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t writeEnumVector(const std::vector<T>& val); + status_t writeEnumVector(const std::vector<T>& val) + { return writeData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t writeEnumVector(const std::optional<std::vector<T>>& val); + status_t writeEnumVector(const std::optional<std::vector<T>>& val) + { return writeData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead"))); + status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val) __attribute__((deprecated("use std::optional version instead"))) + { return writeData(val); } template<typename T> - status_t writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val); + status_t writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val) + { return writeData(val); } template<typename T> - status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead"))); + status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead"))) + { return writeData(val); } template<typename T> - status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead"))); + status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) __attribute__((deprecated("use std::optional version instead"))) + { return writeData(val); } template<typename T> - status_t writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val); + status_t writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val) + { return writeData(val); } template<typename T> - status_t writeParcelableVector(const std::vector<T>& val); + status_t writeParcelableVector(const std::vector<T>& val) + { return writeData(val); } template<typename T> - status_t writeNullableParcelable(const std::optional<T>& parcelable); + status_t writeNullableParcelable(const std::optional<T>& parcelable) + { return writeData(parcelable); } template<typename T> - status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable) __attribute__((deprecated("use std::optional version instead"))); + status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable) __attribute__((deprecated("use std::optional version instead"))) + { return writeData(parcelable); } status_t writeParcelable(const Parcelable& parcelable); @@ -335,35 +348,48 @@ public: // Read an Enum vector with underlying type int8_t. // Does not use padding; each byte is contiguous. template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t readEnumVector(std::vector<T>* val) const; + status_t readEnumVector(std::vector<T>* val) const + { return readData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead"))); + status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead"))) + { return readData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t readEnumVector(std::optional<std::vector<T>>* val) const; + status_t readEnumVector(std::optional<std::vector<T>>* val) const + { return readData(val); } // Read an Enum vector with underlying type != int8_t. template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t readEnumVector(std::vector<T>* val) const; + status_t readEnumVector(std::vector<T>* val) const + { return readData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead"))); + status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead"))) + { return readData(val); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> - status_t readEnumVector(std::optional<std::vector<T>>* val) const; + status_t readEnumVector(std::optional<std::vector<T>>* val) const + { return readData(val); } template<typename T> status_t readParcelableVector( - std::optional<std::vector<std::optional<T>>>* val) const; + std::optional<std::vector<std::optional<T>>>* val) const + { return readData(val); } template<typename T> status_t readParcelableVector( - std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead"))); + std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const __attribute__((deprecated("use std::optional version instead"))) + { return readData(val); } template<typename T> - status_t readParcelableVector(std::vector<T>* val) const; + status_t readParcelableVector(std::vector<T>* val) const + { return readData(val); } status_t readParcelable(Parcelable* parcelable) const; template<typename T> - status_t readParcelable(std::optional<T>* parcelable) const; + status_t readParcelable(std::optional<T>* parcelable) const + { return readData(parcelable); } template<typename T> - status_t readParcelable(std::unique_ptr<T>* parcelable) const __attribute__((deprecated("use std::optional version instead"))); + status_t readParcelable(std::unique_ptr<T>* parcelable) const __attribute__((deprecated("use std::optional version instead"))) + { return readData(parcelable); } + // If strong binder would be nullptr, readStrongBinder() returns an error. + // TODO: T must be derived from IInterface, fix for clarity. template<typename T> status_t readStrongBinder(sp<T>* val) const; @@ -418,20 +444,13 @@ public: template<typename T> status_t read(LightFlattenable<T>& val) const; + // resizeOutVector is used to resize AIDL out vector parameters. template<typename T> status_t resizeOutVector(std::vector<T>* val) const; template<typename T> status_t resizeOutVector(std::optional<std::vector<T>>* val) const; template<typename T> status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const __attribute__((deprecated("use std::optional version instead"))); - template<typename T> - status_t reserveOutVector(std::vector<T>* val, size_t* size) const; - template<typename T> - status_t reserveOutVector(std::optional<std::vector<T>>* val, - size_t* size) const; - template<typename T> - status_t reserveOutVector(std::unique_ptr<std::vector<T>>* val, - size_t* size) const __attribute__((deprecated("use std::optional version instead"))); // Like Parcel.java's readExceptionCode(). Reads the first int32 // off of a Parcel's header, returning 0 or the negative error @@ -518,10 +537,6 @@ private: void scanForFds() const; status_t validateReadData(size_t len) const; - // Reads an int32 size and does a coarse bounds check against the number - // of available bytes in the Parcel. - status_t readVectorSizeWithCoarseBoundCheck(int32_t *size) const; - void updateWorkSourceRequestHeaderPosition() const; status_t finishFlattenBinder(const sp<IBinder>& binder); @@ -540,53 +555,544 @@ private: status_t writeRawNullableParcelable(const Parcelable* parcelable); - template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool> = 0> - status_t writeEnum(const T& val); - template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool> = 0> - status_t writeEnum(const T& val); + //----------------------------------------------------------------------------- + // Generic type read and write methods for Parcel: + // + // readData(T *value) will read a value from the Parcel. + // writeData(const T& value) will write a value to the Parcel. + // + // Our approach to parceling is based on two overloaded functions + // readData() and writeData() that generate parceling code for an + // object automatically based on its type. The code from templates are generated at + // compile time (if constexpr), and decomposes an object through a call graph matching + // recursive descent of the template typename. + // + // This approach unifies handling of complex objects, + // resulting in fewer lines of code, greater consistency, + // extensibility to nested types, efficiency (decisions made at compile time), + // and better code maintainability and optimization. + // + // Design decision: Incorporate the read and write code into Parcel rather than + // as a non-intrusive serializer that emits a byte stream, as we have + // active objects, alignment, legacy code, and historical idiosyncrasies. + // + // --- Overview + // + // Parceling is a way of serializing objects into a sequence of bytes for communication + // between processes, as part of marshaling data for remote procedure calls. + // + // The Parcel instance contains objects serialized as bytes, such as the following: + // + // 1) Ordinary primitive data such as int, float. + // 2) Established structured data such as String16, std::string. + // 3) Parcelables, which are C++ objects that derive from Parcelable (and thus have a + // readFromParcel and writeToParcel method). (Similar for Java) + // 4) A std::vector<> of such data. + // 5) Nullable objects contained in std::optional, std::unique_ptr, or std::shared_ptr. + // + // And active objects from the Android ecosystem such as: + // 6) File descriptors, base::unique_fd (kernel object handles) + // 7) Binder objects, sp<IBinder> (active Android RPC handles) + // + // Objects from (1) through (5) serialize into the mData buffer. + // Active objects (6) and (7) serialize into both mData and mObjects buffers. + // + // --- Data layout details + // + // Data is read or written to the parcel by recursively decomposing the type of the parameter + // type T through readData() and writeData() methods. + // + // We focus on writeData() here in our explanation of the data layout. + // + // 1) Alignment + // Implementation detail: Regardless of the parameter type, writeData() calls are designed + // to finish at a multiple of 4 bytes, the default alignment of the Parcel. + // + // Writes of single uint8_t, int8_t, enums based on types of size 1, char16_t, etc + // will result in 4 bytes being written. The data is widened to int32 and then written; + // hence the position of the nonzero bytes depend on the native endianness of the CPU. + // + // Writes of primitive values with 8 byte size, double, int64_t, uint64_t, + // are stored with 4 byte alignment. The ARM and x86/x64 permit unaligned reads + // and writes (albeit with potential latency/throughput penalty) which may or may + // not be observable unless the process is IO bound. + // + // 2) Parcelables + // Parcelables are detected by the type's base class, and implemented through calling + // into the Parcelable type's readFromParcel() or writeToParcel() methods. + // Historically, due to null object detection, a (int32_t) 1 is prepended to the data written. + // Parcelables must have a default constructor (i.e. one that takes no arguments). + // + // 3) Arrays + // Arrays of uint8_t and int8_t, and enums based on size 1 are written as + // a contiguous packed byte stream. Hidden zero padding is applied at the end of the byte + // stream to make a multiple of 4 bytes (and prevent info leakage when writing). + // + // All other array writes can be conceptually thought of as recursively calling + // writeData on the individual elements (though may be implemented differently for speed). + // As discussed in (1), alignment rules are therefore applied for each element + // write (not as an aggregate whole), so the wire representation of data can be + // substantially larger. + // + // Historical Note: + // Because of element-wise alignment, CharVector and BoolVector are expanded + // element-wise into integers even though they could have been optimized to be packed + // just like uint8_t, int8_t (size 1 data). + // + // 3.1) Arrays accessed by the std::vector type. This is the default for AIDL. + // + // 4) Nullables + // std::optional, std::unique_ptr, std::shared_ptr are all parceled identically + // (i.e. result in identical byte layout). + // The target of the std::optional, std::unique_ptr, or std::shared_ptr + // can either be a std::vector, String16, std::string, or a Parcelable. + // + // Detection of null relies on peeking the first int32 data and checking if the + // the peeked value is considered invalid for the object: + // (-1 for vectors, String16, std::string) (0 for Parcelables). If the peeked value + // is invalid, then a null is returned. + // + // Application Note: When to use each nullable type: + // + // std::optional: Embeds the object T by value rather than creating a new instance + // by managed pointer as std::unique_ptr or std::shared_ptr. This will save a malloc + // when creating an optional instance. + // + // Use of std::optionals by value can result in copies of the underlying value stored in it, + // so a std::move may be used to move in and move out (for example) a vector value into + // the std::optional or for the std::optional itself. + // + // std::unique_ptr, std::shared_ptr: These are preferred when the lifetime of the object is + // already managed by the application. This reduces unnecessary copying of data + // especially when the calls are local in-proc (rather than via binder rpc). + // + // 5) StrongBinder (sp<IBinder>) + // StrongBinder objects are written regardless of null. When read, null StrongBinder values + // will be interpreted as UNKNOWN_ERROR if the type is a single argument <sp<T>> + // or in a vector argument <std::vector<sp<T>>. However, they will be read without an error + // if present in a std::optional, std::unique_ptr, or std::shared_ptr vector, e.g. + // <std::optional<std::vector<sp<T>>>. + // + // See AIDL annotation @Nullable, readStrongBinder(), and readNullableStrongBinder(). + // + // Historical Note: writing a vector of StrongBinder objects <std::vector<sp<T>> + // containing a null will not cause an error. However reading such a vector will cause + // an error _and_ early termination of the read. - template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool> = 0> - status_t readEnum(T* pArg) const; - template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool> = 0> - status_t readEnum(T* pArg) const; + // --- Examples + // + // Using recursive parceling, we can parcel complex data types so long + // as they obey the rules described above. + // + // Example #1 + // Parceling of a 3D vector + // + // std::vector<std::vector<std::vector<int32_t>>> v1 { + // { {1}, {2, 3}, {4} }, + // {}, + // { {10}, {20}, {30, 40} }, + // }; + // Parcel p1; + // p1.writeData(v1); + // decltype(v1) v2; + // p1.setDataPosition(0); + // p1.readData(&v2); + // ASSERT_EQ(v1, v2); + // + // Example #2 + // Parceling of mixed shared pointers + // + // Parcel p1; + // auto sp1 = std::make_shared<std::vector<std::shared_ptr<std::vector<int>>>>(3); + // (*sp1)[2] = std::make_shared<std::vector<int>>(3); + // (*(*sp1)[2])[2] = 2; + // p1.writeData(sp1); + // decltype(sp1) sp2; + // p1.setDataPosition(0); + // p1.readData(&sp2); + // ASSERT_EQ((*sp1)[0], (*sp2)[0]); // nullptr + // ASSERT_EQ((*sp1)[1], (*sp2)[1]); // nullptr + // ASSERT_EQ(*(*sp1)[2], *(*sp2)[2]); // { 0, 0, 2} + + // --- Helper Methods + // TODO: move this to a utils header. + // + // Determine if a type is a specialization of a templated type + // Example: is_specialization_v<T, std::vector> - status_t writeByteVectorInternal(const int8_t* data, size_t size); - template<typename T> - status_t readByteVectorInternal(std::vector<T>* val, size_t size) const; + template <typename Test, template <typename...> class Ref> + struct is_specialization : std::false_type {}; - template<typename T, typename U> - status_t unsafeReadTypedVector(std::vector<T>* val, - status_t(Parcel::*read_func)(U*) const) const; - template<typename T> - status_t readNullableTypedVector(std::optional<std::vector<T>>* val, - status_t(Parcel::*read_func)(T*) const) const; - template<typename T> - status_t readNullableTypedVector(std::unique_ptr<std::vector<T>>* val, - status_t(Parcel::*read_func)(T*) const) const __attribute__((deprecated("use std::optional version instead"))); - template<typename T> - status_t readTypedVector(std::vector<T>* val, - status_t(Parcel::*read_func)(T*) const) const; - template<typename T, typename U> - status_t unsafeWriteTypedVector(const std::vector<T>& val, - status_t(Parcel::*write_func)(U)); - template<typename T> - status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val, - status_t(Parcel::*write_func)(const T&)); - template<typename T> - status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val, - status_t(Parcel::*write_func)(const T&)) __attribute__((deprecated("use std::optional version instead"))); - template<typename T> - status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val, - status_t(Parcel::*write_func)(T)); - template<typename T> - status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val, - status_t(Parcel::*write_func)(T)) __attribute__((deprecated("use std::optional version instead"))); - template<typename T> - status_t writeTypedVector(const std::vector<T>& val, - status_t(Parcel::*write_func)(const T&)); - template<typename T> - status_t writeTypedVector(const std::vector<T>& val, - status_t(Parcel::*write_func)(T)); + template <template <typename...> class Ref, typename... Args> + struct is_specialization<Ref<Args...>, Ref>: std::true_type {}; + + template <typename Test, template <typename...> class Ref> + static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value; + + // Get the first template type from a container, the T from MyClass<T, ...>. + template<typename T> struct first_template_type; + + template <template <typename ...> class V, typename T, typename... Args> + struct first_template_type<V<T, Args...>> { + using type_t = T; + }; + + template <typename T> + using first_template_type_t = typename first_template_type<T>::type_t; + + // For static assert(false) we need a template version to avoid early failure. + template <typename T> + static inline constexpr bool dependent_false_v = false; + + // primitive types that we consider packed and trivially copyable as an array + template <typename T> + static inline constexpr bool is_pointer_equivalent_array_v = + std::is_same_v<T, int8_t> + || std::is_same_v<T, uint8_t> + // We could support int16_t and uint16_t, but those aren't currently AIDL types. + || std::is_same_v<T, int32_t> + || std::is_same_v<T, uint32_t> + || std::is_same_v<T, float> + // are unaligned reads and write support is assumed. + || std::is_same_v<T, uint64_t> + || std::is_same_v<T, int64_t> + || std::is_same_v<T, double> + || (std::is_enum_v<T> && (sizeof(T) == 1 || sizeof(T) == 4)); // size check not type + + // allowed "nullable" types + // These are nonintrusive containers std::optional, std::unique_ptr, std::shared_ptr. + template <typename T> + static inline constexpr bool is_parcel_nullable_type_v = + is_specialization_v<T, std::optional> + || is_specialization_v<T, std::unique_ptr> + || is_specialization_v<T, std::shared_ptr>; + + // special int32 value to indicate NonNull or Null parcelables + // This is fixed to be only 0 or 1 by contract, do not change. + static constexpr int32_t kNonNullParcelableFlag = 1; + static constexpr int32_t kNullParcelableFlag = 0; + + // special int32 size representing a null vector, when applicable in Nullable data. + // This fixed as -1 by contract, do not change. + static constexpr int32_t kNullVectorSize = -1; + + // --- readData and writeData methods. + // We choose a mixture of function and template overloads to improve code readability. + // TODO: Consider C++20 concepts when they become available. + + // writeData function overloads. + // Implementation detail: Function overloading improves code readability over + // template overloading, but prevents writeData<T> from being used for those types. + + status_t writeData(bool t) { + return writeBool(t); // this writes as int32_t + } + + status_t writeData(int8_t t) { + return writeByte(t); // this writes as int32_t + } + + status_t writeData(uint8_t t) { + return writeByte(static_cast<int8_t>(t)); // this writes as int32_t + } + + status_t writeData(char16_t t) { + return writeChar(t); // this writes as int32_t + } + + status_t writeData(int32_t t) { + return writeInt32(t); + } + + status_t writeData(uint32_t t) { + return writeUint32(t); + } + + status_t writeData(int64_t t) { + return writeInt64(t); + } + + status_t writeData(uint64_t t) { + return writeUint64(t); + } + + status_t writeData(float t) { + return writeFloat(t); + } + + status_t writeData(double t) { + return writeDouble(t); + } + + status_t writeData(const String16& t) { + return writeString16(t); + } + + status_t writeData(const std::string& t) { + return writeUtf8AsUtf16(t); + } + + status_t writeData(const base::unique_fd& t) { + return writeUniqueFileDescriptor(t); + } + + status_t writeData(const Parcelable& t) { // std::is_base_of_v<Parcelable, T> + // implemented here. writeParcelable() calls this. + status_t status = writeData(static_cast<int32_t>(kNonNullParcelableFlag)); + if (status != OK) return status; + return t.writeToParcel(this); + } + + // writeData<T> template overloads. + // Written such that the first template type parameter is the complete type + // of the first function parameter. + template <typename T, + typename std::enable_if_t<std::is_enum_v<T>, bool> = true> + status_t writeData(const T& t) { + // implemented here. writeEnum() calls this. + using UT = std::underlying_type_t<T>; + return writeData(static_cast<UT>(t)); // recurse + } + + template <typename T, + typename std::enable_if_t<is_specialization_v<T, sp>, bool> = true> + status_t writeData(const T& t) { + return writeStrongBinder(t); + } + + // std::optional, std::unique_ptr, std::shared_ptr special case. + template <typename CT, + typename std::enable_if_t<is_parcel_nullable_type_v<CT>, bool> = true> + status_t writeData(const CT& c) { + using T = first_template_type_t<CT>; // The T in CT == C<T, ...> + if constexpr (is_specialization_v<T, std::vector> + || std::is_same_v<T, String16> + || std::is_same_v<T, std::string>) { + if (!c) return writeData(static_cast<int32_t>(kNullVectorSize)); + } else if constexpr (std::is_base_of_v<Parcelable, T>) { + if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag)); + } else /* constexpr */ { // could define this, but raise as error. + static_assert(dependent_false_v<CT>); + } + return writeData(*c); + } + + template <typename CT, + typename std::enable_if_t<is_specialization_v<CT, std::vector>, bool> = true> + status_t writeData(const CT& c) { + using T = first_template_type_t<CT>; // The T in CT == C<T, ...> + if (c.size() > std::numeric_limits<int32_t>::max()) return BAD_VALUE; + const auto size = static_cast<int32_t>(c.size()); + writeData(size); + if constexpr (is_pointer_equivalent_array_v<T>) { + constexpr size_t limit = std::numeric_limits<size_t>::max() / sizeof(T); + if (c.size() > limit) return BAD_VALUE; + // is_pointer_equivalent types do not have gaps which could leak info, + // which is only a concern when writing through binder. + + // TODO: Padding of the write is suboptimal when the length of the + // data is not a multiple of 4. Consider improving the write() method. + return write(c.data(), c.size() * sizeof(T)); + } else if constexpr (std::is_same_v<T, bool> + || std::is_same_v<T, char16_t>) { + // reserve data space to write to + auto data = reinterpret_cast<int32_t*>(writeInplace(c.size() * sizeof(int32_t))); + if (data == nullptr) return BAD_VALUE; + for (const auto t: c) { + *data++ = static_cast<int32_t>(t); + } + } else /* constexpr */ { + for (const auto &t : c) { + const status_t status = writeData(t); + if (status != OK) return status; + } + } + return OK; + } + + // readData function overloads. + // Implementation detail: Function overloading improves code readability over + // template overloading, but prevents readData<T> from being used for those types. + + status_t readData(bool* t) const { + return readBool(t); // this reads as int32_t + } + + status_t readData(int8_t* t) const { + return readByte(t); // this reads as int32_t + } + + status_t readData(uint8_t* t) const { + return readByte(reinterpret_cast<int8_t*>(t)); // NOTE: this reads as int32_t + } + + status_t readData(char16_t* t) const { + return readChar(t); // this reads as int32_t + } + + status_t readData(int32_t* t) const { + return readInt32(t); + } + + status_t readData(uint32_t* t) const { + return readUint32(t); + } + + status_t readData(int64_t* t) const { + return readInt64(t); + } + + status_t readData(uint64_t* t) const { + return readUint64(t); + } + + status_t readData(float* t) const { + return readFloat(t); + } + + status_t readData(double* t) const { + return readDouble(t); + } + + status_t readData(String16* t) const { + return readString16(t); + } + + status_t readData(std::string* t) const { + return readUtf8FromUtf16(t); + } + + status_t readData(base::unique_fd* t) const { + return readUniqueFileDescriptor(t); + } + + status_t readData(Parcelable* t) const { // std::is_base_of_v<Parcelable, T> + // implemented here. readParcelable() calls this. + int32_t present; + status_t status = readData(&present); + if (status != OK) return status; + if (present != kNonNullParcelableFlag) return UNEXPECTED_NULL; + return t->readFromParcel(this); + } + + // readData<T> template overloads. + // Written such that the first template type parameter is the complete type + // of the first function parameter. + + template <typename T, + typename std::enable_if_t<std::is_enum_v<T>, bool> = true> + status_t readData(T* t) const { + // implemented here. readEnum() calls this. + using UT = std::underlying_type_t<T>; + return readData(reinterpret_cast<UT*>(t)); + } + + template <typename T, + typename std::enable_if_t<is_specialization_v<T, sp>, bool> = true> + status_t readData(T* t) const { + return readStrongBinder(t); // Note: on null, returns failure + } + + + template <typename CT, + typename std::enable_if_t<is_parcel_nullable_type_v<CT>, bool> = true> + status_t readData(CT* c) const { + using T = first_template_type_t<CT>; // The T in CT == C<T, ...> + const size_t startPos = dataPosition(); + int32_t peek; + status_t status = readData(&peek); + if (status != OK) return status; + if constexpr (is_specialization_v<T, std::vector> + || std::is_same_v<T, String16> + || std::is_same_v<T, std::string>) { + if (peek == kNullVectorSize) { + c->reset(); + return OK; + } + } else if constexpr (std::is_base_of_v<Parcelable, T>) { + if (peek == kNullParcelableFlag) { + c->reset(); + return OK; + } + } else /* constexpr */ { // could define this, but raise as error. + static_assert(dependent_false_v<CT>); + } + // create a new object. + if constexpr (is_specialization_v<CT, std::optional>) { + c->emplace(); + } else /* constexpr */ { + T* const t = new (std::nothrow) T; // contents read from Parcel below. + if (t == nullptr) return NO_MEMORY; + c->reset(t); + } + // rewind data ptr to reread (this is pretty quick), otherwise we could + // pass an optional argument to readData to indicate a peeked value. + setDataPosition(startPos); + if constexpr (is_specialization_v<T, std::vector>) { + return readData(&**c, READ_FLAG_SP_NULLABLE); // nullable sp<> allowed now + } else { + return readData(&**c); + } + } + + // std::vector special case, incorporating flags whether the vector + // accepts nullable sp<> to be read. + enum ReadFlags { + READ_FLAG_NONE = 0, + READ_FLAG_SP_NULLABLE = 1 << 0, + }; + + template <typename CT, + typename std::enable_if_t<is_specialization_v<CT, std::vector>, bool> = true> + status_t readData(CT* c, ReadFlags readFlags = READ_FLAG_NONE) const { + using T = first_template_type_t<CT>; // The T in CT == C<T, ...> + int32_t size; + status_t status = readInt32(&size); + if (status != OK) return status; + if (size < 0) return UNEXPECTED_NULL; + const size_t availableBytes = dataAvail(); // coarse bound on vector size. + if (static_cast<size_t>(size) > availableBytes) return BAD_VALUE; + c->clear(); // must clear before resizing/reserving otherwise move ctors may be called. + if constexpr (is_pointer_equivalent_array_v<T>) { + // could consider POD without gaps and alignment of 4. + auto data = reinterpret_cast<const T*>( + readInplace(static_cast<size_t>(size) * sizeof(T))); + if (data == nullptr) return BAD_VALUE; + c->insert(c->begin(), data, data + size); // insert should do a reserve(). + } else if constexpr (std::is_same_v<T, bool> + || std::is_same_v<T, char16_t>) { + c->reserve(size); // avoids default initialization + auto data = reinterpret_cast<const int32_t*>( + readInplace(static_cast<size_t>(size) * sizeof(int32_t))); + if (data == nullptr) return BAD_VALUE; + for (int32_t i = 0; i < size; ++i) { + c->emplace_back(static_cast<T>(*data++)); + } + } else if constexpr (is_specialization_v<T, sp>) { + c->resize(size); // calls ctor + if (readFlags & READ_FLAG_SP_NULLABLE) { + for (auto &t : *c) { + status = readNullableStrongBinder(&t); // allow nullable + if (status != OK) return status; + } + } else { + for (auto &t : *c) { + status = readStrongBinder(&t); + if (status != OK) return status; + } + } + } else /* constexpr */ { + c->resize(size); // calls ctor + for (auto &t : *c) { + status = readData(&t); + if (status != OK) return status; + } + } + return OK; + } + + //----------------------------------------------------------------------------- + private: status_t mError; uint8_t* mData; @@ -792,7 +1298,6 @@ status_t Parcel::writeVectorSize(const std::unique_ptr<std::vector<T>>& val) { template<typename T> status_t Parcel::resizeOutVector(std::vector<T>* val) const { int32_t size; - // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here status_t err = readInt32(&size); if (err != NO_ERROR) { return err; @@ -808,7 +1313,6 @@ status_t Parcel::resizeOutVector(std::vector<T>* val) const { template<typename T> status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const { int32_t size; - // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here status_t err = readInt32(&size); if (err != NO_ERROR) { return err; @@ -825,7 +1329,6 @@ status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const { template<typename T> status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const { int32_t size; - // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here status_t err = readInt32(&size); if (err != NO_ERROR) { return err; @@ -840,61 +1343,6 @@ status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const { } template<typename T> -status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const { - int32_t read_size; - status_t err = readVectorSizeWithCoarseBoundCheck(&read_size); - if (err != NO_ERROR) { - return err; - } - - if (read_size < 0) { - return UNEXPECTED_NULL; - } - *size = static_cast<size_t>(read_size); - val->reserve(*size); - return OK; -} - -template<typename T> -status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const { - int32_t read_size; - status_t err = readVectorSizeWithCoarseBoundCheck(&read_size); - if (err != NO_ERROR) { - return err; - } - - if (read_size >= 0) { - *size = static_cast<size_t>(read_size); - val->emplace(); - (*val)->reserve(*size); - } else { - val->reset(); - } - - return OK; -} - -template<typename T> -status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val, - size_t* size) const { - int32_t read_size; - status_t err = readVectorSizeWithCoarseBoundCheck(&read_size); - if (err != NO_ERROR) { - return err; - } - - if (read_size >= 0) { - *size = static_cast<size_t>(read_size); - val->reset(new std::vector<T>()); - (*val)->reserve(*size); - } else { - val->reset(); - } - - return OK; -} - -template<typename T> status_t Parcel::readStrongBinder(sp<T>* val) const { sp<IBinder> tmp; status_t ret = readStrongBinder(&tmp); @@ -926,422 +1374,6 @@ status_t Parcel::readNullableStrongBinder(sp<T>* val) const { return ret; } -template<typename T, typename U> -status_t Parcel::unsafeReadTypedVector( - std::vector<T>* val, - status_t(Parcel::*read_func)(U*) const) const { - int32_t size; - status_t status = this->readVectorSizeWithCoarseBoundCheck(&size); - - if (status != OK) { - return status; - } - - if (size < 0) { - return UNEXPECTED_NULL; - } - - if (val->max_size() < static_cast<size_t>(size)) { - return NO_MEMORY; - } - - val->resize(static_cast<size_t>(size)); - - if (val->size() < static_cast<size_t>(size)) { - return NO_MEMORY; - } - - for (auto& v: *val) { - status = (this->*read_func)(&v); - - if (status != OK) { - return status; - } - } - - return OK; -} - -template<typename T> -status_t Parcel::readTypedVector(std::vector<T>* val, - status_t(Parcel::*read_func)(T*) const) const { - return unsafeReadTypedVector(val, read_func); -} - -template<typename T> -status_t Parcel::readNullableTypedVector(std::optional<std::vector<T>>* val, - status_t(Parcel::*read_func)(T*) const) const { - const size_t start = dataPosition(); - int32_t size; - status_t status = readVectorSizeWithCoarseBoundCheck(&size); - val->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - val->emplace(); - - status = unsafeReadTypedVector(&**val, read_func); - - if (status != OK) { - val->reset(); - } - - return status; -} - -template<typename T> -status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val, - status_t(Parcel::*read_func)(T*) const) const { - const size_t start = dataPosition(); - int32_t size; - status_t status = readVectorSizeWithCoarseBoundCheck(&size); - val->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - val->reset(new std::vector<T>()); - - status = unsafeReadTypedVector(val->get(), read_func); - - if (status != OK) { - val->reset(); - } - - return status; -} - -template<typename T, typename U> -status_t Parcel::unsafeWriteTypedVector(const std::vector<T>& val, - status_t(Parcel::*write_func)(U)) { - if (val.size() > std::numeric_limits<int32_t>::max()) { - return BAD_VALUE; - } - - status_t status = this->writeInt32(static_cast<int32_t>(val.size())); - - if (status != OK) { - return status; - } - - for (const auto& item : val) { - status = (this->*write_func)(item); - - if (status != OK) { - return status; - } - } - - return OK; -} - -template<typename T> -status_t Parcel::writeTypedVector(const std::vector<T>& val, - status_t(Parcel::*write_func)(const T&)) { - return unsafeWriteTypedVector(val, write_func); -} - -template<typename T> -status_t Parcel::writeTypedVector(const std::vector<T>& val, - status_t(Parcel::*write_func)(T)) { - return unsafeWriteTypedVector(val, write_func); -} - -template<typename T> -status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val, - status_t(Parcel::*write_func)(const T&)) { - if (!val) { - return this->writeInt32(-1); - } - - return unsafeWriteTypedVector(*val, write_func); -} - -template<typename T> -status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val, - status_t(Parcel::*write_func)(const T&)) { - if (val.get() == nullptr) { - return this->writeInt32(-1); - } - - return unsafeWriteTypedVector(*val, write_func); -} - -template<typename T> -status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val, - status_t(Parcel::*write_func)(T)) { - if (!val) { - return this->writeInt32(-1); - } - - return unsafeWriteTypedVector(*val, write_func); -} - -template<typename T> -status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val, - status_t(Parcel::*write_func)(T)) { - if (val.get() == nullptr) { - return this->writeInt32(-1); - } - - return unsafeWriteTypedVector(*val, write_func); -} - -template<typename T> -status_t Parcel::readParcelableVector(std::vector<T>* val) const { - return unsafeReadTypedVector<T, Parcelable>(val, &Parcel::readParcelable); -} - -template<typename T> -status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const { - const size_t start = dataPosition(); - int32_t size; - status_t status = readVectorSizeWithCoarseBoundCheck(&size); - val->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - val->emplace(); - - using NullableT = std::optional<T>; - status = unsafeReadTypedVector<NullableT, NullableT>(&**val, &Parcel::readParcelable); - - if (status != OK) { - val->reset(); - } - - return status; -} - -template<typename T> -status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const { - const size_t start = dataPosition(); - int32_t size; - status_t status = readVectorSizeWithCoarseBoundCheck(&size); - val->reset(); - - if (status != OK || size < 0) { - return status; - } - - setDataPosition(start); - val->reset(new std::vector<std::unique_ptr<T>>()); - - using NullableT = std::unique_ptr<T>; - status = unsafeReadTypedVector<NullableT, NullableT>(val->get(), &Parcel::readParcelable); - - if (status != OK) { - val->reset(); - } - - return status; -} - -template<typename T> -status_t Parcel::readParcelable(std::optional<T>* parcelable) const { - const size_t start = dataPosition(); - int32_t present; - status_t status = readInt32(&present); - parcelable->reset(); - - if (status != OK || !present) { - return status; - } - - setDataPosition(start); - parcelable->emplace(); - - status = readParcelable(&**parcelable); - - if (status != OK) { - parcelable->reset(); - } - - return status; -} - -template<typename T> -status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const { - const size_t start = dataPosition(); - int32_t present; - status_t status = readInt32(&present); - parcelable->reset(); - - if (status != OK || !present) { - return status; - } - - setDataPosition(start); - parcelable->reset(new T()); - - status = readParcelable(parcelable->get()); - - if (status != OK) { - parcelable->reset(); - } - - return status; -} - -template<typename T> -status_t Parcel::writeNullableParcelable(const std::optional<T>& parcelable) { - return writeRawNullableParcelable(parcelable ? &*parcelable : nullptr); -} - -template<typename T> -status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) { - return writeRawNullableParcelable(parcelable.get()); -} - -template<typename T> -status_t Parcel::writeParcelableVector(const std::vector<T>& val) { - return unsafeWriteTypedVector<T,const Parcelable&>(val, &Parcel::writeParcelable); -} - -template<typename T> -status_t Parcel::writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val) { - if (!val) { - return this->writeInt32(-1); - } - - using NullableT = std::optional<T>; - return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable); -} - -template<typename T> -status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) { - if (val.get() == nullptr) { - return this->writeInt32(-1); - } - - return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>); -} - -template<typename T> -status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val) { - if (val.get() == nullptr) { - return this->writeInt32(-1); - } - - using NullableT = std::unique_ptr<T>; - return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable); -} - -template<typename T> -status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::optional<T>>>& val) { - if (val.get() == nullptr) { - return this->writeInt32(-1); - } - - using NullableT = std::optional<T>; - return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable); -} - -template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>> -status_t Parcel::writeEnum(const T& val) { - return writeInt32(static_cast<int32_t>(val)); -} -template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool>> -status_t Parcel::writeEnum(const T& val) { - return writeInt64(static_cast<int64_t>(val)); -} - -template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::writeEnumVector(const std::vector<T>& val) { - return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size()); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) { - if (!val) return writeInt32(-1); - return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) { - if (!val) return writeInt32(-1); - return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::writeEnumVector(const std::vector<T>& val) { - return writeTypedVector(val, &Parcel::writeEnum); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) { - return writeNullableTypedVector(val, &Parcel::writeEnum); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) { - return writeNullableTypedVector(val, &Parcel::writeEnum); -} - -template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>> -status_t Parcel::readEnum(T* pArg) const { - return readInt32(reinterpret_cast<int32_t *>(pArg)); -} -template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool>> -status_t Parcel::readEnum(T* pArg) const { - return readInt64(reinterpret_cast<int64_t *>(pArg)); -} - -template<typename T> -inline status_t Parcel::readByteVectorInternal(std::vector<T>* val, size_t size) const { - // readByteVectorInternal expects a vector that has been reserved (but not - // resized) to have the provided size. - const T* data = reinterpret_cast<const T*>(readInplace(size)); - if (!data) return BAD_VALUE; - val->clear(); - val->insert(val->begin(), data, data+size); - return NO_ERROR; -} - -template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::readEnumVector(std::vector<T>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - return readByteVectorInternal(val, size); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - if (!*val) { - // reserveOutVector does not create the out vector if size is < 0. - // This occurs when writing a null Enum vector. - return OK; - } - return readByteVectorInternal(&**val, size); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const { - size_t size; - if (status_t status = reserveOutVector(val, &size); status != OK) return status; - if (val->get() == nullptr) { - // reserveOutVector does not create the out vector if size is < 0. - // This occurs when writing a null Enum vector. - return OK; - } - return readByteVectorInternal(val->get(), size); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::readEnumVector(std::vector<T>* val) const { - return readTypedVector(val, &Parcel::readEnum); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const { - return readNullableTypedVector(val, &Parcel::readEnum); -} -template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> -status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const { - return readNullableTypedVector(val, &Parcel::readEnum); -} - // --------------------------------------------------------------------------- inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h index 5a719b8930..157919903f 100644 --- a/libs/binder/include/private/binder/binder_module.h +++ b/libs/binder/include/private/binder/binder_module.h @@ -88,10 +88,6 @@ struct binder_frozen_status_info { }; #endif //BINDER_GET_FROZEN_INFO -enum transaction_flags_ext { - TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */ -}; - #ifdef __cplusplus } // namespace android #endif diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 897c72a406..eb103d3d77 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -15,6 +15,23 @@ */ // TODO(b/31559095): bionic on host should define this +package { + default_applicable_licenses: ["frameworks_native_libs_binder_ndk_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_libs_binder_ndk_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "libbinder_ndk_host_user", target: { diff --git a/libs/binder/ndk/include_cpp/android/binder_to_string.h b/libs/binder/ndk/include_cpp/android/binder_to_string.h index bd51b1167f..584292551b 100644 --- a/libs/binder/ndk/include_cpp/android/binder_to_string.h +++ b/libs/binder/ndk/include_cpp/android/binder_to_string.h @@ -28,10 +28,22 @@ #include <codecvt> #include <locale> +#include <memory> +#include <optional> #include <sstream> #include <string> #include <type_traits> +#if __has_include(<utils/StrongPointer.h>) +#include <utils/StrongPointer.h> +#define HAS_STRONG_POINTER +#endif + +#if __has_include(<utils/String16.h>) +#include <utils/String16.h> +#define HAS_STRING16 +#endif + #if __has_include(<android/binder_ibinder.h>) #include <android/binder_auto_utils.h> #include <android/binder_interface_utils.h> @@ -42,9 +54,6 @@ #include <binder/IInterface.h> #include <binder/ParcelFileDescriptor.h> #include <binder/ParcelableHolder.h> -#include <utils/String16.h> -#include <utils/StrongPointer.h> -#define HAS_CPP_INTERFACE #endif //_has_include namespace android { @@ -80,11 +89,28 @@ class HasToStringFunction { enum { value = decltype(_test<_T>(0))::value }; }; -// Truthy if _T is like a pointer +template <typename T, template <typename...> typename U> +struct IsInstantiationOf : std::false_type {}; + +template <template <typename...> typename U, typename... Args> +struct IsInstantiationOf<U<Args...>, U> : std::true_type {}; + +// Truthy if _T is like a pointer: one of sp/optional/shared_ptr template <typename _T> class IsPointerLike { template <typename _U> - static auto _test(int) -> decltype(!std::declval<_U>(), *std::declval<_U>(), std::true_type()); + static std::enable_if_t< +#ifdef HAS_STRONG_POINTER + IsInstantiationOf<_U, sp>::value || // for IBinder and interface types in the C++ + // backend +#endif + IsInstantiationOf<_U, std::optional>::value || // for @nullable types in the + // C++/NDK backends + IsInstantiationOf<_U, std::shared_ptr>::value, // for interface types in the + // NDK backends + + std::true_type> + _test(int); template <typename _U> static std::false_type _test(...); @@ -142,12 +168,17 @@ std::string ToString(const _T& t) { return std::to_string(t); } else if constexpr (std::is_same_v<std::string, _T>) { return t; -#ifdef HAS_CPP_INTERFACE +#ifdef HAS_STRING16 } else if constexpr (std::is_same_v<String16, _T>) { std::stringstream out; out << t; return out.str(); #endif + } else if constexpr (details::IsPointerLike<_T>::value) { + if (!t) return "(null)"; + std::stringstream out; + out << ToString(*t); + return out.str(); } else if constexpr (details::HasToStringMethod<_T>::value) { return t.toString(); } else if constexpr (details::HasToStringFunction<_T>::value) { @@ -168,11 +199,6 @@ std::string ToString(const _T& t) { } out << "]"; return out.str(); - } else if constexpr (details::IsPointerLike<_T>::value) { - if (!t) return "(null)"; - std::stringstream out; - out << ToString(*t); - return out.str(); } else { return "{no toString() implemented}"; } diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 2784aa823d..45807515e4 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -31,7 +31,8 @@ __BEGIN_DECLS * * \return EX_NONE on success. */ -binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance); +__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addService( + AIBinder* binder, const char* instance); /** * Gets a binder object with this specific instance name. Will return nullptr immediately if the @@ -94,4 +95,52 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(con */ bool AServiceManager_isDeclared(const char* instance) __INTRODUCED_IN(31); +/** + * Prevent lazy services without client from shutting down their process + * + * \param persist 'true' if the process should not exit. + */ +void AServiceManager_forceLazyServicesPersist(bool persist) __INTRODUCED_IN(31); + +/** + * Set a callback that is invoked when the active service count (i.e. services with clients) + * registered with this process drops to zero (or becomes nonzero). + * The callback takes a boolean argument, which is 'true' if there is + * at least one service with clients. + * + * \param callback function to call when the number of services + * with clients changes. + * \param context opaque pointer passed back as second parameter to the + * callback. + * + * The callback takes two arguments. The first is a boolean that represents if there are + * services with clients (true) or not (false). + * The second is the 'context' pointer passed during the registration. + * + * Callback return value: + * - false: Default behavior for lazy services (shut down the process if there + * are no clients). + * - true: Don't shut down the process even if there are no clients. + * + * This callback gives a chance to: + * 1 - Perform some additional operations before exiting; + * 2 - Prevent the process from exiting by returning "true" from the callback. + */ +void AServiceManager_setActiveServicesCallback(bool (*callback)(bool, void*), void* context) + __INTRODUCED_IN(31); + +/** + * Try to unregister all services previously registered with 'registerService'. + * + * \return true on success. + */ +bool AServiceManager_tryUnregister() __INTRODUCED_IN(31); + +/** + * Re-register services that were unregistered by 'tryUnregister'. + * This method should be called in the case 'tryUnregister' fails + * (and should be called on the same thread). + */ +void AServiceManager_reRegister() __INTRODUCED_IN(31); + __END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h index f5e8bf60ef..44ed48f839 100644 --- a/libs/binder/ndk/include_platform/android/binder_stability.h +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -30,6 +30,8 @@ enum { FLAG_PRIVATE_VENDOR = 0x10000000, }; +// TODO(b/180646847): __ANDROID_APEX__ here is what allows product partition to +// talk to system. #if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) enum { diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 9a93bf3c4b..cef0bf31d2 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -120,6 +120,10 @@ LIBBINDER_NDK31 { # introduced=31 AServiceManager_isDeclared; # apex llndk AServiceManager_registerLazyService; # llndk AServiceManager_waitForService; # apex llndk + AServiceManager_forceLazyServicesPersist; # llndk + AServiceManager_setActiveServicesCallback; # llndk + AServiceManager_tryUnregister; # llndk + AServiceManager_reRegister; # llndk AIBinder_Class_getDescriptor; AIBinder_Weak_clone; diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index c782d47c1c..cb0987e3e1 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -92,3 +92,22 @@ bool AServiceManager_isDeclared(const char* instance) { sp<IServiceManager> sm = defaultServiceManager(); return sm->isDeclared(String16(instance)); } +void AServiceManager_forceLazyServicesPersist(bool persist) { + auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance(); + serviceRegistrar.forcePersist(persist); +} +void AServiceManager_setActiveServicesCallback(bool (*callback)(bool, void*), void* context) { + auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance(); + std::function<bool(bool)> fn = [=](bool hasClients) -> bool { + return callback(hasClients, context); + }; + serviceRegistrar.setActiveServicesCallback(fn); +} +bool AServiceManager_tryUnregister() { + auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance(); + return serviceRegistrar.tryUnregister(); +} +void AServiceManager_reRegister() { + auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance(); + serviceRegistrar.reRegister(); +}
\ No newline at end of file diff --git a/libs/binder/ndk/tests/Android.bp b/libs/binder/ndk/tests/Android.bp index 46e6270eb0..bb51bf0b5d 100644 --- a/libs/binder/ndk/tests/Android.bp +++ b/libs/binder/ndk/tests/Android.bp @@ -14,6 +14,15 @@ * limitations under the License. */ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_libs_binder_ndk_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_libs_binder_ndk_license"], +} + cc_defaults { name: "test_libbinder_ndk_defaults", shared_libs: [ diff --git a/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl index dc77467d8c..ecbd6490e9 100644 --- a/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl +++ b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl @@ -28,4 +28,7 @@ interface IBinderNdkUnitTest { void forceFlushCommands(); boolean getsRequestedSid(); + + void forcePersist(boolean persist); + void setCustomActiveServicesCallback(); } diff --git a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp index ad78e319a2..f3cd21822d 100644 --- a/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp +++ b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp @@ -163,7 +163,8 @@ int main(int argc, char** argv) { // LOCAL SERVERS std::shared_ptr<NdkServer> ndkServer = SharedRefBase::make<NdkServer>(); - AServiceManager_addService(ndkServer->asBinder().get(), kLocalNdkServerName.c_str()); + CHECK(STATUS_OK == + AServiceManager_addService(ndkServer->asBinder().get(), kLocalNdkServerName.c_str())); return RUN_ALL_TESTS(); } diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 0d1989e4d5..de1a48dfd8 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -46,6 +46,11 @@ using namespace android; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest"; constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest"; +constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService"; +constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService"; + +constexpr unsigned int kShutdownWaitTime = 10; +constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715; class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) { @@ -76,6 +81,46 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { fsync(out); return STATUS_OK; } + ndk::ScopedAStatus forcePersist(bool persist) { + AServiceManager_forceLazyServicesPersist(persist); + return ndk::ScopedAStatus::ok(); + } + ndk::ScopedAStatus setCustomActiveServicesCallback() { + AServiceManager_setActiveServicesCallback(activeServicesCallback, this); + return ndk::ScopedAStatus::ok(); + } + static bool activeServicesCallback(bool hasClients, void* context) { + if (hasClients) { + return false; + } + + // Unregister all services + if (!AServiceManager_tryUnregister()) { + // Prevent shutdown (test will fail) + return false; + } + + // Re-register all services + AServiceManager_reRegister(); + + // Unregister again before shutdown + if (!AServiceManager_tryUnregister()) { + // Prevent shutdown (test will fail) + return false; + } + + // Check if the context was passed correctly + MyBinderNdkUnitTest* service = static_cast<MyBinderNdkUnitTest*>(context); + if (service->contextTestValue != kContextTestValue) { + // Prevent shutdown (test will fail) + return false; + } + + exit(EXIT_SUCCESS); + // Unreachable + } + + uint64_t contextTestValue = kContextTestValue; }; int generatedService() { @@ -168,6 +213,16 @@ int lazyService(const char* instance) { return 1; // should not return } +bool isServiceRunning(const char* serviceName) { + AIBinder* binder = AServiceManager_checkService(serviceName); + if (binder == nullptr) { + return false; + } + AIBinder_decStrong(binder); + + return true; +} + TEST(NdkBinder, GetServiceThatDoesntExist) { sp<IFoo> foo = IFoo::getService("asdfghkl;"); EXPECT_EQ(nullptr, foo.get()); @@ -238,10 +293,51 @@ TEST(NdkBinder, CheckLazyServiceShutDown) { service = nullptr; IPCThreadState::self()->flushCommands(); // Make sure the service is dead after some time of no use - sleep(10); + sleep(kShutdownWaitTime); ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService)); } +TEST(NdkBinder, ForcedPersistenceTest) { + for (int i = 0; i < 2; i++) { + ndk::SpAIBinder binder(AServiceManager_waitForService(kForcePersistNdkUnitTestService)); + std::shared_ptr<aidl::IBinderNdkUnitTest> service = + aidl::IBinderNdkUnitTest::fromBinder(binder); + ASSERT_NE(service, nullptr); + ASSERT_TRUE(service->forcePersist(i == 0).isOk()); + + binder = nullptr; + 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."; + } else { + ASSERT_FALSE(isRunning) << "Service failed to shut down."; + } + } +} + +TEST(NdkBinder, ActiveServicesCallbackTest) { + ndk::SpAIBinder binder(AServiceManager_waitForService(kActiveServicesNdkUnitTestService)); + std::shared_ptr<aidl::IBinderNdkUnitTest> service = + aidl::IBinderNdkUnitTest::fromBinder(binder); + ASSERT_NE(service, nullptr); + ASSERT_TRUE(service->setCustomActiveServicesCallback().isOk()); + + binder = nullptr; + service = nullptr; + IPCThreadState::self()->flushCommands(); + + sleep(kShutdownWaitTime); + + ASSERT_FALSE(isServiceRunning(kActiveServicesNdkUnitTestService)) + << "Service failed to shut down."; +} + void LambdaOnDeath(void* cookie) { auto onDeath = static_cast<std::function<void(void)>*>(cookie); (*onDeath)(); @@ -564,10 +660,18 @@ int main(int argc, char* argv[]) { } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); + return lazyService(kForcePersistNdkUnitTestService); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return lazyService(kActiveServicesNdkUnitTestService); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); return generatedService(); } - ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks + ABinderProcess_setThreadPoolMaxThreadCount(1); // to receive death notifications/callbacks ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/libs/binder/parcel_fuzzer/Android.bp b/libs/binder/parcel_fuzzer/Android.bp index 3e6fe99541..74b8eb8d93 100644 --- a/libs/binder/parcel_fuzzer/Android.bp +++ b/libs/binder/parcel_fuzzer/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_fuzz { name: "binder_parcel_fuzzer", defaults: ["libbinder_ndk_host_user"], diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index fd5f2f5d9c..e12a429cf9 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + rust_library { name: "libbinder_rs", crate_name: "binder", @@ -14,7 +23,11 @@ rust_library { darwin: { enabled: false, } - } + }, + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], } rust_library { @@ -32,7 +45,11 @@ rust_library { darwin: { enabled: false, } - } + }, + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], } rust_bindgen { @@ -78,6 +95,10 @@ rust_bindgen { enabled: false, }, }, + apex_available: [ + "//apex_available:platform", + "com.android.virt", + ], } rust_test { diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 42c1e0ac0e..d53a88f726 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -16,12 +16,17 @@ //! Trait definitions for binder objects -use crate::error::{status_t, Result}; +use crate::error::{status_t, Result, StatusCode}; use crate::parcel::Parcel; -use crate::proxy::{DeathRecipient, SpIBinder}; +use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder}; use crate::sys; +use std::borrow::Borrow; +use std::cmp::Ordering; use std::ffi::{c_void, CStr, CString}; +use std::fmt; +use std::marker::PhantomData; +use std::ops::Deref; use std::os::raw::c_char; use std::os::unix::io::AsRawFd; use std::ptr; @@ -44,7 +49,7 @@ pub type TransactionFlags = u32; /// interfaces) must implement this trait. /// /// This is equivalent `IInterface` in C++. -pub trait Interface { +pub trait Interface: Send { /// Convert this binder object into a generic [`SpIBinder`] reference. fn as_binder(&self) -> SpIBinder { panic!("This object was not a Binder object and cannot be converted into an SpIBinder.") @@ -230,6 +235,132 @@ impl From<InterfaceClass> for *const sys::AIBinder_Class { } } +/// Strong reference to a binder object +pub struct Strong<I: FromIBinder + ?Sized>(Box<I>); + +impl<I: FromIBinder + ?Sized> Strong<I> { + /// Create a new strong reference to the provided binder object + pub fn new(binder: Box<I>) -> Self { + Self(binder) + } + + /// Construct a new weak reference to this binder + pub fn downgrade(this: &Strong<I>) -> Weak<I> { + Weak::new(this) + } +} + +impl<I: FromIBinder + ?Sized> Clone for Strong<I> { + fn clone(&self) -> Self { + // Since we hold a strong reference, we should always be able to create + // a new strong reference to the same interface type, so try_from() + // should never fail here. + FromIBinder::try_from(self.0.as_binder()).unwrap() + } +} + +impl<I: FromIBinder + ?Sized> Borrow<I> for Strong<I> { + fn borrow(&self) -> &I { + &self.0 + } +} + +impl<I: FromIBinder + ?Sized> AsRef<I> for Strong<I> { + fn as_ref(&self) -> &I { + &self.0 + } +} + +impl<I: FromIBinder + ?Sized> Deref for Strong<I> { + type Target = I; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<I: FromIBinder + fmt::Debug + ?Sized> fmt::Debug for Strong<I> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl<I: FromIBinder + ?Sized> Ord for Strong<I> { + fn cmp(&self, other: &Self) -> Ordering { + self.0.as_binder().cmp(&other.0.as_binder()) + } +} + +impl<I: FromIBinder + ?Sized> PartialOrd for Strong<I> { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + self.0.as_binder().partial_cmp(&other.0.as_binder()) + } +} + +impl<I: FromIBinder + ?Sized> PartialEq for Strong<I> { + fn eq(&self, other: &Self) -> bool { + self.0.as_binder().eq(&other.0.as_binder()) + } +} + +impl<I: FromIBinder + ?Sized> Eq for Strong<I> {} + +/// Weak reference to a binder object +#[derive(Debug)] +pub struct Weak<I: FromIBinder + ?Sized> { + weak_binder: WpIBinder, + interface_type: PhantomData<I>, +} + +impl<I: FromIBinder + ?Sized> Weak<I> { + /// Construct a new weak reference from a strong reference + fn new(binder: &Strong<I>) -> Self { + let weak_binder = binder.as_binder().downgrade(); + Weak { + weak_binder, + interface_type: PhantomData, + } + } + + /// Upgrade this weak reference to a strong reference if the binder object + /// is still alive + pub fn upgrade(&self) -> Result<Strong<I>> { + self.weak_binder + .promote() + .ok_or(StatusCode::DEAD_OBJECT) + .and_then(FromIBinder::try_from) + } +} + +impl<I: FromIBinder + ?Sized> Clone for Weak<I> { + fn clone(&self) -> Self { + Self { + weak_binder: self.weak_binder.clone(), + interface_type: PhantomData, + } + } +} + +impl<I: FromIBinder + ?Sized> Ord for Weak<I> { + fn cmp(&self, other: &Self) -> Ordering { + self.weak_binder.cmp(&other.weak_binder) + } +} + +impl<I: FromIBinder + ?Sized> PartialOrd for Weak<I> { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + self.weak_binder.partial_cmp(&other.weak_binder) + } +} + +impl<I: FromIBinder + ?Sized> PartialEq for Weak<I> { + fn eq(&self, other: &Self) -> bool { + self.weak_binder == other.weak_binder + } +} + +impl<I: FromIBinder + ?Sized> Eq for Weak<I> {} + /// Create a function implementing a static getter for an interface class. /// /// Each binder interface (i.e. local [`Remotable`] service or remote proxy @@ -354,12 +485,12 @@ pub trait InterfaceClassMethods { /// } /// } /// ``` -pub trait FromIBinder { +pub trait FromIBinder: Interface { /// Try to interpret a generic Binder object as this interface. /// /// Returns a trait object for the `Self` interface if this object /// implements that interface. - fn try_from(ibinder: SpIBinder) -> Result<Box<Self>>; + fn try_from(ibinder: SpIBinder) -> Result<Strong<Self>>; } /// Trait for transparent Rust wrappers around android C++ native types. @@ -534,8 +665,9 @@ macro_rules! declare_binder_interface { impl $native { /// Create a new binder service. - pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> impl $interface { - $crate::Binder::new($native(Box::new(inner))) + pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> $crate::Strong<dyn $interface> { + let binder = $crate::Binder::new($native(Box::new(inner))); + $crate::Strong::new(Box::new(binder)) } } @@ -577,7 +709,7 @@ macro_rules! declare_binder_interface { } impl $crate::FromIBinder for dyn $interface { - fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<Box<dyn $interface>> { + fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $interface>> { use $crate::AssociateClass; let existing_class = ibinder.get_class(); @@ -590,7 +722,7 @@ macro_rules! declare_binder_interface { // associated object as remote, because we can't cast it // into a Rust service object without a matching class // pointer. - return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)); + return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?))); } } @@ -600,10 +732,10 @@ macro_rules! declare_binder_interface { if let Ok(service) = service { // We were able to associate with our expected class and // the service is local. - return Ok(Box::new(service)); + return Ok($crate::Strong::new(Box::new(service))); } else { // Service is remote - return Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)); + return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?))); } } @@ -633,9 +765,9 @@ macro_rules! declare_binder_interface { } } - // Convert a &dyn $interface to Box<dyn $interface> + /// Convert a &dyn $interface to Strong<dyn $interface> impl std::borrow::ToOwned for dyn $interface { - type Owned = Box<dyn $interface>; + type Owned = $crate::Strong<dyn $interface>; fn to_owned(&self) -> Self::Owned { self.as_binder().into_interface() .expect(concat!("Error cloning interface ", stringify!($interface))) diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index edfb56a26c..43a237abf9 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -107,7 +107,8 @@ use binder_ndk_sys as sys; pub mod parcel; pub use crate::binder::{ - FromIBinder, IBinder, Interface, InterfaceClass, Remotable, TransactionCode, TransactionFlags, + FromIBinder, IBinder, Interface, InterfaceClass, Remotable, Strong, TransactionCode, + TransactionFlags, Weak, }; pub use error::{status_t, ExceptionCode, Result, Status, StatusCode}; pub use native::add_service; @@ -122,7 +123,8 @@ pub mod public_api { pub use super::parcel::ParcelFileDescriptor; pub use super::{add_service, get_interface}; pub use super::{ - ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, WpIBinder, + ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, + Weak, WpIBinder, }; /// Binder result containing a [`Status`] on error. diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index 8d18fb486d..f57788b87e 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -14,7 +14,7 @@ * limitations under the License. */ -use crate::binder::{AsNative, FromIBinder}; +use crate::binder::{AsNative, FromIBinder, Strong}; use crate::error::{status_result, status_t, Result, Status, StatusCode}; use crate::parcel::Parcel; use crate::proxy::SpIBinder; @@ -628,26 +628,26 @@ impl Deserialize for Status { } } -impl<T: Serialize + ?Sized> Serialize for Box<T> { +impl<T: Serialize + FromIBinder + ?Sized> Serialize for Strong<T> { fn serialize(&self, parcel: &mut Parcel) -> Result<()> { Serialize::serialize(&**self, parcel) } } -impl<T: SerializeOption + ?Sized> SerializeOption for Box<T> { +impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> { fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { SerializeOption::serialize_option(this.map(|b| &**b), parcel) } } -impl<T: FromIBinder + ?Sized> Deserialize for Box<T> { +impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> { fn deserialize(parcel: &Parcel) -> Result<Self> { let ibinder: SpIBinder = parcel.read()?; FromIBinder::try_from(ibinder) } } -impl<T: FromIBinder + ?Sized> DeserializeOption for Box<T> { +impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> { fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { let ibinder: Option<SpIBinder> = parcel.read()?; ibinder.map(FromIBinder::try_from).transpose() diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index e9e74c05e8..132e0758e1 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -17,7 +17,7 @@ //! Rust API for interacting with a remote binder service. use crate::binder::{ - AsNative, FromIBinder, IBinder, Interface, InterfaceClass, TransactionCode, TransactionFlags, + AsNative, FromIBinder, IBinder, Interface, InterfaceClass, Strong, TransactionCode, TransactionFlags, }; use crate::error::{status_result, Result, StatusCode}; use crate::parcel::{ @@ -27,6 +27,7 @@ use crate::parcel::{ use crate::sys; use std::convert::TryInto; +use std::cmp::Ordering; use std::ffi::{c_void, CString}; use std::fmt; use std::os::unix::io::AsRawFd; @@ -99,7 +100,7 @@ impl SpIBinder { /// /// If this object does not implement the expected interface, the error /// `StatusCode::BAD_TYPE` is returned. - pub fn into_interface<I: FromIBinder + ?Sized>(self) -> Result<Box<I>> { + pub fn into_interface<I: FromIBinder + Interface + ?Sized>(self) -> Result<Strong<I>> { FromIBinder::try_from(self) } @@ -148,6 +149,36 @@ impl AssociateClass for SpIBinder { } } +impl Ord for SpIBinder { + fn cmp(&self, other: &Self) -> Ordering { + let less_than = unsafe { + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so + // this pointer is always safe to pass to `AIBinder_lt` (null is + // also safe to pass to this function, but we should never do that). + sys::AIBinder_lt(self.0, other.0) + }; + let greater_than = unsafe { + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so + // this pointer is always safe to pass to `AIBinder_lt` (null is + // also safe to pass to this function, but we should never do that). + sys::AIBinder_lt(other.0, self.0) + }; + if !less_than && !greater_than { + Ordering::Equal + } else if less_than { + Ordering::Less + } else { + Ordering::Greater + } + } +} + +impl PartialOrd for SpIBinder { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + impl PartialEq for SpIBinder { fn eq(&self, other: &Self) -> bool { ptr::eq(self.0, other.0) @@ -326,7 +357,7 @@ impl<T: AsNative<sys::AIBinder>> IBinder for T { // Safety: `SpIBinder` guarantees that `self` always contains a // valid pointer to an `AIBinder`. `recipient` can always be // converted into a valid pointer to an - // `AIBinder_DeatRecipient`. Any value is safe to pass as the + // `AIBinder_DeathRecipient`. Any value is safe to pass as the // cookie, although we depend on this value being set by // `get_cookie` when the death recipient callback is called. sys::AIBinder_linkToDeath( @@ -342,7 +373,7 @@ impl<T: AsNative<sys::AIBinder>> IBinder for T { // Safety: `SpIBinder` guarantees that `self` always contains a // valid pointer to an `AIBinder`. `recipient` can always be // converted into a valid pointer to an - // `AIBinder_DeatRecipient`. Any value is safe to pass as the + // `AIBinder_DeathRecipient`. Any value is safe to pass as the // cookie, although we depend on this value being set by // `get_cookie` when the death recipient callback is called. sys::AIBinder_unlinkToDeath( @@ -430,6 +461,62 @@ impl WpIBinder { } } +impl Clone for WpIBinder { + fn clone(&self) -> Self { + let ptr = unsafe { + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, + // so this pointer is always safe to pass to `AIBinder_Weak_clone` + // (although null is also a safe value to pass to this API). + // + // We get ownership of the returned pointer, so can construct a new + // WpIBinder object from it. + sys::AIBinder_Weak_clone(self.0) + }; + assert!(!ptr.is_null(), "Unexpected null pointer from AIBinder_Weak_clone"); + Self(ptr) + } +} + +impl Ord for WpIBinder { + fn cmp(&self, other: &Self) -> Ordering { + let less_than = unsafe { + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, + // so this pointer is always safe to pass to `AIBinder_Weak_lt` + // (null is also safe to pass to this function, but we should never + // do that). + sys::AIBinder_Weak_lt(self.0, other.0) + }; + let greater_than = unsafe { + // Safety: WpIBinder always holds a valid `AIBinder_Weak` pointer, + // so this pointer is always safe to pass to `AIBinder_Weak_lt` + // (null is also safe to pass to this function, but we should never + // do that). + sys::AIBinder_Weak_lt(other.0, self.0) + }; + if !less_than && !greater_than { + Ordering::Equal + } else if less_than { + Ordering::Less + } else { + Ordering::Greater + } + } +} + +impl PartialOrd for WpIBinder { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl PartialEq for WpIBinder { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for WpIBinder {} + impl Drop for WpIBinder { fn drop(&mut self) { unsafe { @@ -564,7 +651,7 @@ pub fn get_service(name: &str) -> Option<SpIBinder> { /// Retrieve an existing service for a particular interface, blocking for a few /// seconds if it doesn't yet exist. -pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Box<T>> { +pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { let service = get_service(name); match service { Some(service) => FromIBinder::try_from(service), diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp index 8810b5dd16..0bf76c696a 100644 --- a/libs/binder/rust/tests/Android.bp +++ b/libs/binder/rust/tests/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + rust_test { name: "rustBinderTest", srcs: ["integration.rs"], diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index bb8c492bdb..719229c9b7 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -209,7 +209,7 @@ mod tests { use std::thread; use std::time::Duration; - use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode}; + use binder::{Binder, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode, Strong}; use super::{BnTest, ITest, ITestSameDescriptor, RUST_SERVICE_BINARY, TestService}; @@ -271,7 +271,7 @@ mod tests { fn trivial_client() { let service_name = "trivial_client_test"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Box<dyn ITest> = + let test_client: Strong<dyn ITest> = binder::get_interface(service_name).expect("Did not get manager binder service"); assert_eq!(test_client.test().unwrap(), "trivial_client_test"); } @@ -280,7 +280,7 @@ mod tests { fn get_selinux_context() { let service_name = "get_selinux_context"; let _process = ScopedServiceProcess::new(service_name); - let test_client: Box<dyn ITest> = + let test_client: Strong<dyn ITest> = binder::get_interface(service_name).expect("Did not get manager binder service"); let expected_context = unsafe { let mut out_ptr = ptr::null_mut(); @@ -453,7 +453,7 @@ mod tests { let extension = maybe_extension.expect("Remote binder did not have an extension"); - let extension: Box<dyn ITest> = FromIBinder::try_from(extension) + let extension: Strong<dyn ITest> = FromIBinder::try_from(extension) .expect("Extension could not be converted to the expected interface"); assert_eq!(extension.test().unwrap(), extension_name); @@ -479,7 +479,7 @@ mod tests { // This should succeed although we will have to treat the service as // remote. - let _interface: Box<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder()) + let _interface: Strong<dyn ITestSameDescriptor> = FromIBinder::try_from(service.as_binder()) .expect("Could not re-interpret service as the ITestSameDescriptor interface"); } @@ -490,9 +490,60 @@ mod tests { let service_ibinder = BnTest::new_binder(TestService { s: service_name.to_string() }) .as_binder(); - let service: Box<dyn ITest> = service_ibinder.into_interface() + let service: Strong<dyn ITest> = service_ibinder.into_interface() .expect("Could not reassociate the generic ibinder"); assert_eq!(service.test().unwrap(), service_name); } + + #[test] + fn weak_binder_upgrade() { + let service_name = "testing_service"; + let service = BnTest::new_binder(TestService { s: service_name.to_string() }); + + let weak = Strong::downgrade(&service); + + let upgraded = weak.upgrade().expect("Could not upgrade weak binder"); + + assert_eq!(service, upgraded); + } + + #[test] + fn weak_binder_upgrade_dead() { + let service_name = "testing_service"; + let weak = { + let service = BnTest::new_binder(TestService { s: service_name.to_string() }); + + Strong::downgrade(&service) + }; + + assert_eq!(weak.upgrade(), Err(StatusCode::DEAD_OBJECT)); + } + + #[test] + fn weak_binder_clone() { + let service_name = "testing_service"; + let service = BnTest::new_binder(TestService { s: service_name.to_string() }); + + let weak = Strong::downgrade(&service); + let cloned = weak.clone(); + assert_eq!(weak, cloned); + + let upgraded = weak.upgrade().expect("Could not upgrade weak binder"); + let clone_upgraded = cloned.upgrade().expect("Could not upgrade weak binder"); + + assert_eq!(service, upgraded); + assert_eq!(service, clone_upgraded); + } + + #[test] + #[allow(clippy::eq_op)] + fn binder_ord() { + let service1 = BnTest::new_binder(TestService { s: "testing_service1".to_string() }); + let service2 = BnTest::new_binder(TestService { s: "testing_service2".to_string() }); + + assert!(!(service1 < service1)); + assert!(!(service1 > service1)); + assert_eq!(service1 < service2, !(service2 < service1)); + } } diff --git a/libs/binder/rust/tests/ndk_rust_interop.rs b/libs/binder/rust/tests/ndk_rust_interop.rs index 70a6dc0788..ce75ab7125 100644 --- a/libs/binder/rust/tests/ndk_rust_interop.rs +++ b/libs/binder/rust/tests/ndk_rust_interop.rs @@ -37,7 +37,7 @@ pub unsafe extern "C" fn rust_call_ndk(service_name: *const c_char) -> c_int { // The Rust class descriptor pointer will not match the NDK one, but the // descriptor strings match so this needs to still associate. - let service: Box<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) { + let service: binder::Strong<dyn IBinderRustNdkInteropTest> = match binder::get_interface(service_name) { Err(e) => { eprintln!("Could not find Ndk service {}: {:?}", service_name, e); return StatusCode::NAME_NOT_FOUND as c_int; @@ -53,7 +53,7 @@ pub unsafe extern "C" fn rust_call_ndk(service_name: *const c_char) -> c_int { } // Try using the binder service through the wrong interface type - let wrong_service: Result<Box<dyn IBinderRustNdkInteropTestOther>, StatusCode> = + let wrong_service: Result<binder::Strong<dyn IBinderRustNdkInteropTestOther>, StatusCode> = binder::get_interface(service_name); match wrong_service { Err(e) if e == StatusCode::BAD_TYPE => {} diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 259417a655..3bbb0b52bb 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "binder_test_defaults", cflags: [ diff --git a/libs/binder/tests/binderParcelBenchmark.cpp b/libs/binder/tests/binderParcelBenchmark.cpp index ec69c367ba..26c50ebb57 100644 --- a/libs/binder/tests/binderParcelBenchmark.cpp +++ b/libs/binder/tests/binderParcelBenchmark.cpp @@ -91,56 +91,56 @@ static void BM_ParcelVector(benchmark::State& state) { Results on Crosshatch Pixel 3XL - #BM_BoolVector/1 40 ns 40 ns 17261011 - #BM_BoolVector/2 46 ns 46 ns 15029619 - #BM_BoolVector/4 65 ns 64 ns 10888021 - #BM_BoolVector/8 114 ns 114 ns 6130937 - #BM_BoolVector/16 179 ns 179 ns 3902462 - #BM_BoolVector/32 328 ns 327 ns 2138812 - #BM_BoolVector/64 600 ns 598 ns 1169414 - #BM_BoolVector/128 1168 ns 1165 ns 601281 - #BM_BoolVector/256 2288 ns 2281 ns 305737 - #BM_BoolVector/512 4535 ns 4521 ns 154668 - #BM_ByteVector/1 53 ns 52 ns 13212196 - #BM_ByteVector/2 53 ns 53 ns 13194050 - #BM_ByteVector/4 50 ns 50 ns 13768037 - #BM_ByteVector/8 50 ns 50 ns 13890210 - #BM_ByteVector/16 50 ns 50 ns 13897305 - #BM_ByteVector/32 51 ns 51 ns 13679862 - #BM_ByteVector/64 54 ns 53 ns 12988544 - #BM_ByteVector/128 64 ns 64 ns 10921227 - #BM_ByteVector/256 82 ns 81 ns 8542549 - #BM_ByteVector/512 118 ns 118 ns 5862931 - #BM_CharVector/1 32 ns 32 ns 21783579 - #BM_CharVector/2 38 ns 38 ns 18200971 - #BM_CharVector/4 53 ns 53 ns 13111785 - #BM_CharVector/8 80 ns 80 ns 8698331 - #BM_CharVector/16 159 ns 159 ns 4390738 - #BM_CharVector/32 263 ns 262 ns 2667310 - #BM_CharVector/64 486 ns 485 ns 1441118 - #BM_CharVector/128 937 ns 934 ns 749006 - #BM_CharVector/256 1848 ns 1843 ns 379537 - #BM_CharVector/512 3650 ns 3639 ns 191713 - #BM_Int32Vector/1 31 ns 31 ns 22104147 - #BM_Int32Vector/2 38 ns 38 ns 18075471 - #BM_Int32Vector/4 53 ns 52 ns 13249969 - #BM_Int32Vector/8 80 ns 80 ns 8719798 - #BM_Int32Vector/16 161 ns 160 ns 4350096 - #BM_Int32Vector/32 271 ns 270 ns 2591896 - #BM_Int32Vector/64 499 ns 498 ns 1406201 - #BM_Int32Vector/128 948 ns 945 ns 740052 - #BM_Int32Vector/256 1855 ns 1849 ns 379127 - #BM_Int32Vector/512 3665 ns 3653 ns 191533 - #BM_Int64Vector/1 31 ns 31 ns 22388370 - #BM_Int64Vector/2 38 ns 38 ns 18300347 - #BM_Int64Vector/4 53 ns 53 ns 13137818 - #BM_Int64Vector/8 81 ns 81 ns 8599613 - #BM_Int64Vector/16 167 ns 166 ns 4195953 - #BM_Int64Vector/32 280 ns 280 ns 2499271 - #BM_Int64Vector/64 523 ns 522 ns 1341380 - #BM_Int64Vector/128 991 ns 988 ns 707437 - #BM_Int64Vector/256 1940 ns 1934 ns 361704 - #BM_Int64Vector/512 3843 ns 3831 ns 183204 + #BM_BoolVector/1 44 ns 44 ns 15630626 + #BM_BoolVector/2 54 ns 54 ns 12900340 + #BM_BoolVector/4 73 ns 72 ns 9749841 + #BM_BoolVector/8 107 ns 107 ns 6503326 + #BM_BoolVector/16 186 ns 185 ns 3773627 + #BM_BoolVector/32 337 ns 336 ns 2083877 + #BM_BoolVector/64 607 ns 605 ns 1154113 + #BM_BoolVector/128 1155 ns 1151 ns 608128 + #BM_BoolVector/256 2259 ns 2253 ns 310973 + #BM_BoolVector/512 4469 ns 4455 ns 157277 + #BM_ByteVector/1 41 ns 41 ns 16837425 + #BM_ByteVector/2 41 ns 41 ns 16820726 + #BM_ByteVector/4 38 ns 38 ns 18217813 + #BM_ByteVector/8 38 ns 38 ns 18290298 + #BM_ByteVector/16 38 ns 38 ns 18117817 + #BM_ByteVector/32 38 ns 38 ns 18172385 + #BM_ByteVector/64 41 ns 41 ns 16950055 + #BM_ByteVector/128 53 ns 53 ns 13170749 + #BM_ByteVector/256 69 ns 69 ns 10113626 + #BM_ByteVector/512 106 ns 106 ns 6561936 + #BM_CharVector/1 38 ns 38 ns 18074831 + #BM_CharVector/2 40 ns 40 ns 17206266 + #BM_CharVector/4 50 ns 50 ns 13785944 + #BM_CharVector/8 67 ns 67 ns 10223316 + #BM_CharVector/16 96 ns 96 ns 7297285 + #BM_CharVector/32 156 ns 155 ns 4484845 + #BM_CharVector/64 277 ns 276 ns 2536003 + #BM_CharVector/128 520 ns 518 ns 1347070 + #BM_CharVector/256 1006 ns 1003 ns 695952 + #BM_CharVector/512 1976 ns 1970 ns 354673 + #BM_Int32Vector/1 41 ns 41 ns 16951262 + #BM_Int32Vector/2 41 ns 41 ns 16916883 + #BM_Int32Vector/4 41 ns 41 ns 16761373 + #BM_Int32Vector/8 42 ns 42 ns 16553179 + #BM_Int32Vector/16 43 ns 43 ns 16200362 + #BM_Int32Vector/32 55 ns 54 ns 12724454 + #BM_Int32Vector/64 70 ns 69 ns 10049223 + #BM_Int32Vector/128 107 ns 107 ns 6525796 + #BM_Int32Vector/256 179 ns 178 ns 3922563 + #BM_Int32Vector/512 324 ns 323 ns 2160653 + #BM_Int64Vector/1 41 ns 41 ns 16909470 + #BM_Int64Vector/2 41 ns 41 ns 16740788 + #BM_Int64Vector/4 42 ns 42 ns 16564197 + #BM_Int64Vector/8 43 ns 42 ns 16284082 + #BM_Int64Vector/16 54 ns 54 ns 12839474 + #BM_Int64Vector/32 69 ns 69 ns 10011010 + #BM_Int64Vector/64 107 ns 106 ns 6557956 + #BM_Int64Vector/128 177 ns 177 ns 3925618 + #BM_Int64Vector/256 324 ns 323 ns 2163321 + #BM_Int64Vector/512 613 ns 611 ns 1140418 */ static void BM_BoolVector(benchmark::State& state) { diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp index 5531296edb..b1263e8d8e 100644 --- a/libs/binder/tests/fuzzers/Android.bp +++ b/libs/binder/tests/fuzzers/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "binder_fuzz_defaults", host_supported: true, diff --git a/libs/binderdebug/Android.bp b/libs/binderdebug/Android.bp index 343246a324..3eeaf3e4f1 100644 --- a/libs/binderdebug/Android.bp +++ b/libs/binderdebug/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libbinderdebug", vendor_available: true, diff --git a/libs/binderdebug/tests/Android.bp b/libs/binderdebug/tests/Android.bp index 4c06b1d0d9..d141a05ca9 100644 --- a/libs/binderdebug/tests/Android.bp +++ b/libs/binderdebug/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "libbinderdebug_test", test_suites: ["general-tests"], diff --git a/libs/binderthreadstate/1.0/Android.bp b/libs/binderthreadstate/1.0/Android.bp index ebdc932591..99477d8e26 100644 --- a/libs/binderthreadstate/1.0/Android.bp +++ b/libs/binderthreadstate/1.0/Android.bp @@ -1,5 +1,14 @@ // This file is autogenerated by hidl-gen -Landroidbp. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + hidl_interface { name: "binderthreadstateutilstest@1.0", root: "binderthreadstateutilstest", diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp index 08c62df072..0a82463feb 100644 --- a/libs/binderthreadstate/Android.bp +++ b/libs/binderthreadstate/Android.bp @@ -14,6 +14,15 @@ // DO NOT ADD NEW USAGES OF THIS // See comments in header file. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libbinderthreadstateutils", double_loadable: true, diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index bab267466c..c5d3a3207c 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libbufferqueueconverter_headers", vendor_available: true, diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp index e3cd0859cb..570af71d9a 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libtimeinstate", srcs: ["cputimeinstate.cpp"], @@ -35,4 +44,3 @@ cc_test { ], require_root: true, } - diff --git a/libs/diskusage/Android.bp b/libs/diskusage/Android.bp index a8263069de..86840613e6 100644 --- a/libs/diskusage/Android.bp +++ b/libs/diskusage/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libdiskusage", srcs: ["dirsize.c"], diff --git a/libs/dumputils/Android.bp b/libs/dumputils/Android.bp index e403d36da1..acda402993 100644 --- a/libs/dumputils/Android.bp +++ b/libs/dumputils/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libdumputils", diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp index 76518c1286..47c0657bbd 100644 --- a/libs/fakeservicemanager/Android.bp +++ b/libs/fakeservicemanager/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "fakeservicemanager_defaults", host_supported: true, diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index dd0ae30703..a0032aecb9 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libgralloctypes", defaults: ["libbinder_ndk_host_user"], diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp index 8933dc323f..6689771a24 100644 --- a/libs/gralloc/types/fuzzer/Android.bp +++ b/libs/gralloc/types/fuzzer/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_fuzz { name: "libgralloctypes_fuzzer", defaults: ["libbinder_ndk_host_user"], diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp index b939c1db59..66eb0aa2fe 100644 --- a/libs/gralloc/types/tests/Android.bp +++ b/libs/gralloc/types/tests/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "GrallocTypes_test", shared_libs: [ diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index 642c5f2cc0..a96a07a9b8 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libgraphicsenv", diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index fa5044cc16..debd664aba 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -11,6 +11,15 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libgui_headers", vendor_available: true, diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 1e6fc2b217..82c9268feb 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -55,7 +55,7 @@ namespace android { ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) void BLASTBufferItemConsumer::onDisconnect() { - Mutex::Autolock lock(mFrameEventHistoryMutex); + Mutex::Autolock lock(mMutex); mPreviouslyConnected = mCurrentlyConnected; mCurrentlyConnected = false; if (mPreviouslyConnected) { @@ -66,7 +66,7 @@ void BLASTBufferItemConsumer::onDisconnect() { void BLASTBufferItemConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) { - Mutex::Autolock lock(mFrameEventHistoryMutex); + Mutex::Autolock lock(mMutex); if (newTimestamps) { // BufferQueueProducer only adds a new timestamp on // queueBuffer @@ -90,7 +90,7 @@ void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_ const sp<Fence>& prevReleaseFence, CompositorTiming compositorTiming, nsecs_t latchTime, nsecs_t dequeueReadyTime) { - Mutex::Autolock lock(mFrameEventHistoryMutex); + Mutex::Autolock lock(mMutex); // if the producer is not connected, don't bother updating, // the next producer that connects won't access this frame event @@ -108,7 +108,7 @@ void BLASTBufferItemConsumer::updateFrameTimestamps(uint64_t frameNumber, nsecs_ void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect) { bool disconnect = false; - Mutex::Autolock lock(mFrameEventHistoryMutex); + Mutex::Autolock lock(mMutex); while (!mDisconnectEvents.empty() && mDisconnectEvents.front() <= frameNumber) { disconnect = true; mDisconnectEvents.pop(); @@ -116,6 +116,19 @@ void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* ne if (needsDisconnect != nullptr) *needsDisconnect = disconnect; } +void BLASTBufferItemConsumer::setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) { + Mutex::Autolock lock(mMutex); + mBLASTBufferQueue = blastbufferqueue; +} + +void BLASTBufferItemConsumer::onSidebandStreamChanged() { + Mutex::Autolock lock(mMutex); + if (mBLASTBufferQueue != nullptr) { + sp<NativeHandle> stream = getSidebandStream(); + mBLASTBufferQueue->setSidebandStream(stream); + } +} + BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, int height, int32_t format, bool enableTripleBuffering) @@ -145,6 +158,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont mBufferItemConsumer->setBufferFreedListener(this); mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height); mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format)); + mBufferItemConsumer->setBlastBufferQueue(this); mTransformHint = mSurfaceControl->getTransformHint(); mBufferItemConsumer->setTransformHint(mTransformHint); @@ -160,6 +174,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont } BLASTBufferQueue::~BLASTBufferQueue() { + mBufferItemConsumer->setBlastBufferQueue(nullptr); if (mPendingTransactions.empty()) { return; } @@ -557,6 +572,13 @@ status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTi return OK; } +void BLASTBufferQueue::setSidebandStream(const sp<NativeHandle>& stream) { + std::unique_lock _lock{mMutex}; + SurfaceComposerClient::Transaction t; + + t.setSidebandStream(mSurfaceControl, stream).apply(); +} + sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) { std::unique_lock _lock{mMutex}; sp<IBinder> scHandle = nullptr; diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 762746c0ce..eba7b3c9fd 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -36,11 +36,12 @@ #include <system/graphics.h> -#include <ui/DisplayInfo.h> #include <ui/DisplayMode.h> #include <ui/DisplayStatInfo.h> #include <ui/DisplayState.h> +#include <ui/DynamicDisplayInfo.h> #include <ui/HdrCapabilities.h> +#include <ui/StaticDisplayInfo.h> #include <utils/Log.h> @@ -323,32 +324,26 @@ public: return result; } - status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) override { + status_t getStaticDisplayInfo(const sp<IBinder>& display, + ui::StaticDisplayInfo* info) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply); + remote()->transact(BnSurfaceComposer::GET_STATIC_DISPLAY_INFO, data, &reply); const status_t result = reply.readInt32(); if (result != NO_ERROR) return result; return reply.read(*info); } - status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>* modes) override { + status_t getDynamicDisplayInfo(const sp<IBinder>& display, + ui::DynamicDisplayInfo* info) override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_DISPLAY_MODES, data, &reply); + remote()->transact(BnSurfaceComposer::GET_DYNAMIC_DISPLAY_INFO, data, &reply); const status_t result = reply.readInt32(); - if (result == NO_ERROR) { - const size_t numModes = reply.readUint32(); - modes->clear(); - modes->resize(numModes); - for (size_t i = 0; i < numModes; i++) { - memcpy(&(modes->editItemAt(i)), reply.readInplace(sizeof(ui::DisplayMode)), - sizeof(ui::DisplayMode)); - } - } - return result; + if (result != NO_ERROR) return result; + return reply.read(*info); } status_t getDisplayStats(const sp<IBinder>& display, DisplayStatInfo* stats) override { @@ -365,44 +360,6 @@ public: return result; } - int getActiveDisplayModeId(const sp<IBinder>& display) override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - remote()->transact(BnSurfaceComposer::GET_ACTIVE_DISPLAY_MODE, data, &reply); - return reply.readInt32(); - } - - status_t getDisplayColorModes(const sp<IBinder>& display, - Vector<ColorMode>* outColorModes) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("getDisplayColorModes failed to writeInterfaceToken: %d", result); - return result; - } - result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("getDisplayColorModes failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::GET_DISPLAY_COLOR_MODES, data, &reply); - if (result != NO_ERROR) { - ALOGE("getDisplayColorModes failed to transact: %d", result); - return result; - } - result = static_cast<status_t>(reply.readInt32()); - if (result == NO_ERROR) { - size_t numModes = reply.readUint32(); - outColorModes->clear(); - outColorModes->resize(numModes); - for (size_t i = 0; i < numModes; ++i) { - outColorModes->replaceAt(static_cast<ColorMode>(reply.readInt32()), i); - } - } - return result; - } - status_t getDisplayNativePrimaries(const sp<IBinder>& display, ui::DisplayPrimaries& primaries) override { Parcel data, reply; @@ -429,26 +386,6 @@ public: return result; } - ColorMode getActiveColorMode(const sp<IBinder>& display) override { - Parcel data, reply; - status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - if (result != NO_ERROR) { - ALOGE("getActiveColorMode failed to writeInterfaceToken: %d", result); - return static_cast<ColorMode>(result); - } - result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("getActiveColorMode failed to writeStrongBinder: %d", result); - return static_cast<ColorMode>(result); - } - result = remote()->transact(BnSurfaceComposer::GET_ACTIVE_COLOR_MODE, data, &reply); - if (result != NO_ERROR) { - ALOGE("getActiveColorMode failed to transact: %d", result); - return static_cast<ColorMode>(result); - } - return static_cast<ColorMode>(reply.readInt32()); - } - status_t setActiveColorMode(const sp<IBinder>& display, ColorMode colorMode) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -474,24 +411,6 @@ public: return static_cast<status_t>(reply.readInt32()); } - status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display, - bool* outSupport) const override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - status_t result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data, - &reply); - if (result != NO_ERROR) { - ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result); - return result; - } - return reply.readBool(outSupport); - } - void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -517,23 +436,6 @@ public: } } - status_t getGameContentTypeSupport(const sp<IBinder>& display, - bool* outSupport) const override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - status_t result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply); - if (result != NO_ERROR) { - ALOGE("getGameContentTypeSupport failed to transact: %d", result); - return result; - } - return reply.readBool(outSupport); - } - void setGameContentType(const sp<IBinder>& display, bool on) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -580,28 +482,6 @@ public: return reply.readInt32(); } - status_t getHdrCapabilities(const sp<IBinder>& display, - HdrCapabilities* outCapabilities) const override { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - status_t result = data.writeStrongBinder(display); - if (result != NO_ERROR) { - ALOGE("getHdrCapabilities failed to writeStrongBinder: %d", result); - return result; - } - result = remote()->transact(BnSurfaceComposer::GET_HDR_CAPABILITIES, - data, &reply); - if (result != NO_ERROR) { - ALOGE("getHdrCapabilities failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result == NO_ERROR) { - result = reply.read(*outCapabilities); - } - return result; - } - status_t enableVSyncInjections(bool enable) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -881,9 +761,10 @@ public: return error; } - status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode, - bool allowGroupSwitching, float primaryRefreshRateMin, - float primaryRefreshRateMax, float appRequestRefreshRateMin, + status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + ui::DisplayModeId defaultMode, bool allowGroupSwitching, + float primaryRefreshRateMin, float primaryRefreshRateMax, + float appRequestRefreshRateMin, float appRequestRefreshRateMax) override { Parcel data, reply; status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -938,7 +819,8 @@ public: return reply.readInt32(); } - status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t* outDefaultMode, + status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + ui::DisplayModeId* outDefaultMode, bool* outAllowGroupSwitching, float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, @@ -966,17 +848,16 @@ public: ALOGE("getDesiredDisplayModeSpecs failed to transact: %d", result); return result; } - int32_t defaultMode; - result = reply.readInt32(&defaultMode); + + result = reply.readInt32(outDefaultMode); if (result != NO_ERROR) { ALOGE("getDesiredDisplayModeSpecs failed to read defaultMode: %d", result); return result; } - if (defaultMode < 0) { - ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, defaultMode); + if (*outDefaultMode < 0) { + ALOGE("%s: defaultMode must be non-negative but it was %d", __func__, *outDefaultMode); return BAD_VALUE; } - *outDefaultMode = static_cast<size_t>(defaultMode); result = reply.readBool(outAllowGroupSwitching); if (result != NO_ERROR) { @@ -1436,28 +1317,24 @@ status_t BnSurfaceComposer::onTransact( } return NO_ERROR; } - case GET_DISPLAY_INFO: { + case GET_STATIC_DISPLAY_INFO: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - DisplayInfo info; + ui::StaticDisplayInfo info; const sp<IBinder> display = data.readStrongBinder(); - const status_t result = getDisplayInfo(display, &info); - reply->writeInt32(result); + const status_t result = getStaticDisplayInfo(display, &info); + SAFE_PARCEL(reply->writeInt32, result); if (result != NO_ERROR) return result; - return reply->write(info); + SAFE_PARCEL(reply->write, info); + return NO_ERROR; } - case GET_DISPLAY_MODES: { + case GET_DYNAMIC_DISPLAY_INFO: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - Vector<ui::DisplayMode> modes; + ui::DynamicDisplayInfo info; const sp<IBinder> display = data.readStrongBinder(); - const status_t result = getDisplayModes(display, &modes); - reply->writeInt32(result); - if (result == NO_ERROR) { - reply->writeUint32(static_cast<uint32_t>(modes.size())); - for (size_t i = 0; i < modes.size(); i++) { - memcpy(reply->writeInplace(sizeof(ui::DisplayMode)), &modes[i], - sizeof(ui::DisplayMode)); - } - } + const status_t result = getDynamicDisplayInfo(display, &info); + SAFE_PARCEL(reply->writeInt32, result); + if (result != NO_ERROR) return result; + SAFE_PARCEL(reply->write, info); return NO_ERROR; } case GET_DISPLAY_STATS: { @@ -1472,32 +1349,6 @@ status_t BnSurfaceComposer::onTransact( } return NO_ERROR; } - case GET_ACTIVE_DISPLAY_MODE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = data.readStrongBinder(); - int id = getActiveDisplayModeId(display); - reply->writeInt32(id); - return NO_ERROR; - } - case GET_DISPLAY_COLOR_MODES: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - Vector<ColorMode> colorModes; - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getDisplayColorModes failed to readStrongBinder: %d", result); - return result; - } - result = getDisplayColorModes(display, &colorModes); - reply->writeInt32(result); - if (result == NO_ERROR) { - reply->writeUint32(static_cast<uint32_t>(colorModes.size())); - for (size_t i = 0; i < colorModes.size(); ++i) { - reply->writeInt32(static_cast<int32_t>(colorModes[i])); - } - } - return NO_ERROR; - } case GET_DISPLAY_NATIVE_PRIMARIES: { CHECK_INTERFACE(ISurfaceComposer, data, reply); ui::DisplayPrimaries primaries; @@ -1518,18 +1369,6 @@ status_t BnSurfaceComposer::onTransact( return NO_ERROR; } - case GET_ACTIVE_COLOR_MODE: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getActiveColorMode failed to readStrongBinder: %d", result); - return result; - } - ColorMode colorMode = getActiveColorMode(display); - result = reply->writeInt32(static_cast<int32_t>(colorMode)); - return result; - } case SET_ACTIVE_COLOR_MODE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = nullptr; @@ -1549,23 +1388,6 @@ status_t BnSurfaceComposer::onTransact( result = reply->writeInt32(result); return result; } - - case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result); - return result; - } - bool supported = false; - result = getAutoLowLatencyModeSupport(display, &supported); - if (result == NO_ERROR) { - result = reply->writeBool(supported); - } - return result; - } - case SET_AUTO_LOW_LATENCY_MODE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = nullptr; @@ -1583,23 +1405,6 @@ status_t BnSurfaceComposer::onTransact( setAutoLowLatencyMode(display, setAllm); return result; } - - case GET_GAME_CONTENT_TYPE_SUPPORT: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result); - return result; - } - bool supported = false; - result = getGameContentTypeSupport(display, &supported); - if (result == NO_ERROR) { - result = reply->writeBool(supported); - } - return result; - } - case SET_GAME_CONTENT_TYPE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> display = nullptr; @@ -1617,7 +1422,6 @@ status_t BnSurfaceComposer::onTransact( setGameContentType(display, setGameContentTypeOn); return result; } - case CLEAR_ANIMATION_FRAME_STATS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); status_t result = clearAnimationFrameStats(); @@ -1639,23 +1443,6 @@ status_t BnSurfaceComposer::onTransact( setPowerMode(display, mode); return NO_ERROR; } - case GET_HDR_CAPABILITIES: { - CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = nullptr; - status_t result = data.readStrongBinder(&display); - if (result != NO_ERROR) { - ALOGE("getHdrCapabilities failed to readStrongBinder: %d", - result); - return result; - } - HdrCapabilities capabilities; - result = getHdrCapabilities(display, &capabilities); - reply->writeInt32(result); - if (result == NO_ERROR) { - reply->write(capabilities); - } - return NO_ERROR; - } case ENABLE_VSYNC_INJECTIONS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bool enable = false; @@ -1862,7 +1649,7 @@ status_t BnSurfaceComposer::onTransact( case SET_DESIRED_DISPLAY_MODE_SPECS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> displayToken = data.readStrongBinder(); - int32_t defaultMode; + ui::DisplayModeId defaultMode; status_t result = data.readInt32(&defaultMode); if (result != NO_ERROR) { ALOGE("setDesiredDisplayModeSpecs: failed to read defaultMode: %d", result); @@ -1906,10 +1693,9 @@ status_t BnSurfaceComposer::onTransact( result); return result; } - result = setDesiredDisplayModeSpecs(displayToken, static_cast<size_t>(defaultMode), - allowGroupSwitching, primaryRefreshRateMin, - primaryRefreshRateMax, appRequestRefreshRateMin, - appRequestRefreshRateMax); + result = setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching, + primaryRefreshRateMin, primaryRefreshRateMax, + appRequestRefreshRateMin, appRequestRefreshRateMax); if (result != NO_ERROR) { ALOGE("setDesiredDisplayModeSpecs: failed to call setDesiredDisplayModeSpecs: " "%d", @@ -1922,7 +1708,7 @@ status_t BnSurfaceComposer::onTransact( case GET_DESIRED_DISPLAY_MODE_SPECS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> displayToken = data.readStrongBinder(); - size_t defaultMode; + ui::DisplayModeId defaultMode; bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; @@ -1941,7 +1727,7 @@ status_t BnSurfaceComposer::onTransact( return result; } - result = reply->writeInt32(static_cast<int32_t>(defaultMode)); + result = reply->writeInt32(defaultMode); if (result != NO_ERROR) { ALOGE("getDesiredDisplayModeSpecs: failed to write defaultMode: %d", result); return result; diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp index cdde9a2308..e707684618 100644 --- a/libs/gui/LayerDebugInfo.cpp +++ b/libs/gui/LayerDebugInfo.cpp @@ -61,6 +61,7 @@ status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { RETURN_ON_ERROR(parcel->writeBool(mRefreshPending)); RETURN_ON_ERROR(parcel->writeBool(mIsOpaque)); RETURN_ON_ERROR(parcel->writeBool(mContentDirty)); + RETURN_ON_ERROR(parcel->write(mStretchEffect)); return NO_ERROR; } @@ -105,6 +106,7 @@ status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { RETURN_ON_ERROR(parcel->readBool(&mRefreshPending)); RETURN_ON_ERROR(parcel->readBool(&mIsOpaque)); RETURN_ON_ERROR(parcel->readBool(&mContentDirty)); + RETURN_ON_ERROR(parcel->read(mStretchEffect)); return NO_ERROR; } @@ -115,6 +117,12 @@ std::string to_string(const LayerDebugInfo& info) { info.mTransparentRegion.dump(result, "TransparentRegion"); info.mVisibleRegion.dump(result, "VisibleRegion"); info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); + if (info.mStretchEffect.hasEffect()) { + const auto& se = info.mStretchEffect; + StringAppendF(&result, " StretchEffect area=[%f, %f, %f, %f] vec=(%f, %f) maxAmount=%f\n", + se.area.left, se.area.top, se.area.right, se.area.bottom, se.vectorX, + se.vectorY, se.maxAmount); + } StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", info.mLayerStack, info.mZ, static_cast<double>(info.mX), diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index fff33056e1..288bf92c15 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -167,6 +167,9 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeInt32, region.right); SAFE_PARCEL(output.writeInt32, region.bottom); } + + SAFE_PARCEL(output.write, stretchEffect); + return NO_ERROR; } @@ -290,6 +293,9 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readInt32, ®ion.bottom); blurRegions.push_back(region); } + + SAFE_PARCEL(input.read, stretchEffect); + return NO_ERROR; } @@ -542,6 +548,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eAutoRefreshChanged; autoRefresh = other.autoRefresh; } + if (other.what & eStretchChanged) { + what |= eStretchChanged; + stretchEffect = other.stretchEffect; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIu64 " what=0x%" PRIu64, diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 07fc0694d6..6de3e971b2 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -34,9 +34,9 @@ #include <utils/NativeHandle.h> #include <ui/DisplayStatInfo.h> +#include <ui/DynamicDisplayInfo.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> -#include <ui/HdrCapabilities.h> #include <ui/Region.h> #include <gui/BufferItem.h> @@ -48,7 +48,6 @@ namespace android { -using ui::ColorMode; using ui::Dataspace; namespace { @@ -361,15 +360,12 @@ status_t Surface::getHdrSupport(bool* supported) { return NAME_NOT_FOUND; } - HdrCapabilities hdrCapabilities; - status_t err = - composerService()->getHdrCapabilities(display, &hdrCapabilities); - - if (err) + ui::DynamicDisplayInfo info; + if (status_t err = composerService()->getDynamicDisplayInfo(display, &info); err != NO_ERROR) { return err; + } - *supported = !hdrCapabilities.getSupportedHdrTypes().empty(); - + *supported = !info.hdrCapabilities.getSupportedHdrTypes().empty(); return NO_ERROR; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 4493a21fc3..0317b2bd2b 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -40,6 +40,7 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <ui/DisplayMode.h> +#include <ui/DynamicDisplayInfo.h> #ifndef NO_INPUT #include <input/InputWindow.h> @@ -194,6 +195,25 @@ void TransactionCompletedListener::removeJankListener(const sp<JankDataListener> } } +void TransactionCompletedListener::addSurfaceStatsListener(void* context, void* cookie, + sp<SurfaceControl> surfaceControl, SurfaceStatsCallback listener) { + std::lock_guard<std::mutex> lock(mMutex); + mSurfaceStatsListeners.insert({surfaceControl->getHandle(), + SurfaceStatsCallbackEntry(context, cookie, listener)}); +} + +void TransactionCompletedListener::removeSurfaceStatsListener(void* context, void* cookie) { + std::lock_guard<std::mutex> lock(mMutex); + for (auto it = mSurfaceStatsListeners.begin(); it != mSurfaceStatsListeners.end();) { + auto [itContext, itCookie, itListener] = it->second; + if (itContext == context && itCookie == cookie) { + it = mSurfaceStatsListeners.erase(it); + } else { + it++; + } + } +} + void TransactionCompletedListener::addSurfaceControlToCallbacks( const sp<SurfaceControl>& surfaceControl, const std::unordered_set<CallbackId>& callbackIds) { @@ -210,6 +230,7 @@ void TransactionCompletedListener::addSurfaceControlToCallbacks( void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { std::unordered_map<CallbackId, CallbackTranslation> callbacksMap; std::multimap<sp<IBinder>, sp<JankDataListener>> jankListenersMap; + std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> surfaceListeners; { std::lock_guard<std::mutex> lock(mMutex); @@ -226,6 +247,7 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener */ callbacksMap = mCallbacks; jankListenersMap = mJankListeners; + surfaceListeners = mSurfaceStatsListeners; for (const auto& transactionStats : listenerStats.transactionStats) { for (auto& callbackId : transactionStats.callbackIds) { mCallbacks.erase(callbackId); @@ -259,9 +281,16 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener surfaceControlStats); } for (const auto& surfaceStats : transactionStats.surfaceStats) { + auto listenerRange = surfaceListeners.equal_range(surfaceStats.surfaceControl); + for (auto it = listenerRange.first; it != listenerRange.second; it++) { + auto entry = it->second; + entry.callback(entry.context, transactionStats.latchTime, + transactionStats.presentFence, surfaceStats); + } + if (surfaceStats.jankData.empty()) continue; - for (auto it = jankListenersMap.find(surfaceStats.surfaceControl); - it != jankListenersMap.end(); it++) { + auto jankRange = jankListenersMap.equal_range(surfaceStats.surfaceControl); + for (auto it = jankRange.first; it != jankRange.second; it++) { it->second->onJankDataAvailable(surfaceStats.jankData); } } @@ -1569,6 +1598,23 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApply return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setStretchEffect( + const sp<SurfaceControl>& sc, float left, float top, float right, float bottom, float vecX, + float vecY, float maxAmount) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eStretchChanged; + s->stretchEffect = StretchEffect{.area = {left, top, right, bottom}, + .vectorX = vecX, + .vectorY = vecY, + .maxAmount = maxAmount}; + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -1798,39 +1844,35 @@ status_t SurfaceComposerClient::getDisplayState(const sp<IBinder>& display, return ComposerService::getComposerService()->getDisplayState(display, state); } -status_t SurfaceComposerClient::getDisplayInfo(const sp<IBinder>& display, DisplayInfo* info) { - return ComposerService::getComposerService()->getDisplayInfo(display, info); +status_t SurfaceComposerClient::getStaticDisplayInfo(const sp<IBinder>& display, + ui::StaticDisplayInfo* info) { + return ComposerService::getComposerService()->getStaticDisplayInfo(display, info); } -status_t SurfaceComposerClient::getDisplayModes(const sp<IBinder>& display, - Vector<ui::DisplayMode>* modes) { - return ComposerService::getComposerService()->getDisplayModes(display, modes); +status_t SurfaceComposerClient::getDynamicDisplayInfo(const sp<IBinder>& display, + ui::DynamicDisplayInfo* info) { + return ComposerService::getComposerService()->getDynamicDisplayInfo(display, info); } status_t SurfaceComposerClient::getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode* mode) { - Vector<ui::DisplayMode> modes; - status_t result = getDisplayModes(display, &modes); + ui::DynamicDisplayInfo info; + status_t result = getDynamicDisplayInfo(display, &info); if (result != NO_ERROR) { return result; } - int activeId = getActiveDisplayModeId(display); - if (activeId < 0) { - ALOGE("No active mode found"); - return NAME_NOT_FOUND; + if (const auto activeMode = info.getActiveDisplayMode()) { + *mode = *activeMode; + return NO_ERROR; } - *mode = modes[static_cast<size_t>(activeId)]; - return NO_ERROR; -} - -int SurfaceComposerClient::getActiveDisplayModeId(const sp<IBinder>& display) { - return ComposerService::getComposerService()->getActiveDisplayModeId(display); + ALOGE("Active display mode not found."); + return NAME_NOT_FOUND; } status_t SurfaceComposerClient::setDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, size_t defaultMode, bool allowGroupSwitching, + const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, float appRequestRefreshRateMax) { return ComposerService::getComposerService() @@ -1839,51 +1881,33 @@ status_t SurfaceComposerClient::setDesiredDisplayModeSpecs( appRequestRefreshRateMin, appRequestRefreshRateMax); } -status_t SurfaceComposerClient::getDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, size_t* outDefaultMode, bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) { +status_t SurfaceComposerClient::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + ui::DisplayModeId* outDefaultMode, + bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, + float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, + float* outAppRequestRefreshRateMax) { return ComposerService::getComposerService() ->getDesiredDisplayModeSpecs(displayToken, outDefaultMode, outAllowGroupSwitching, outPrimaryRefreshRateMin, outPrimaryRefreshRateMax, outAppRequestRefreshRateMin, outAppRequestRefreshRateMax); } -status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display, - Vector<ColorMode>* outColorModes) { - return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes); -} - status_t SurfaceComposerClient::getDisplayNativePrimaries(const sp<IBinder>& display, ui::DisplayPrimaries& outPrimaries) { return ComposerService::getComposerService()->getDisplayNativePrimaries(display, outPrimaries); } -ColorMode SurfaceComposerClient::getActiveColorMode(const sp<IBinder>& display) { - return ComposerService::getComposerService()->getActiveColorMode(display); -} - status_t SurfaceComposerClient::setActiveColorMode(const sp<IBinder>& display, ColorMode colorMode) { return ComposerService::getComposerService()->setActiveColorMode(display, colorMode); } -bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) { - bool supported = false; - ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported); - return supported; -} - void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) { ComposerService::getComposerService()->setAutoLowLatencyMode(display, on); } -bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) { - bool supported = false; - ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported); - return supported; -} - void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) { ComposerService::getComposerService()->setGameContentType(display, on); } @@ -1915,12 +1939,6 @@ status_t SurfaceComposerClient::getAnimationFrameStats(FrameStats* outStats) { return ComposerService::getComposerService()->getAnimationFrameStats(outStats); } -status_t SurfaceComposerClient::getHdrCapabilities(const sp<IBinder>& display, - HdrCapabilities* outCapabilities) { - return ComposerService::getComposerService()->getHdrCapabilities(display, - outCapabilities); -} - status_t SurfaceComposerClient::getDisplayedContentSamplingAttributes(const sp<IBinder>& display, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index e842382ded..1dcfe2e804 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -30,9 +30,9 @@ #include <binder/IPCThreadState.h> -#include <ui/DisplayInfo.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> +#include <ui/StaticDisplayInfo.h> #include <gui/BufferQueueCore.h> #include <gui/ISurfaceComposer.h> diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index bccb71b362..dd8e714e23 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -32,6 +32,7 @@ namespace android { +class BLASTBufferQueue; class BufferItemConsumer; class BLASTBufferItemConsumer : public BufferItemConsumer { @@ -40,27 +41,32 @@ public: int bufferCount, bool controlledByApp) : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp), mCurrentlyConnected(false), - mPreviouslyConnected(false) {} + mPreviouslyConnected(false), + mBLASTBufferQueue(nullptr) {} void onDisconnect() override; void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, - FrameEventHistoryDelta* outDelta) override - REQUIRES(mFrameEventHistoryMutex); + FrameEventHistoryDelta* outDelta) override REQUIRES(mMutex); void updateFrameTimestamps(uint64_t frameNumber, nsecs_t refreshStartTime, const sp<Fence>& gpuCompositionDoneFence, const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence, CompositorTiming compositorTiming, nsecs_t latchTime, - nsecs_t dequeueReadyTime) REQUIRES(mFrameEventHistoryMutex); + nsecs_t dequeueReadyTime) REQUIRES(mMutex); void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect); + void setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) REQUIRES(mMutex); + +protected: + void onSidebandStreamChanged() override REQUIRES(mMutex); private: uint64_t mCurrentFrameNumber = 0; - Mutex mFrameEventHistoryMutex; - ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mFrameEventHistoryMutex); - std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mFrameEventHistoryMutex); - bool mCurrentlyConnected GUARDED_BY(mFrameEventHistoryMutex); - bool mPreviouslyConnected GUARDED_BY(mFrameEventHistoryMutex); + Mutex mMutex; + ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mMutex); + std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mMutex); + bool mCurrentlyConnected GUARDED_BY(mMutex); + bool mPreviouslyConnected GUARDED_BY(mMutex); + BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mMutex); }; class BLASTBufferQueue @@ -94,6 +100,8 @@ public: status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); status_t setFrameTimelineInfo(const FrameTimelineInfo& info); + void setSidebandStream(const sp<NativeHandle>& stream); + virtual ~BLASTBufferQueue(); private: diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 292838e9ae..6289e5a5d0 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -33,6 +33,7 @@ #include <ui/ConfigStoreTypes.h> #include <ui/DisplayId.h> +#include <ui/DisplayMode.h> #include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> @@ -54,7 +55,6 @@ namespace android { struct client_cache_t; struct ComposerState; struct DisplayCaptureArgs; -struct DisplayInfo; struct DisplayStatInfo; struct DisplayState; struct InputWindowCommands; @@ -74,6 +74,8 @@ namespace ui { struct DisplayMode; struct DisplayState; +struct DynamicDisplayInfo; +struct StaticDisplayInfo; } // namespace ui @@ -202,56 +204,32 @@ public: virtual status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*) = 0; /** - * Get immutable information about given physical display. + * Gets immutable information about given physical display. */ - virtual status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*) = 0; + virtual status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*) = 0; /** - * Get modes supported by given physical display. + * Gets dynamic information about given physical display. */ - virtual status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>*) = 0; + virtual status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*) = 0; - /** - * Get the index into modes returned by getDisplayModes, - * corresponding to the active mode. - */ - virtual int getActiveDisplayModeId(const sp<IBinder>& display) = 0; - - virtual status_t getDisplayColorModes(const sp<IBinder>& display, - Vector<ui::ColorMode>* outColorModes) = 0; virtual status_t getDisplayNativePrimaries(const sp<IBinder>& display, ui::DisplayPrimaries& primaries) = 0; - virtual ui::ColorMode getActiveColorMode(const sp<IBinder>& display) = 0; virtual status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode) = 0; /** - * Returns true if the connected display reports support for HDMI 2.1 Auto - * Low Latency Mode. - * For more information, see the HDMI 2.1 specification. - */ - virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display, - bool* outSupport) const = 0; - - /** * Switches Auto Low Latency Mode on/off on the connected display, if it is - * available. This should only be called if #getAutoLowLatencyMode returns - * true. + * available. This should only be called if the display supports Auto Low + * Latency Mode as reported in #getDynamicDisplayInfo. * For more information, see the HDMI 2.1 specification. */ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0; /** - * Returns true if the connected display reports support for Game Content Type. - * For more information, see the HDMI 1.4 specification. - */ - virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, - bool* outSupport) const = 0; - - /** * This will start sending infoframes to the connected display with - * ContentType=Game (if on=true). This will switch the disply to Game mode. - * This should only be called if #getGameContentTypeSupport returns true. + * ContentType=Game (if on=true). This should only be called if the display + * Game Content Type as reported in #getDynamicDisplayInfo. * For more information, see the HDMI 1.4 specification. */ virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0; @@ -296,13 +274,6 @@ public: */ virtual status_t getAnimationFrameStats(FrameStats* outStats) const = 0; - /* Gets the supported HDR capabilities of the given display. - * - * Requires the ACCESS_SURFACE_FLINGER permission. - */ - virtual status_t getHdrCapabilities(const sp<IBinder>& display, - HdrCapabilities* outCapabilities) const = 0; - virtual status_t enableVSyncInjections(bool enable) = 0; virtual status_t injectVSync(nsecs_t when) = 0; @@ -397,20 +368,21 @@ public: * * defaultMode is used to narrow the list of display modes SurfaceFlinger will consider * switching between. Only modes with a mode group and resolution matching defaultMode - * will be considered for switching. The defaultMode index corresponds to the list of modes - * returned from getDisplayModes(). - */ - virtual status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode, - bool allowGroupSwitching, - float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) = 0; - - virtual status_t getDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, size_t* outDefaultMode, bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) = 0; + * will be considered for switching. The defaultMode corresponds to an ID of mode in the list + * of supported modes returned from getDynamicDisplayInfo(). + */ + virtual status_t setDesiredDisplayModeSpecs( + const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, + bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, + float appRequestRefreshRateMin, float appRequestRefreshRateMax) = 0; + + virtual status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + ui::DisplayModeId* outDefaultMode, + bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, + float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, + float* outAppRequestRefreshRateMax) = 0; /* * Gets whether brightness operations are supported on a display. * @@ -534,7 +506,7 @@ public: // Java by ActivityManagerService. BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION, CREATE_CONNECTION, - GET_DISPLAY_INFO, + GET_STATIC_DISPLAY_INFO, CREATE_DISPLAY_EVENT_CONNECTION, CREATE_DISPLAY, DESTROY_DISPLAY, @@ -542,8 +514,8 @@ public: SET_TRANSACTION_STATE, AUTHENTICATE_SURFACE, GET_SUPPORTED_FRAME_TIMESTAMPS, - GET_DISPLAY_MODES, - GET_ACTIVE_DISPLAY_MODE, + GET_DISPLAY_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. + GET_ACTIVE_DISPLAY_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. GET_DISPLAY_STATE, CAPTURE_DISPLAY, CAPTURE_LAYERS, @@ -551,9 +523,9 @@ public: GET_ANIMATION_FRAME_STATS, SET_POWER_MODE, GET_DISPLAY_STATS, - GET_HDR_CAPABILITIES, - GET_DISPLAY_COLOR_MODES, - GET_ACTIVE_COLOR_MODE, + GET_HDR_CAPABILITIES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. + GET_DISPLAY_COLOR_MODES, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. + GET_ACTIVE_COLOR_MODE, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. SET_ACTIVE_COLOR_MODE, ENABLE_VSYNC_INJECTIONS, INJECT_VSYNC, @@ -576,9 +548,9 @@ public: CAPTURE_DISPLAY_BY_ID, NOTIFY_POWER_BOOST, SET_GLOBAL_SHADOW_SETTINGS, - GET_AUTO_LOW_LATENCY_MODE_SUPPORT, + GET_AUTO_LOW_LATENCY_MODE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. SET_AUTO_LOW_LATENCY_MODE, - GET_GAME_CONTENT_TYPE_SUPPORT, + GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead. SET_GAME_CONTENT_TYPE, SET_FRAME_RATE, ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, @@ -586,6 +558,7 @@ public: ADD_TRANSACTION_TRACE_LISTENER, GET_GPU_CONTEXT_PRIORITY, GET_EXTRA_BUFFER_COUNT, + GET_DYNAMIC_DISPLAY_INFO, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h index 66a7b4dc06..8b7d32c0a5 100644 --- a/libs/gui/include/gui/LayerDebugInfo.h +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -20,6 +20,7 @@ #include <ui/PixelFormat.h> #include <ui/Region.h> +#include <ui/StretchEffect.h> #include <string> #include <math/vec4.h> @@ -66,6 +67,7 @@ public: bool mRefreshPending = false; bool mIsOpaque = false; bool mContentDirty = false; + StretchEffect mStretchEffect = {}; }; std::string to_string(const LayerDebugInfo& info); diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 2f9a0c01eb..b273805e25 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -56,6 +56,7 @@ #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Rotation.h> +#include <ui/StretchEffect.h> #include <ui/Transform.h> #include <utils/Errors.h> @@ -135,6 +136,7 @@ struct layer_state_t { eFrameTimelineInfoChanged = 0x800'00000000, eBlurRegionsChanged = 0x1000'00000000, eAutoRefreshChanged = 0x2000'00000000, + eStretchChanged = 0x4000'00000000, }; layer_state_t(); @@ -244,6 +246,9 @@ struct layer_state_t { // can and not wait for a frame to become available. This is only relevant // in shared buffer mode. bool autoRefresh; + + // Stretch effect to be applied to this layer + StretchEffect stretchEffect; }; struct ComposerState { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index bd9f066f96..64c8af50bd 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -80,6 +80,10 @@ using TransactionCompletedCallbackTakesContext = using TransactionCompletedCallback = std::function<void(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/, const std::vector<SurfaceControlStats>& /*stats*/)>; +using SurfaceStatsCallback = + std::function<void(void* /*context*/, nsecs_t /*latchTime*/, + const sp<Fence>& /*presentFence*/, + const SurfaceStats& /*stats*/)>; // --------------------------------------------------------------------------- @@ -108,58 +112,42 @@ public: static status_t getDisplayState(const sp<IBinder>& display, ui::DisplayState*); // Get immutable information about given physical display. - static status_t getDisplayInfo(const sp<IBinder>& display, DisplayInfo*); + static status_t getStaticDisplayInfo(const sp<IBinder>& display, ui::StaticDisplayInfo*); - // Get modes supported by given physical display. - static status_t getDisplayModes(const sp<IBinder>& display, Vector<ui::DisplayMode>*); + // Get dynamic information about given physical display. + static status_t getDynamicDisplayInfo(const sp<IBinder>& display, ui::DynamicDisplayInfo*); - // Get the ID of the active DisplayMode, as getDisplayModes index. - static int getActiveDisplayModeId(const sp<IBinder>& display); - - // Shorthand for getDisplayModes element at getActiveDisplayModeId index. + // Shorthand for the active display mode from getDynamicDisplayInfo(). + // TODO(b/180391891): Update clients to use getDynamicDisplayInfo and remove this function. static status_t getActiveDisplayMode(const sp<IBinder>& display, ui::DisplayMode*); // Sets the refresh rate boundaries for the display. - static status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t defaultMode, - bool allowGroupSwitching, - float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax); + static status_t setDesiredDisplayModeSpecs( + const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, + bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, + float appRequestRefreshRateMin, float appRequestRefreshRateMax); // Gets the refresh rate boundaries for the display. static status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - size_t* outDefaultMode, bool* outAllowGroupSwitching, + ui::DisplayModeId* outDefaultMode, + bool* outAllowGroupSwitching, float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax); - // Gets the list of supported color modes for the given display - static status_t getDisplayColorModes(const sp<IBinder>& display, - Vector<ui::ColorMode>* outColorModes); - // Get the coordinates of the display's native color primaries static status_t getDisplayNativePrimaries(const sp<IBinder>& display, ui::DisplayPrimaries& outPrimaries); - // Gets the active color mode for the given display - static ui::ColorMode getActiveColorMode(const sp<IBinder>& display); - // Sets the active color mode for the given display static status_t setActiveColorMode(const sp<IBinder>& display, ui::ColorMode colorMode); - // Reports whether the connected display supports Auto Low Latency Mode - static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display); - // Switches on/off Auto Low Latency Mode on the connected display. This should only be // called if the connected display supports Auto Low Latency Mode as reported by // #getAutoLowLatencyModeSupport static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on); - // Reports whether the connected display supports Game content type - static bool getGameContentTypeSupport(const sp<IBinder>& display); - // Turns Game mode on/off on the connected display. This should only be called // if the display supports Game content type, as reported by #getGameContentTypeSupport static void setGameContentType(const sp<IBinder>& display, bool on); @@ -550,6 +538,10 @@ public: // transactions from blocking each other. Transaction& setApplyToken(const sp<IBinder>& token); + Transaction& setStretchEffect(const sp<SurfaceControl>& sc, float left, float top, + float right, float bottom, float vecX, float vecY, + float maxAmount); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -579,9 +571,6 @@ public: static status_t clearAnimationFrameStats(); static status_t getAnimationFrameStats(FrameStats* outStats); - static status_t getHdrCapabilities(const sp<IBinder>& display, - HdrCapabilities* outCapabilities); - static void setDisplayProjection(const sp<IBinder>& token, ui::Rotation orientation, const Rect& layerStackRect, const Rect& displayRect); @@ -646,8 +635,21 @@ class TransactionCompletedListener : public BnTransactionCompletedListener { surfaceControls; }; + struct SurfaceStatsCallbackEntry { + SurfaceStatsCallbackEntry(void* context, void* cookie, SurfaceStatsCallback callback) + : context(context), + cookie(cookie), + callback(callback) {} + + void* context; + void* cookie; + SurfaceStatsCallback callback; + }; + std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex); std::multimap<sp<IBinder>, sp<JankDataListener>> mJankListeners GUARDED_BY(mMutex); + std::multimap<sp<IBinder>, SurfaceStatsCallbackEntry> + mSurfaceStatsListeners GUARDED_BY(mMutex); public: static sp<TransactionCompletedListener> getInstance(); @@ -675,6 +677,10 @@ public: */ void removeJankListener(const sp<JankDataListener>& listener); + void addSurfaceStatsListener(void* context, void* cookie, sp<SurfaceControl> surfaceControl, + SurfaceStatsCallback listener); + void removeSurfaceStatsListener(void* context, void* cookie); + // Overrides BnTransactionCompletedListener's onTransactionCompleted void onTransactionCompleted(ListenerStats stats) override; }; diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp index 64b1eacaa2..bddb0ac5ee 100644 --- a/libs/gui/sysprop/Android.bp +++ b/libs/gui/sysprop/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + sysprop_library { name: "LibGuiProperties", srcs: ["*.sysprop"], diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 53c13c8859..c801c6243a 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -2,6 +2,15 @@ // Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) // to integrate with auto-test framework. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "libgui_test", test_suites: ["device-tests"], diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 5447b7659b..fb07a198ec 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -129,7 +129,11 @@ protected: } void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) { - auto igbProducer = adapter.getIGraphicBufferProducer(); + producer = adapter.getIGraphicBufferProducer(); + setUpProducer(producer); + } + + void setUpProducer(sp<IGraphicBufferProducer>& igbProducer) { ASSERT_NE(nullptr, igbProducer.get()); ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2)); IGraphicBufferProducer::QueueBufferOutput qbOutput; @@ -137,7 +141,6 @@ protected: igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); - producer = igbProducer; } void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g, @@ -555,6 +558,8 @@ TEST_F(BLASTBufferQueueTest, QueryNativeWindowQueuesToWindowComposer) { ASSERT_EQ(queuesToNativeWindow, 1); } +// Test a slow producer doesn't hold up a faster producer from the same client. Essentially tests +// BBQ uses separate transaction queues. TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) { sp<SurfaceControl> bgSurface = mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888, @@ -781,7 +786,7 @@ public: void setUpAndQueueBuffer(const sp<IGraphicBufferProducer>& igbProducer, nsecs_t* requestedPresentTime, nsecs_t* postedTime, IGraphicBufferProducer::QueueBufferOutput* qbOutput, - bool getFrameTimestamps) { + bool getFrameTimestamps, nsecs_t requestedPresentTimeDelay = 0) { int slot; sp<Fence> fence; sp<GraphicBuffer> buf; @@ -791,7 +796,7 @@ public: ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); - nsecs_t requestedTime = systemTime(); + nsecs_t requestedTime = systemTime() + requestedPresentTimeDelay; if (requestedPresentTime) *requestedPresentTime = requestedTime; IGraphicBufferProducer::QueueBufferInput input(requestedTime, false, HAL_DATASPACE_UNKNOWN, Rect(mDisplayWidth, mDisplayHeight), @@ -801,6 +806,27 @@ public: if (postedTime) *postedTime = systemTime(); igbProducer->queueBuffer(slot, input, qbOutput); } + + void createBufferQueueProducer(sp<IGraphicBufferProducer>* bqIgbp) { + mBufferQueueSurfaceControl = + mClient->createSurface(String8("BqSurface"), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferQueue); + ASSERT_NE(nullptr, mBufferQueueSurfaceControl.get()); + Transaction() + .setLayerStack(mBufferQueueSurfaceControl, 0) + .show(mBufferQueueSurfaceControl) + .setDataspace(mBufferQueueSurfaceControl, ui::Dataspace::V0_SRGB) + .setSize(mBufferQueueSurfaceControl, mDisplayWidth, mDisplayHeight) + .setLayer(mBufferQueueSurfaceControl, std::numeric_limits<int32_t>::max()) + .apply(); + + sp<Surface> bqSurface = mBufferQueueSurfaceControl->getSurface(); + ASSERT_NE(nullptr, bqSurface.get()); + + *bqIgbp = bqSurface->getIGraphicBufferProducer(); + setUpProducer(*bqIgbp); + } + sp<SurfaceControl> mBufferQueueSurfaceControl; }; TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { @@ -853,4 +879,153 @@ TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic) { // wait for any callbacks that have not been received adapter.waitForCallbacks(); } + +// Runs the same Frame Event History test +TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_Basic_BufferQueue) { + sp<IGraphicBufferProducer> bqIgbp; + createBufferQueueProducer(&bqIgbp); + + ProducerFrameEventHistory history; + IGraphicBufferProducer::QueueBufferOutput qbOutput; + nsecs_t requestedPresentTimeA = 0; + nsecs_t postedTimeA = 0; + setUpAndQueueBuffer(bqIgbp, &requestedPresentTimeA, &postedTimeA, &qbOutput, true); + history.applyDelta(qbOutput.frameTimestamps); + + FrameEvents* events = nullptr; + events = history.getFrame(1); + ASSERT_NE(nullptr, events); + ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeA); + + // wait for buffer to be presented + std::this_thread::sleep_for(200ms); + + nsecs_t requestedPresentTimeB = 0; + nsecs_t postedTimeB = 0; + setUpAndQueueBuffer(bqIgbp, &requestedPresentTimeB, &postedTimeB, &qbOutput, true); + history.applyDelta(qbOutput.frameTimestamps); + events = history.getFrame(1); + ASSERT_NE(nullptr, events); + + // frame number, requestedPresentTime, and postTime should not have changed + ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeA); + + ASSERT_GE(events->latchTime, postedTimeA); + ASSERT_FALSE(events->hasDequeueReadyInfo()); + + ASSERT_NE(nullptr, events->gpuCompositionDoneFence); + ASSERT_NE(nullptr, events->displayPresentFence); + ASSERT_NE(nullptr, events->releaseFence); + + // we should also have gotten the initial values for the next frame + events = history.getFrame(2); + ASSERT_NE(nullptr, events); + ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeB); +} + +TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + ProducerFrameEventHistory history; + IGraphicBufferProducer::QueueBufferOutput qbOutput; + nsecs_t requestedPresentTimeA = 0; + nsecs_t postedTimeA = 0; + nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count(); + setUpAndQueueBuffer(igbProducer, &requestedPresentTimeA, &postedTimeA, &qbOutput, true, + presentTimeDelay); + history.applyDelta(qbOutput.frameTimestamps); + + FrameEvents* events = nullptr; + events = history.getFrame(1); + ASSERT_NE(nullptr, events); + ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeA); + + // queue another buffer so the first can be dropped + nsecs_t requestedPresentTimeB = 0; + nsecs_t postedTimeB = 0; + setUpAndQueueBuffer(igbProducer, &requestedPresentTimeB, &postedTimeB, &qbOutput, true); + history.applyDelta(qbOutput.frameTimestamps); + events = history.getFrame(1); + ASSERT_NE(nullptr, events); + + // frame number, requestedPresentTime, and postTime should not have changed + ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeA); + + // a valid latchtime should not be set + ASSERT_FALSE(events->hasLatchInfo()); + ASSERT_FALSE(events->hasDequeueReadyInfo()); + + ASSERT_NE(nullptr, events->gpuCompositionDoneFence); + ASSERT_NE(nullptr, events->displayPresentFence); + ASSERT_NE(nullptr, events->releaseFence); + + // we should also have gotten the initial values for the next frame + events = history.getFrame(2); + ASSERT_NE(nullptr, events); + ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeB); +} + +TEST_F(BLASTFrameEventHistoryTest, FrameEventHistory_DroppedFrame_BufferQueue) { + sp<IGraphicBufferProducer> bqIgbp; + createBufferQueueProducer(&bqIgbp); + + ProducerFrameEventHistory history; + IGraphicBufferProducer::QueueBufferOutput qbOutput; + nsecs_t requestedPresentTimeA = 0; + nsecs_t postedTimeA = 0; + nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count(); + setUpAndQueueBuffer(bqIgbp, &requestedPresentTimeA, &postedTimeA, &qbOutput, true, + presentTimeDelay); + history.applyDelta(qbOutput.frameTimestamps); + + FrameEvents* events = nullptr; + events = history.getFrame(1); + ASSERT_NE(nullptr, events); + ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeA); + + // queue another buffer so the first can be dropped + nsecs_t requestedPresentTimeB = 0; + nsecs_t postedTimeB = 0; + setUpAndQueueBuffer(bqIgbp, &requestedPresentTimeB, &postedTimeB, &qbOutput, true); + history.applyDelta(qbOutput.frameTimestamps); + events = history.getFrame(1); + ASSERT_NE(nullptr, events); + + // frame number, requestedPresentTime, and postTime should not have changed + ASSERT_EQ(1, events->frameNumber); + ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeA); + + // a valid latchtime should not be set + ASSERT_FALSE(events->hasLatchInfo()); + ASSERT_FALSE(events->hasDequeueReadyInfo()); + + ASSERT_NE(nullptr, events->gpuCompositionDoneFence); + ASSERT_NE(nullptr, events->displayPresentFence); + ASSERT_NE(nullptr, events->releaseFence); + + // we should also have gotten the initial values for the next frame + events = history.getFrame(2); + ASSERT_NE(nullptr, events); + ASSERT_EQ(2, events->frameNumber); + ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime); + ASSERT_GE(events->postedTime, postedTimeB); +} + } // namespace android diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 43909ac00d..69bb86649c 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -32,6 +32,7 @@ #include <inttypes.h> #include <private/gui/ComposerService.h> #include <ui/BufferQueueDefs.h> +#include <ui/DisplayMode.h> #include <ui/Rect.h> #include <utils/String8.h> @@ -734,10 +735,11 @@ public: } void setPowerMode(const sp<IBinder>& /*display*/, int /*mode*/) override {} - status_t getDisplayInfo(const sp<IBinder>& /*display*/, DisplayInfo*) override { + status_t getStaticDisplayInfo(const sp<IBinder>& /*display*/, ui::StaticDisplayInfo*) override { return NO_ERROR; } - status_t getDisplayModes(const sp<IBinder>& /*display*/, Vector<ui::DisplayMode>*) override { + status_t getDynamicDisplayInfo(const sp<IBinder>& /*display*/, + ui::DynamicDisplayInfo*) override { return NO_ERROR; } status_t getDisplayState(const sp<IBinder>& /*display*/, ui::DisplayState*) override { @@ -745,34 +747,17 @@ public: } status_t getDisplayStats(const sp<IBinder>& /*display*/, DisplayStatInfo* /*stats*/) override { return NO_ERROR; } - int getActiveDisplayModeId(const sp<IBinder>& /*display*/) override { return 0; } - status_t getDisplayColorModes(const sp<IBinder>& /*display*/, - Vector<ColorMode>* /*outColorModes*/) override { - return NO_ERROR; - } status_t getDisplayNativePrimaries(const sp<IBinder>& /*display*/, ui::DisplayPrimaries& /*primaries*/) override { return NO_ERROR; } - ColorMode getActiveColorMode(const sp<IBinder>& /*display*/) - override { - return ColorMode::NATIVE; - } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */, const sp<IScreenCaptureListener>& /* captureListener */) override { return NO_ERROR; } - status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/, - bool* /*outSupport*/) const override { - return NO_ERROR; - } void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {} - status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/, - bool* /*outSupport*/) const override { - return NO_ERROR; - } void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {} status_t captureDisplay(uint64_t /*displayOrLayerStack*/, const sp<IScreenCaptureListener>& /* captureListener */) override { @@ -787,10 +772,6 @@ public: status_t getAnimationFrameStats(FrameStats* /*outStats*/) const override { return NO_ERROR; } - status_t getHdrCapabilities(const sp<IBinder>& /*display*/, - HdrCapabilities* /*outCapabilities*/) const override { - return NO_ERROR; - } status_t enableVSyncInjections(bool /*enable*/) override { return NO_ERROR; } @@ -843,7 +824,8 @@ public: const sp<IRegionSamplingListener>& /*listener*/) override { return NO_ERROR; } - status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, size_t /*defaultMode*/, + status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, + ui::DisplayModeId /*defaultMode*/, bool /*allowGroupSwitching*/, float /*primaryRefreshRateMin*/, float /*primaryRefreshRateMax*/, @@ -852,7 +834,7 @@ public: return NO_ERROR; } status_t getDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/, - size_t* /*outDefaultMode*/, + ui::DisplayModeId* /*outDefaultMode*/, bool* /*outAllowGroupSwitching*/, float* /*outPrimaryRefreshRateMin*/, float* /*outPrimaryRefreshRateMax*/, diff --git a/libs/incidentcompanion/Android.bp b/libs/incidentcompanion/Android.bp index 63411b9698..ef7f52318b 100644 --- a/libs/incidentcompanion/Android.bp +++ b/libs/incidentcompanion/Android.bp @@ -14,6 +14,15 @@ * limitations under the License. */ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + filegroup { name: "incidentcompanion_aidl", srcs: [ @@ -49,4 +58,3 @@ cc_library_static { "-Wunused-parameter", ], } - diff --git a/libs/input/Android.bp b/libs/input/Android.bp index fce3000a0f..425361004c 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -14,6 +14,15 @@ // libinput is partially built for the host (used by build time keymap validation tool) +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + filegroup { name: "inputconstants_aidl", srcs: [ diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 698cf6eebc..ffcc1cd93a 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -170,7 +170,8 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mHasButtonUnderPad(other.mHasButtonUnderPad), mHasSensor(other.mHasSensor), mMotionRanges(other.mMotionRanges), - mSensors(other.mSensors) {} + mSensors(other.mSensors), + mLights(other.mLights) {} InputDeviceInfo::~InputDeviceInfo() { } @@ -193,6 +194,7 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mHasSensor = false; mMotionRanges.clear(); mSensors.clear(); + mLights.clear(); } const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( @@ -229,6 +231,13 @@ void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) { mSensors.insert_or_assign(info.type, info); } +void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) { + if (mLights.find(info.id) != mLights.end()) { + ALOGW("Light id %d already exists, will be replaced by new light added.", info.id); + } + mLights.insert_or_assign(info.id, info); +} + const std::vector<InputDeviceSensorType> InputDeviceInfo::getSensorTypes() { std::vector<InputDeviceSensorType> types; for (const auto& [type, info] : mSensors) { @@ -245,4 +254,20 @@ const InputDeviceSensorInfo* InputDeviceInfo::getSensorInfo(InputDeviceSensorTyp return &it->second; } +const std::vector<int32_t> InputDeviceInfo::getLightIds() { + std::vector<int32_t> ids; + for (const auto& [id, info] : mLights) { + ids.push_back(id); + } + return ids; +} + +const InputDeviceLightInfo* InputDeviceInfo::getLightInfo(int32_t id) { + auto it = mLights.find(id); + if (it == mLights.end()) { + return nullptr; + } + return &it->second; +} + } // namespace android diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index acea473242..6218fdcac1 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -234,6 +234,7 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { } case InputMessage::Type::FINISHED: { msg->body.finished.handled = body.finished.handled; + msg->body.finished.consumeTime = body.finished.consumeTime; break; } case InputMessage::Type::FOCUS: { @@ -597,7 +598,8 @@ status_t InputPublisher::publishCaptureEvent(uint32_t seq, int32_t eventId, return mChannel->sendMessage(&msg); } -status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { +status_t InputPublisher::receiveFinishedSignal( + const std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)>& callback) { if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str()); } @@ -605,8 +607,6 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle InputMessage msg; status_t result = mChannel->receiveMessage(&msg); if (result) { - *outSeq = 0; - *outHandled = false; return result; } if (msg.header.type != InputMessage::Type::FINISHED) { @@ -614,8 +614,7 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle mChannel->getName().c_str(), msg.header.type); return UNKNOWN_ERROR; } - *outSeq = msg.header.seq; - *outHandled = msg.body.finished.handled == 1; + callback(msg.header.seq, msg.body.finished.handled == 1, msg.body.finished.consumeTime); return OK; } @@ -651,6 +650,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum } else { // Receive a fresh message. status_t result = mChannel->receiveMessage(&mMsg); + if (result == OK) { + mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC)); + } if (result) { // Consume the next batched event unless batches are being held for later. if (consumeBatches || result != WOULD_BLOCK) { @@ -1147,12 +1149,33 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { return sendUnchainedFinishedSignal(seq, handled); } +nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const { + auto it = mConsumeTimes.find(seq); + // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was + // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed. + LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32, + seq); + return it->second; +} + +void InputConsumer::popConsumeTime(uint32_t seq) { + mConsumeTimes.erase(seq); +} + status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { InputMessage msg; msg.header.type = InputMessage::Type::FINISHED; msg.header.seq = seq; msg.body.finished.handled = handled ? 1 : 0; - return mChannel->sendMessage(&msg); + msg.body.finished.consumeTime = getConsumeTime(seq); + status_t result = mChannel->sendMessage(&msg); + if (result == OK) { + // Remove the consume time if the socket write succeeded. We will not need to ack this + // message anymore. If the socket write did not succeed, we will try again and will still + // need consume time. + popConsumeTime(seq); + } + return result; } bool InputConsumer::hasDeferredEvent() const { @@ -1304,8 +1327,9 @@ std::string InputConsumer::dump() const { break; } case InputMessage::Type::FINISHED: { - out += android::base::StringPrintf("handled=%s", - toString(msg.body.finished.handled)); + out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64, + toString(msg.body.finished.handled), + msg.body.finished.consumeTime); break; } case InputMessage::Type::FOCUS: { @@ -1335,6 +1359,14 @@ std::string InputConsumer::dump() const { if (mSeqChains.empty()) { out += " <empty>\n"; } + out += "mConsumeTimes:\n"; + for (const auto& [seq, consumeTime] : mConsumeTimes) { + out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq, + consumeTime); + } + if (mConsumeTimes.empty()) { + out += " <empty>\n"; + } return out; } diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index b23aaded78..fe8a5677d9 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -1,4 +1,13 @@ // Build the unit tests. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "libinput_tests", srcs: [ diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 9da7b69b19..e7e566dde6 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -82,6 +82,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { constexpr int32_t repeatCount = 1; constexpr nsecs_t downTime = 3; constexpr nsecs_t eventTime = 4; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, @@ -122,13 +123,22 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { uint32_t finishedSeq = 0; bool handled = false; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_TRUE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; } void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { @@ -160,6 +170,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; for (size_t i = 0; i < pointerCount; i++) { @@ -262,13 +273,22 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { uint32_t finishedSeq = 0; bool handled = true; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_FALSE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; } void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { @@ -278,6 +298,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { int32_t eventId = InputEvent::nextId(); constexpr bool hasFocus = true; constexpr bool inTouchMode = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; @@ -302,12 +323,21 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { uint32_t finishedSeq = 0; bool handled = false; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_TRUE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; } void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() { @@ -316,6 +346,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() { constexpr uint32_t seq = 42; int32_t eventId = InputEvent::nextId(); constexpr bool captureEnabled = true; + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled); ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; @@ -339,12 +370,21 @@ void InputPublisherAndConsumerTest::PublishAndConsumeCaptureEvent() { uint32_t finishedSeq = 0; bool handled = false; - status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled); + nsecs_t consumeTime; + status = mPublisher->receiveFinishedSignal( + [&finishedSeq, &handled, &consumeTime](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + finishedSeq = inSeq; + handled = inHandled; + consumeTime = inConsumeTime; + }); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; ASSERT_EQ(seq, finishedSeq) << "publisher receiveFinishedSignal should have returned the original sequence number"; ASSERT_TRUE(handled) << "publisher receiveFinishedSignal should have set handled to consumer's reply"; + ASSERT_GE(consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; } TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) { diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 4107d61eb2..a8865858d3 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -83,6 +83,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); + CHECK_OFFSET(InputMessage::Body::Finished, consumeTime, 8); } void TestHeaderSize() { @@ -100,7 +101,7 @@ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Motion) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); - static_assert(sizeof(InputMessage::Body::Finished) == 8); + static_assert(sizeof(InputMessage::Body::Finished) == 16); static_assert(sizeof(InputMessage::Body::Focus) == 8); static_assert(sizeof(InputMessage::Body::Capture) == 8); } diff --git a/libs/math/Android.bp b/libs/math/Android.bp index ab969504b1..907eb67e9a 100644 --- a/libs/math/Android.bp +++ b/libs/math/Android.bp @@ -12,6 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_libs_math_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_libs_math_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_static { name: "libmath", host_supported: true, diff --git a/libs/math/tests/Android.bp b/libs/math/tests/Android.bp index 472d3379f7..14fb72af78 100644 --- a/libs/math/tests/Android.bp +++ b/libs/math/tests/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_libs_math_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_libs_math_license"], +} + cc_test { name: "vec_test", srcs: ["vec_test.cpp"], diff --git a/libs/nativebase/Android.bp b/libs/nativebase/Android.bp index 8399e8ce0a..1a4729c610 100644 --- a/libs/nativebase/Android.bp +++ b/libs/nativebase/Android.bp @@ -12,6 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_libs_nativebase_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_libs_nativebase_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_headers { name: "libnativebase_headers", vendor_available: true, diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp index c595aa6309..6288194714 100644 --- a/libs/nativedisplay/ADisplay.cpp +++ b/libs/nativedisplay/ADisplay.cpp @@ -16,10 +16,11 @@ #include <apex/display.h> #include <gui/SurfaceComposerClient.h> -#include <ui/DisplayInfo.h> #include <ui/DisplayMode.h> +#include <ui/DynamicDisplayInfo.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> +#include <ui/StaticDisplayInfo.h> #include <algorithm> #include <optional> @@ -33,6 +34,11 @@ namespace android::display::impl { */ struct DisplayConfigImpl { /** + * The ID of the display configuration. + */ + size_t id; + + /** * The width in pixels of the display configuration. */ int32_t width{0}; @@ -139,17 +145,19 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { for (int i = 0; i < size; ++i) { const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]); - DisplayInfo info; - if (const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info); + ui::StaticDisplayInfo staticInfo; + if (const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &staticInfo); status != OK) { return status; } - Vector<ui::DisplayMode> modes; - if (const status_t status = SurfaceComposerClient::getDisplayModes(token, &modes); + ui::DynamicDisplayInfo dynamicInfo; + if (const status_t status = + SurfaceComposerClient::getDynamicDisplayInfo(token, &dynamicInfo); status != OK) { return status; } + const auto& modes = dynamicInfo.supportedDisplayModes; if (modes.empty()) { return NO_INIT; } @@ -159,9 +167,9 @@ int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) { for (int j = 0; j < modes.size(); ++j) { const ui::DisplayMode& mode = modes[j]; modesPerDisplay[i].emplace_back( - DisplayConfigImpl{mode.resolution.getWidth(), mode.resolution.getHeight(), - info.density, mode.refreshRate, mode.sfVsyncOffset, - mode.appVsyncOffset}); + DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(), + mode.resolution.getHeight(), staticInfo.density, + mode.refreshRate, mode.sfVsyncOffset, mode.appVsyncOffset}); } } @@ -257,15 +265,22 @@ int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) { CHECK_NOT_NULL(display); sp<IBinder> token = getToken(display); - const int index = SurfaceComposerClient::getActiveDisplayModeId(token); - if (index < 0) { - return index; + ui::DynamicDisplayInfo info; + if (const auto status = SurfaceComposerClient::getDynamicDisplayInfo(token, &info); + status != OK) { + return status; } DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display); + for (size_t i = 0; i < impl->numConfigs; i++) { + auto* config = impl->configs + i; + if (config->id == info.activeDisplayModeId) { + *outConfig = reinterpret_cast<ADisplayConfig*>(config); + return OK; + } + } - *outConfig = reinterpret_cast<ADisplayConfig*>(impl->configs + index); - return OK; + return NAME_NOT_FOUND; } float ADisplayConfig_getDensity(ADisplayConfig* config) { diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp index 6574ae64f7..ed728dcb45 100644 --- a/libs/nativedisplay/Android.bp +++ b/libs/nativedisplay/Android.bp @@ -12,6 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: [ + "frameworks_native_libs_nativedisplay_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_libs_nativedisplay_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_library_headers { name: "libnativedisplay_headers", export_include_dirs: ["include",], diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h index a7eaf87b9e..bd94b5523e 100644 --- a/libs/nativedisplay/include/apex/display.h +++ b/libs/nativedisplay/include/apex/display.h @@ -97,6 +97,11 @@ void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outData * such an update is observed, then this method should be recalled to get the * new current configuration. * + * After a subsequent hotplug "connected" event the supported display configs + * may change. Then the preloaded display configs will be stale and the + * call for current config may return NAME_NOT_FOUND. In this case the client + * should release and re-acquire the display handle. + * * Returns OK on success, -errno on failure. */ int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig); diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 3011dccf1e..8675439938 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -12,6 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: [ + "frameworks_native_libs_nativewindow_license", + ], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_libs_nativewindow_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + ndk_headers { name: "libnativewindow_ndk_headers", from: "include/android", diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp index 2e4bd991e0..30737c1bf6 100644 --- a/libs/nativewindow/tests/Android.bp +++ b/libs/nativewindow/tests/Android.bp @@ -14,6 +14,17 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_libs_nativewindow_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: [ + "frameworks_native_libs_nativewindow_license", + ], +} + cc_test { name: "libnativewindow_test", test_suites: [ diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index d69c7aea30..eb3b434935 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "renderengine_defaults", cflags: [ @@ -31,10 +40,6 @@ cc_defaults { "libui", "libutils", ], - include_dirs: [ - "external/skia/src/gpu", - ], - whole_static_libs: ["libskia"], local_include_dirs: ["include"], export_include_dirs: ["include"], } @@ -106,6 +111,10 @@ cc_library_static { ":librenderengine_threaded_sources", ":librenderengine_skia_sources", ], + include_dirs: [ + "external/skia/src/gpu", + ], + whole_static_libs: ["libskia"], lto: { thin: true, }, diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS index 08cf2de7f9..c4785060cb 100644 --- a/libs/renderengine/OWNERS +++ b/libs/renderengine/OWNERS @@ -1,3 +1,5 @@ alecmouri@google.com +djsollen@google.com jreck@google.com lpy@google.com +scroggo@google.com diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 64d6c6bdd7..7496b74ba5 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -72,6 +72,7 @@ public: base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; bool cleanupPostRender(CleanupMode mode) override; int getContextPriority() override; + bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; } EGLDisplay getEGLDisplay() const { return mEGLDisplay; } // Creates an output image for rendering to diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp index 19f18c0a7f..3455e08cfe 100644 --- a/libs/renderengine/gl/filters/BlurFilter.cpp +++ b/libs/renderengine/gl/filters/BlurFilter.cpp @@ -207,7 +207,7 @@ status_t BlurFilter::render(bool multiPass) { } string BlurFilter::getVertexShader() const { - return R"SHADER(#version 310 es + return R"SHADER(#version 300 es precision mediump float; in vec2 aPosition; @@ -222,7 +222,7 @@ string BlurFilter::getVertexShader() const { } string BlurFilter::getFragmentShader() const { - return R"SHADER(#version 310 es + return R"SHADER(#version 300 es precision mediump float; uniform sampler2D uTexture; @@ -244,7 +244,7 @@ string BlurFilter::getFragmentShader() const { } string BlurFilter::getMixFragShader() const { - string shader = R"SHADER(#version 310 es + string shader = R"SHADER(#version 300 es precision mediump float; in highp vec2 vUV; diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index 3a727c9caf..7661233967 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -28,6 +28,7 @@ #include <ui/GraphicTypes.h> #include <ui/Rect.h> #include <ui/Region.h> +#include <ui/StretchEffect.h> #include <ui/Transform.h> namespace android { @@ -155,6 +156,8 @@ struct LayerSettings { std::vector<BlurRegion> blurRegions; + StretchEffect stretchEffect; + // Name associated with the layer for debugging purposes. std::string name; }; diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 506f81ecc2..163a163fb2 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -188,6 +188,10 @@ public: // number of contexts that can be created at a specific priority level in the system. virtual int getContextPriority() = 0; + // Returns true if blur was requested in the RenderEngineCreationArgs and the implementation + // also supports background blur. If false, no blur will be applied when drawing layers. + virtual bool supportsBackgroundBlur() = 0; + protected: friend class threaded::RenderEngineThreaded; }; diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index 2c34da40f8..895ba3fe85 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -54,6 +54,7 @@ public: base::unique_fd*)); MOCK_METHOD0(cleanFramebufferCache, void()); MOCK_METHOD0(getContextPriority, int()); + MOCK_METHOD0(supportsBackgroundBlur, bool()); }; } // namespace mock diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index dd26b17eb4..327b04c699 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -286,6 +286,7 @@ SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGL } if (args.supportsBackgroundBlur) { + ALOGD("Background Blurs Enabled"); mBlurFilter = new BlurFilter(); } mCapture = std::make_unique<SkiaCapture>(); @@ -336,7 +337,7 @@ bool SkiaGLRenderEngine::useProtectedContext(bool useProtectedContext) { if (useProtectedContext == mInProtectedContext) { return true; } - if (useProtectedContext && supportsProtectedContent()) { + if (useProtectedContext && !supportsProtectedContent()) { return false; } const EGLSurface surface = @@ -491,6 +492,9 @@ sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> sh const LayerSettings* layer, const DisplaySettings& display, bool undoPremultipliedAlpha) { + if (layer->stretchEffect.hasEffect()) { + // TODO: Implement + } if (mUseColorManagement && needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) { LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace, @@ -513,6 +517,45 @@ sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> sh return shader; } +void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) { + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { + // Record display settings when capture is running. + std::stringstream displaySettings; + PrintTo(display, &displaySettings); + // Store the DisplaySettings in additional information. + canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings", + SkData::MakeWithCString(displaySettings.str().c_str())); + } + + // Before doing any drawing, let's make sure that we'll start at the origin of the display. + // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual + // displays might have different scaling when compared to the physical screen. + + canvas->clipRect(getSkRect(display.physicalDisplay)); + canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top); + + const auto clipWidth = display.clip.width(); + const auto clipHeight = display.clip.height(); + auto rotatedClipWidth = clipWidth; + auto rotatedClipHeight = clipHeight; + // Scale is contingent on the rotation result. + if (display.orientation & ui::Transform::ROT_90) { + std::swap(rotatedClipWidth, rotatedClipHeight); + } + const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) / + static_cast<SkScalar>(rotatedClipWidth); + const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) / + static_cast<SkScalar>(rotatedClipHeight); + canvas->scale(scaleX, scaleY); + + // Canvas rotation is done by centering the clip window at the origin, rotating, translating + // back so that the top left corner of the clip is at (0, 0). + canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2); + canvas->rotate(toDegrees(display.orientation)); + canvas->translate(-clipWidth / 2, -clipHeight / 2); + canvas->translate(-display.clip.left, -display.clip.top); +} + status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, const sp<GraphicBuffer>& buffer, @@ -567,57 +610,50 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, } } - sk_sp<SkSurface> surface = + sk_sp<SkSurface> dstSurface = surfaceTextureRef->getTexture()->getOrCreateSurface(mUseColorManagement ? display.outputDataspace : ui::Dataspace::UNKNOWN, grContext.get()); - SkCanvas* canvas = mCapture->tryCapture(surface.get()); - if (canvas == nullptr) { + SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get()); + if (dstCanvas == nullptr) { ALOGE("Cannot acquire canvas from Skia."); return BAD_VALUE; } - // Clear the entire canvas with a transparent black to prevent ghost images. - canvas->clear(SK_ColorTRANSPARENT); - canvas->save(); - if (mCapture->isCaptureRunning()) { - // Record display settings when capture is running. - std::stringstream displaySettings; - PrintTo(display, &displaySettings); - // Store the DisplaySettings in additional information. - canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings", - SkData::MakeWithCString(displaySettings.str().c_str())); - } - - // Before doing any drawing, let's make sure that we'll start at the origin of the display. - // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual - // displays might have different scaling when compared to the physical screen. - - canvas->clipRect(getSkRect(display.physicalDisplay)); - canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top); - - const auto clipWidth = display.clip.width(); - const auto clipHeight = display.clip.height(); - auto rotatedClipWidth = clipWidth; - auto rotatedClipHeight = clipHeight; - // Scale is contingent on the rotation result. - if (display.orientation & ui::Transform::ROT_90) { - std::swap(rotatedClipWidth, rotatedClipHeight); + // Find if any layers have requested blur, we'll use that info to decide when to render to an + // offscreen buffer and when to render to the native buffer. + sk_sp<SkSurface> activeSurface(dstSurface); + SkCanvas* canvas = dstCanvas; + SkiaCapture::OffscreenState offscreenCaptureState; + const LayerSettings* blurCompositionLayer = nullptr; + if (mBlurFilter) { + bool requiresCompositionLayer = false; + for (const auto& layer : layers) { + if (layer->backgroundBlurRadius > 0) { + // when skbug.com/11208 and b/176903027 are resolved we can add the additional + // restriction for layer->backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius + requiresCompositionLayer = true; + } + for (auto region : layer->blurRegions) { + if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) { + requiresCompositionLayer = true; + } + } + if (requiresCompositionLayer) { + activeSurface = dstSurface->makeSurface(dstSurface->imageInfo()); + canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState); + blurCompositionLayer = layer; + break; + } + } } - const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) / - static_cast<SkScalar>(rotatedClipWidth); - const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) / - static_cast<SkScalar>(rotatedClipHeight); - canvas->scale(scaleX, scaleY); - // Canvas rotation is done by centering the clip window at the origin, rotating, translating - // back so that the top left corner of the clip is at (0, 0). - canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2); - canvas->rotate(toDegrees(display.orientation)); - canvas->translate(-clipWidth / 2, -clipHeight / 2); - canvas->translate(-display.clip.left, -display.clip.top); + canvas->save(); + // Clear the entire canvas with a transparent black to prevent ghost images. + canvas->clear(SK_ColorTRANSPARENT); + initCanvas(canvas, display); // TODO: clearRegion was required for SurfaceView when a buffer is not yet available but the // view is still on-screen. The clear region could be re-specified as a black color layer, @@ -644,9 +680,45 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, for (const auto& layer : layers) { ATRACE_NAME("DrawLayer"); - canvas->save(); - if (mCapture->isCaptureRunning()) { + sk_sp<SkImage> blurInput; + if (blurCompositionLayer == layer) { + LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface); + LOG_ALWAYS_FATAL_IF(canvas == dstCanvas); + + // save a snapshot of the activeSurface to use as input to the blur shaders + blurInput = activeSurface->makeImageSnapshot(); + + // TODO we could skip this step if we know the blur will cover the entire image + // blit the offscreen framebuffer into the destination AHB + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { + uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState); + dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()), + String8::format("SurfaceID|%" PRId64, id).c_str(), + nullptr); + dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); + } else { + activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint); + } + + // assign dstCanvas to canvas and ensure that the canvas state is up to date + canvas = dstCanvas; + canvas->save(); + initCanvas(canvas, display); + + LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() != + dstSurface->getCanvas()->getSaveCount()); + LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() != + dstSurface->getCanvas()->getTotalMatrix()); + + // assign dstSurface to activeSurface + activeSurface = dstSurface; + } + + canvas->save(); + if (CC_UNLIKELY(mCapture->isCaptureRunning())) { // Record the name of the layer if the capture is running. std::stringstream layerSettings; PrintTo(*layer, &layerSettings); @@ -654,34 +726,42 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, canvas->drawAnnotation(SkRect::MakeEmpty(), layer->name.c_str(), SkData::MakeWithCString(layerSettings.str().c_str())); } - // Layers have a local transform that should be applied to them canvas->concat(getSkM44(layer->geometry.positionTransform).asM33()); - SkPaint paint; - const auto& bounds = layer->geometry.boundaries; - const auto dest = getSkRect(bounds); - const auto layerRect = canvas->getTotalMatrix().mapRect(dest); - std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; - if (mBlurFilter) { + const auto bounds = getSkRect(layer->geometry.boundaries); + if (mBlurFilter && layerHasBlur(layer)) { + std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; + + // if multiple layers have blur, then we need to take a snapshot now because + // only the lowest layer will have blurImage populated earlier + if (!blurInput) { + blurInput = activeSurface->makeImageSnapshot(); + } + // rect to be blurred in the coordinate space of blurInput + const auto blurRect = canvas->getTotalMatrix().mapRect(bounds); + if (layer->backgroundBlurRadius > 0) { ATRACE_NAME("BackgroundBlur"); - auto blurredSurface = mBlurFilter->generate(canvas, surface, - layer->backgroundBlurRadius, layerRect); - cachedBlurs[layer->backgroundBlurRadius] = blurredSurface; + auto blurredImage = + mBlurFilter->generate(grContext.get(), layer->backgroundBlurRadius, + blurInput, blurRect); - drawBlurRegion(canvas, getBlurRegion(layer), layerRect, blurredSurface); + cachedBlurs[layer->backgroundBlurRadius] = blurredImage; + + mBlurFilter->drawBlurRegion(canvas, getBlurRegion(layer), blurRect, blurredImage, + blurInput); } - if (layer->blurRegions.size() > 0) { - for (auto region : layer->blurRegions) { - if (cachedBlurs[region.blurRadius]) { - continue; - } + for (auto region : layer->blurRegions) { + if (cachedBlurs[region.blurRadius] == nullptr) { ATRACE_NAME("BlurRegion"); - auto blurredSurface = - mBlurFilter->generate(canvas, surface, region.blurRadius, layerRect); - cachedBlurs[region.blurRadius] = blurredSurface; + cachedBlurs[region.blurRadius] = + mBlurFilter->generate(grContext.get(), region.blurRadius, blurInput, + blurRect); } + + mBlurFilter->drawBlurRegion(canvas, region, blurRect, + cachedBlurs[region.blurRadius], blurInput); } } @@ -695,6 +775,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, : layer->sourceDataspace) : ui::Dataspace::UNKNOWN; + SkPaint paint; if (layer->source.buffer.buffer) { ATRACE_NAME("DrawImage"); const auto& item = layer->source.buffer; @@ -721,7 +802,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, // textureTansform was intended to be passed directly into a shader, so when // building the total matrix with the textureTransform we need to first // normalize it, then apply the textureTransform, then scale back up. - texMatrix.preScale(1.0f / bounds.getWidth(), 1.0f / bounds.getHeight()); + texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height()); texMatrix.postScale(image->width(), image->height()); SkMatrix matrix; @@ -791,14 +872,10 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, paint.setColorFilter(filter); - for (const auto effectRegion : layer->blurRegions) { - drawBlurRegion(canvas, effectRegion, layerRect, cachedBlurs[effectRegion.blurRadius]); - } - if (layer->shadow.length > 0) { const auto rect = layer->geometry.roundedCornersRadius > 0 ? getSkRect(layer->geometry.roundedCornersCrop) - : dest; + : bounds; drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow); } else { // Shadows are assumed to live only on their own layer - it's not valid @@ -809,7 +886,7 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, if (layer->geometry.roundedCornersRadius > 0) { canvas->clipRRect(getRoundedRect(layer), true); } - canvas->drawRect(dest, paint); + canvas->drawRect(bounds, paint); } canvas->restore(); } @@ -817,7 +894,8 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, mCapture->endCapture(); { ATRACE_NAME("flush surface"); - surface->flush(); + LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); + activeSurface->flush(); } if (drawFence != nullptr) { @@ -874,6 +952,10 @@ inline BlurRegion SkiaGLRenderEngine::getBlurRegion(const LayerSettings* layer) .bottom = static_cast<int>(rect.fBottom)}; } +inline bool SkiaGLRenderEngine::layerHasBlur(const LayerSettings* layer) { + return layer->backgroundBlurRadius > 0 || layer->blurRegions.size(); +} + inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) { return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255); } @@ -913,53 +995,6 @@ void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, flags); } -void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion, - const SkRect& layerRect, sk_sp<SkImage> blurredImage) { - ATRACE_CALL(); - - SkPaint paint; - paint.setAlpha(static_cast<int>(effectRegion.alpha * 255)); - const auto matrix = getBlurShaderTransform(canvas, layerRect); - SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone); - paint.setShader(blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, - &matrix)); - - auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right, - effectRegion.bottom); - - if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 || - effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) { - const SkVector radii[4] = - {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL), - SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR), - SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL), - SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)}; - SkRRect roundedRect; - roundedRect.setRectRadii(rect, radii); - canvas->drawRRect(roundedRect, paint); - } else { - canvas->drawRect(rect, paint); - } -} - -SkMatrix SkiaGLRenderEngine::getBlurShaderTransform(const SkCanvas* canvas, - const SkRect& layerRect) { - // 1. Apply the blur shader matrix, which scales up the blured surface to its real size - auto matrix = mBlurFilter->getShaderMatrix(); - // 2. Since the blurred surface has the size of the layer, we align it with the - // top left corner of the layer position. - matrix.postConcat(SkMatrix::Translate(layerRect.fLeft, layerRect.fTop)); - // 3. Finally, apply the inverse canvas matrix. The snapshot made in the BlurFilter is in the - // original surface orientation. The inverse matrix has to be applied to align the blur - // surface with the current orientation/position of the canvas. - SkMatrix drawInverse; - if (canvas->getTotalMatrix().invert(&drawInverse)) { - matrix.postConcat(drawInverse); - } - - return matrix; -} - EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, std::optional<ContextPriority> contextPriority, diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index 810fc2ae8f..5779ae679b 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -61,6 +61,7 @@ public: bool isProtected() const override { return mInProtectedContext; } bool supportsProtectedContent() const override; bool useProtectedContext(bool useProtectedContext) override; + bool supportsBackgroundBlur() override { return mBlurFilter != nullptr; } protected: void dump(std::string& result) override; @@ -81,17 +82,16 @@ private: inline SkRect getSkRect(const Rect& layer); inline SkRRect getRoundedRect(const LayerSettings* layer); inline BlurRegion getBlurRegion(const LayerSettings* layer); + inline bool layerHasBlur(const LayerSettings* layer); inline SkColor getSkColor(const vec4& color); inline SkM44 getSkM44(const mat4& matrix); inline SkPoint3 getSkPoint3(const vec3& vector); base::unique_fd flush(); bool waitFence(base::unique_fd fenceFd); + void initCanvas(SkCanvas* canvas, const DisplaySettings& display); void drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius, const ShadowSettings& shadowSettings); - void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerRect, - sk_sp<SkImage> blurredImage); - SkMatrix getBlurShaderTransform(const SkCanvas* canvas, const SkRect& layerRect); // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime // shader. Otherwise it returns the input shader. sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer, diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp index e9cfead8b6..40f5cf299d 100644 --- a/libs/renderengine/skia/debug/SkiaCapture.cpp +++ b/libs/renderengine/skia/debug/SkiaCapture.cpp @@ -41,11 +41,11 @@ SkiaCapture::~SkiaCapture() { mTimer.stop(); } -SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) { +SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) NO_THREAD_SAFETY_ANALYSIS { ATRACE_CALL(); // If we are not running yet, set up. - if (!mCaptureRunning) { + if (CC_LIKELY(!mCaptureRunning)) { mTimerInterval = std::chrono::milliseconds( base::GetIntProperty(PROPERTY_DEBUG_RENDERENGINE_CAPTURE_SKIA_MS, 0)); // Set up the multi-frame capture. If we fail to set it up, then just return canvas. @@ -56,7 +56,8 @@ SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) { // Start the new timer. When timer expires, write to file. mTimer.setTimeout( [this] { - endCapture(); + const std::scoped_lock lock(mMutex); + LOG_ALWAYS_FATAL_IF(mCurrentPageCanvas != nullptr); writeToFile(); // To avoid going in circles, set the flag to 0. This way the capture can be // restarted just by setting the flag and without restarting the process. @@ -65,29 +66,82 @@ SkCanvas* SkiaCapture::tryCapture(SkSurface* surface) { mTimerInterval); } + mMutex.lock(); + // Create a canvas pointer, fill it. - SkCanvas* pictureCanvas = mMultiPic->beginPage(surface->width(), surface->height()); + mCurrentPageCanvas = mMultiPic->beginPage(surface->width(), surface->height()); // Setting up an nway canvas is common to any kind of capture. mNwayCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); mNwayCanvas->addCanvas(surface->getCanvas()); - mNwayCanvas->addCanvas(pictureCanvas); + mNwayCanvas->addCanvas(mCurrentPageCanvas); return mNwayCanvas.get(); } -void SkiaCapture::endCapture() { +void SkiaCapture::endCapture() NO_THREAD_SAFETY_ANALYSIS { ATRACE_CALL(); // Don't end anything if we are not running. - if (!mCaptureRunning) { + if (CC_LIKELY(!mCaptureRunning)) { return; } // Reset the canvas pointer. + mCurrentPageCanvas = nullptr; mNwayCanvas.reset(); // End page. if (mMultiPic) { mMultiPic->endPage(); } + mMutex.unlock(); +} + +SkCanvas* SkiaCapture::tryOffscreenCapture(SkSurface* surface, OffscreenState* state) { + ATRACE_CALL(); + // Don't start anything if we are not running. + if (CC_LIKELY(!mCaptureRunning)) { + return surface->getCanvas(); + } + + // Create a canvas pointer, fill it. + state->offscreenRecorder = std::make_unique<SkPictureRecorder>(); + SkCanvas* pictureCanvas = + state->offscreenRecorder->beginRecording(surface->width(), surface->height()); + + // Setting up an nway canvas is common to any kind of capture. + state->offscreenCanvas = std::make_unique<SkNWayCanvas>(surface->width(), surface->height()); + state->offscreenCanvas->addCanvas(surface->getCanvas()); + state->offscreenCanvas->addCanvas(pictureCanvas); + + return state->offscreenCanvas.get(); +} + +uint64_t SkiaCapture::endOffscreenCapture(OffscreenState* state) { + ATRACE_CALL(); + // Don't end anything if we are not running. + if (CC_LIKELY(!mCaptureRunning)) { + return 0; + } + + // compute the uniqueID for this capture + static std::atomic<uint64_t> nextID{1}; + const uint64_t uniqueID = nextID.fetch_add(1, std::memory_order_relaxed); + + // Reset the canvas pointer as we are no longer drawing into it + state->offscreenCanvas.reset(); + + // Record the offscreen as a picture in the currently active page. + SkRect bounds = + SkRect::Make(state->offscreenRecorder->getRecordingCanvas()->imageInfo().dimensions()); + mCurrentPageCanvas + ->drawAnnotation(bounds, + String8::format("OffscreenLayerDraw|%" PRId64, uniqueID).c_str(), + nullptr); + mCurrentPageCanvas->drawPicture(state->offscreenRecorder->finishRecordingAsPicture()); + + // Reset the offscreen picture recorder + state->offscreenRecorder.reset(); + + return uniqueID; } void SkiaCapture::writeToFile() { diff --git a/libs/renderengine/skia/debug/SkiaCapture.h b/libs/renderengine/skia/debug/SkiaCapture.h index eaaf598bd0..5e18e60f93 100644 --- a/libs/renderengine/skia/debug/SkiaCapture.h +++ b/libs/renderengine/skia/debug/SkiaCapture.h @@ -18,8 +18,12 @@ #include <SkDocument.h> #include <SkNWayCanvas.h> +#include <SkPictureRecorder.h> #include <SkSurface.h> + #include <chrono> +#include <mutex> + #include "CaptureTimer.h" #include "tools/SkSharingProc.h" @@ -48,6 +52,16 @@ public: // Returns whether the capture is running. bool isCaptureRunning() { return mCaptureRunning; } + // Offscreen state member variables are private to SkiaCapture, but the allocation + // and lifetime is managed by the caller. This enables nested offscreen + // captures to occur. + struct OffscreenState { + std::unique_ptr<SkPictureRecorder> offscreenRecorder; + std::unique_ptr<SkNWayCanvas> offscreenCanvas; + }; + SkCanvas* tryOffscreenCapture(SkSurface* surface, OffscreenState* state); + uint64_t endOffscreenCapture(OffscreenState* state); + private: // Performs the first-frame work of a multi frame SKP capture. Returns true if successful. bool setupMultiFrameCapture(); @@ -61,10 +75,16 @@ private: std::unique_ptr<SkSharingSerialContext> mSerialContext; std::unique_ptr<SkNWayCanvas> mNwayCanvas; + SkCanvas* mCurrentPageCanvas = nullptr; + // Capturing and interval control. bool mCaptureRunning = false; CaptureTimer mTimer; Interval mTimerInterval = 0ms; + + // Mutex to ensure that a frame in progress when the timer fires is allowed to run to + // completion before we write the file to disk. + std::mutex mMutex; }; } // namespace skia diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp index 87b1a7c8f7..5960e48f7b 100644 --- a/libs/renderengine/skia/filters/BlurFilter.cpp +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -20,6 +20,7 @@ #include <SkCanvas.h> #include <SkData.h> #include <SkPaint.h> +#include <SkRRect.h> #include <SkRuntimeEffect.h> #include <SkSize.h> #include <SkString.h> @@ -34,17 +35,14 @@ namespace skia { BlurFilter::BlurFilter() { SkString blurString(R"( in shader input; - uniform float in_inverseScale; uniform float2 in_blurOffset; half4 main(float2 xy) { - float2 scaled_xy = float2(xy.x * in_inverseScale, xy.y * in_inverseScale); - - half4 c = sample(input, scaled_xy); - c += sample(input, scaled_xy + float2( in_blurOffset.x, in_blurOffset.y)); - c += sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y)); - c += sample(input, scaled_xy + float2(-in_blurOffset.x, in_blurOffset.y)); - c += sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y)); + half4 c = sample(input, xy); + c += sample(input, xy + float2( in_blurOffset.x, in_blurOffset.y)); + c += sample(input, xy + float2( in_blurOffset.x, -in_blurOffset.y)); + c += sample(input, xy + float2(-in_blurOffset.x, in_blurOffset.y)); + c += sample(input, xy + float2(-in_blurOffset.x, -in_blurOffset.y)); return half4(c.rgb * 0.2, 1.0); } @@ -55,10 +53,26 @@ BlurFilter::BlurFilter() { LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str()); } mBlurEffect = std::move(blurEffect); + + SkString mixString(R"( + in shader blurredInput; + in shader originalInput; + uniform float mixFactor; + + half4 main(float2 xy) { + return half4(mix(sample(originalInput), sample(blurredInput), mixFactor)); + } + )"); + + auto [mixEffect, mixError] = SkRuntimeEffect::Make(mixString); + if (!mixEffect) { + LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str()); + } + mMixEffect = std::move(mixEffect); } -sk_sp<SkImage> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input, - const uint32_t blurRadius, SkRect rect) const { +sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius, + const sk_sp<SkImage> input, const SkRect& blurRect) const { // Kawase is an approximation of Gaussian, but it behaves differently from it. // A radius transformation is required for approximating them, and also to introduce // non-integer steps, necessary to smoothly interpolate large radii. @@ -66,40 +80,110 @@ sk_sp<SkImage> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> inp float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius)); float radiusByPasses = tmpRadius / (float)numberOfPasses; - SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)rect.width() * kInputScale, - (float)rect.height() * kInputScale); + // create blur surface with the bit depth and colorspace of the original surface + SkImageInfo scaledInfo = input->imageInfo().makeWH(blurRect.width() * kInputScale, + blurRect.height() * kInputScale); const float stepX = radiusByPasses; const float stepY = radiusByPasses; - // start by drawing and downscaling and doing the first blur pass + // For sampling Skia's API expects the inverse of what logically seems appropriate. In this + // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale) + // but instead we must do the inverse. + SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop); + blurMatrix.postScale(kInputScale, kInputScale); + + // start by downscaling and doing the first blur pass SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone); SkRuntimeShaderBuilder blurBuilder(mBlurEffect); blurBuilder.child("input") = - input->makeImageSnapshot(rect.round()) - ->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear); - blurBuilder.uniform("in_inverseScale") = kInverseInputScale; - blurBuilder.uniform("in_blurOffset") = - SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale}; + input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix); + blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale}; - sk_sp<SkImage> tmpBlur( - blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false)); + sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false)); // And now we'll build our chain of scaled blur stages - blurBuilder.uniform("in_inverseScale") = 1.0f; for (auto i = 1; i < numberOfPasses; i++) { const float stepScale = (float)i * kInputScale; blurBuilder.child("input") = tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear); blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale}; - tmpBlur = blurBuilder.makeImage(canvas->recordingContext(), nullptr, scaledInfo, false); + tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false); } return tmpBlur; } -SkMatrix BlurFilter::getShaderMatrix() const { - return SkMatrix::Scale(kInverseInputScale, kInverseInputScale); +static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) { + // 1. Apply the blur shader matrix, which scales up the blured surface to its real size + auto matrix = SkMatrix::Scale(scale, scale); + // 2. Since the blurred surface has the size of the layer, we align it with the + // top left corner of the layer position. + matrix.postConcat(SkMatrix::Translate(blurRect.fLeft, blurRect.fTop)); + // 3. Finally, apply the inverse canvas matrix. The snapshot made in the BlurFilter is in the + // original surface orientation. The inverse matrix has to be applied to align the blur + // surface with the current orientation/position of the canvas. + SkMatrix drawInverse; + if (canvas != nullptr && canvas->getTotalMatrix().invert(&drawInverse)) { + matrix.postConcat(drawInverse); + } + return matrix; +} + +void BlurFilter::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion, + const SkRect& blurRect, sk_sp<SkImage> blurredImage, + sk_sp<SkImage> input) { + ATRACE_CALL(); + + SkPaint paint; + paint.setAlphaf(effectRegion.alpha); + if (effectRegion.alpha == 1.0f) { + paint.setBlendMode(SkBlendMode::kSrc); + } + + const auto blurMatrix = getShaderTransform(canvas, blurRect, kInverseInputScale); + SkSamplingOptions linearSampling(SkFilterMode::kLinear, SkMipmapMode::kNone); + const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, + linearSampling, &blurMatrix); + + if (effectRegion.blurRadius < kMaxCrossFadeRadius) { + // For sampling Skia's API expects the inverse of what logically seems appropriate. In this + // case you might expect the matrix to simply be the canvas matrix. + SkMatrix inputMatrix; + if (!canvas->getTotalMatrix().invert(&inputMatrix)) { + ALOGE("matrix was unable to be inverted"); + } + + SkRuntimeShaderBuilder blurBuilder(mMixEffect); + blurBuilder.child("blurredInput") = blurShader; + blurBuilder.child("originalInput") = + input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling, + inputMatrix); + blurBuilder.uniform("mixFactor") = effectRegion.blurRadius / kMaxCrossFadeRadius; + + paint.setShader(blurBuilder.makeShader(nullptr, true)); + } else { + paint.setShader(blurShader); + } + + // TODO we should AA at least the drawRoundRect which would mean no SRC blending + // TODO this round rect calculation doesn't match the one used to draw in RenderEngine + auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right, + effectRegion.bottom); + + if (effectRegion.cornerRadiusTL > 0 || effectRegion.cornerRadiusTR > 0 || + effectRegion.cornerRadiusBL > 0 || effectRegion.cornerRadiusBR > 0) { + const SkVector radii[4] = + {SkVector::Make(effectRegion.cornerRadiusTL, effectRegion.cornerRadiusTL), + SkVector::Make(effectRegion.cornerRadiusTR, effectRegion.cornerRadiusTR), + SkVector::Make(effectRegion.cornerRadiusBL, effectRegion.cornerRadiusBL), + SkVector::Make(effectRegion.cornerRadiusBR, effectRegion.cornerRadiusBR)}; + SkRRect roundedRect; + roundedRect.setRectRadii(rect, radii); + canvas->drawRRect(roundedRect, paint); + } else { + canvas->drawRect(rect, paint); + } } } // namespace skia diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h index 08641c913d..731ba11483 100644 --- a/libs/renderengine/skia/filters/BlurFilter.h +++ b/libs/renderengine/skia/filters/BlurFilter.h @@ -20,6 +20,7 @@ #include <SkImage.h> #include <SkRuntimeEffect.h> #include <SkSurface.h> +#include <ui/BlurRegion.h> using namespace std; @@ -48,13 +49,15 @@ public: virtual ~BlurFilter(){}; // Execute blur, saving it to a texture - sk_sp<SkImage> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, const uint32_t radius, - SkRect rect) const; - // Returns a matrix that should be applied to the blur shader - SkMatrix getShaderMatrix() const; + sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius, + const sk_sp<SkImage> blurInput, const SkRect& blurRect) const; + + void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& blurRect, + sk_sp<SkImage> blurredImage, sk_sp<SkImage> input); private: sk_sp<SkRuntimeEffect> mBlurEffect; + sk_sp<SkRuntimeEffect> mMixEffect; }; } // namespace skia diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 51c702884b..d0e19dd4e2 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "librenderengine_test", defaults: ["skia_deps", "surfaceflinger_defaults"], diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 58afe6ee75..500a90b6ef 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -914,13 +914,6 @@ void RenderEngineTest::fillBufferWithRoundedCorners() { template <typename SourceVariant> void RenderEngineTest::fillBufferAndBlurBackground() { - char value[PROPERTY_VALUE_MAX]; - property_get("ro.surface_flinger.supports_background_blur", value, "0"); - if (!atoi(value)) { - // This device doesn't support blurs, no-op. - return; - } - auto blurRadius = 50; auto center = DEFAULT_DISPLAY_WIDTH / 2; @@ -950,15 +943,20 @@ void RenderEngineTest::fillBufferAndBlurBackground() { blurLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; blurLayer.geometry.boundaries = fullscreenRect().toFloatRect(); blurLayer.backgroundBlurRadius = blurRadius; + SourceVariant::fillColor(blurLayer, 0.0f, 0.0f, 1.0f, this); blurLayer.alpha = 0; layers.push_back(&blurLayer); invokeDraw(settings, layers); - expectBufferColor(Rect(center - 1, center - 5, center, center + 5), 150, 150, 0, 255, - 50 /* tolerance */); - expectBufferColor(Rect(center, center - 5, center + 1, center + 5), 150, 150, 0, 255, - 50 /* tolerance */); + // solid color + expectBufferColor(Rect(0, 0, 1, 1), 255, 0, 0, 255, 0 /* tolerance */); + + if (mRE->supportsBackgroundBlur()) { + // blurred color (downsampling should result in the center color being close to 128) + expectBufferColor(Rect(center - 1, center - 5, center + 1, center + 5), 128, 128, 0, 255, + 10 /* tolerance */); + } } template <typename SourceVariant> diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp index 02ff06f393..08b672a99f 100644 --- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -144,6 +144,17 @@ TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) { ASSERT_EQ(true, result); } +TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsFalse) { + EXPECT_CALL(*mRenderEngine, supportsBackgroundBlur()).WillOnce(Return(false)); + status_t result = mThreadedRE->supportsBackgroundBlur(); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, supportsBackgroundBlur_returnsTrue) { + EXPECT_CALL(*mRenderEngine, supportsBackgroundBlur()).WillOnce(Return(true)); + status_t result = mThreadedRE->supportsBackgroundBlur(); + ASSERT_EQ(true, result); +} TEST_F(RenderEngineThreadedTest, drawLayers) { renderengine::DisplaySettings settings; std::vector<const renderengine::LayerSettings*> layers; diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index 3b97f5659e..f4481358cf 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -315,6 +315,21 @@ int RenderEngineThreaded::getContextPriority() { return resultFuture.get(); } +bool RenderEngineThreaded::supportsBackgroundBlur() { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::supportsBackgroundBlur"); + bool returnValue = instance.supportsBackgroundBlur(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + } // namespace threaded } // namespace renderengine } // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index 8b1e2dec5a..8279cbcd09 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -64,6 +64,7 @@ public: void cleanFramebufferCache() override; int getContextPriority() override; + bool supportsBackgroundBlur() override; private: void threadMain(CreateInstanceFactory factory); diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index e8154a6931..497c33c386 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libsensor", diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index a6b0aaf0b5..a6cacad374 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -91,13 +91,14 @@ public: } virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName, - int mode, const String16& opPackageName) + int mode, const String16& opPackageName, const String16& attributionTag) { Parcel data, reply; data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); data.writeString8(packageName); data.writeInt32(mode); data.writeString16(opPackageName); + data.writeString16(attributionTag); remote()->transact(CREATE_SENSOR_EVENT_CONNECTION, data, &reply); return interface_cast<ISensorEventConnection>(reply.readStrongBinder()); } @@ -170,8 +171,9 @@ status_t BnSensorServer::onTransact( String8 packageName = data.readString8(); int32_t mode = data.readInt32(); const String16& opPackageName = data.readString16(); + const String16& attributionTag = data.readString16(); sp<ISensorEventConnection> connection(createSensorEventConnection(packageName, mode, - opPackageName)); + opPackageName, attributionTag)); reply->writeStrongBinder(IInterface::asBinder(connection)); return NO_ERROR; } diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index 9d817ae0bd..240738d42d 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -468,6 +468,19 @@ void Sensor::setId(int32_t id) { mUuid.i64[1] = 0; } +void Sensor::capMinDelayMicros(int32_t cappedMinDelay) { + if (mMinDelay < cappedMinDelay) { + mMinDelay = cappedMinDelay; + } +} + +void Sensor::capHighestDirectReportRateLevel(int32_t cappedRateLevel) { + if (cappedRateLevel < getHighestDirectReportRateLevel()) { + mFlags &= ~SENSOR_FLAG_MASK_DIRECT_REPORT; + mFlags |= cappedRateLevel << SENSOR_FLAG_SHIFT_DIRECT_REPORT; + } +} + int32_t Sensor::getId() const { return int32_t(mUuid.i64[0]); } diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index a4a5d135c0..62f4b4e3e2 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -225,13 +225,14 @@ Sensor const* SensorManager::getDefaultSensor(int type) return nullptr; } -sp<SensorEventQueue> SensorManager::createEventQueue(String8 packageName, int mode) { +sp<SensorEventQueue> SensorManager::createEventQueue( + String8 packageName, int mode, String16 attributionTag) { sp<SensorEventQueue> queue; Mutex::Autolock _l(mLock); while (assertStateLocked() == NO_ERROR) { - sp<ISensorEventConnection> connection = - mSensorServer->createSensorEventConnection(packageName, mode, mOpPackageName); + sp<ISensorEventConnection> connection = mSensorServer->createSensorEventConnection( + packageName, mode, mOpPackageName, attributionTag); if (connection == nullptr) { // SensorService just died or the app doesn't have required permissions. ALOGE("createEventQueue: connection is NULL."); diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h index 402678f0fd..ce5c672da0 100644 --- a/libs/sensor/include/sensor/ISensorServer.h +++ b/libs/sensor/include/sensor/ISensorServer.h @@ -45,7 +45,7 @@ public: virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName) = 0; virtual sp<ISensorEventConnection> createSensorEventConnection(const String8& packageName, - int mode, const String16& opPackageName) = 0; + int mode, const String16& opPackageName, const String16& attributionTag) = 0; virtual int32_t isDataInjectionEnabled() = 0; virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName, diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h index 324d443106..374b68fab5 100644 --- a/libs/sensor/include/sensor/Sensor.h +++ b/libs/sensor/include/sensor/Sensor.h @@ -104,6 +104,9 @@ public: int32_t getId() const; void setId(int32_t id); + void capMinDelayMicros(int32_t cappedMinDelay); + void capHighestDirectReportRateLevel(int32_t cappedRateLevel); + // LightFlattenable protocol inline bool isFixedSize() const { return false; } size_t getFlattenedSize() const; diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index f09c9c67b4..09ac7edf27 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h @@ -59,7 +59,8 @@ public: ssize_t getSensorList(Sensor const* const** list); ssize_t getDynamicSensorList(Vector<Sensor>& list); Sensor const* getDefaultSensor(int type); - sp<SensorEventQueue> createEventQueue(String8 packageName = String8(""), int mode = 0); + sp<SensorEventQueue> createEventQueue( + String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16("")); bool isDataInjectionEnabled(); int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData); void destroyDirectChannel(int channelNativeHandle); diff --git a/libs/sensor/tests/Android.bp b/libs/sensor/tests/Android.bp index c9a7668563..8fdb003a5d 100644 --- a/libs/sensor/tests/Android.bp +++ b/libs/sensor/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "libsensor_test", diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp index 4a606ffec2..00514c4417 100644 --- a/libs/sensorprivacy/Android.bp +++ b/libs/sensorprivacy/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libsensorprivacy", diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index a8d476250b..de2b9a406b 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -12,6 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + default_applicable_licenses: ["frameworks_native_libs_ui_license"], +} + +// Added automatically by a large-scale-change +// See: http://go/android-license-faq +license { + name: "frameworks_native_libs_ui_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-Apache-2.0", + ], + license_text: [ + "NOTICE", + ], +} + cc_defaults { name: "libui-defaults", clang: true, @@ -77,7 +94,6 @@ cc_library_static { "libarect", "libmath", ], - } cc_library_shared { @@ -105,7 +121,8 @@ cc_library_shared { srcs: [ "DebugUtils.cpp", "DeviceProductInfo.cpp", - "DisplayInfo.cpp", + "DisplayMode.cpp", + "DynamicDisplayInfo.cpp", "Fence.cpp", "FenceTime.cpp", "FrameStats.cpp", @@ -120,6 +137,7 @@ cc_library_shared { "PixelFormat.cpp", "PublicFormat.cpp", "Size.cpp", + "StaticDisplayInfo.cpp", ], include_dirs: [ diff --git a/libs/ui/DisplayMode.cpp b/libs/ui/DisplayMode.cpp new file mode 100644 index 0000000000..cf05dbfb05 --- /dev/null +++ b/libs/ui/DisplayMode.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/DisplayMode.h> + +#include <cstdint> + +#include <ui/FlattenableHelpers.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android::ui { + +size_t DisplayMode::getFlattenedSize() const { + return FlattenableHelpers::getFlattenedSize(id) + + FlattenableHelpers::getFlattenedSize(resolution) + + FlattenableHelpers::getFlattenedSize(xDpi) + + FlattenableHelpers::getFlattenedSize(yDpi) + + FlattenableHelpers::getFlattenedSize(refreshRate) + + FlattenableHelpers::getFlattenedSize(appVsyncOffset) + + FlattenableHelpers::getFlattenedSize(sfVsyncOffset) + + FlattenableHelpers::getFlattenedSize(presentationDeadline) + + FlattenableHelpers::getFlattenedSize(group); +} + +status_t DisplayMode::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, id)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, resolution)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, xDpi)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, yDpi)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, refreshRate)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, appVsyncOffset)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, sfVsyncOffset)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, presentationDeadline)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, group)); + return OK; +} + +status_t DisplayMode::unflatten(const void* buffer, size_t size) { + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &id)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &resolution)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &xDpi)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &yDpi)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &refreshRate)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &appVsyncOffset)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &sfVsyncOffset)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &presentationDeadline)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &group)); + return OK; +} + +} // namespace android::ui diff --git a/libs/ui/DynamicDisplayInfo.cpp b/libs/ui/DynamicDisplayInfo.cpp new file mode 100644 index 0000000000..d5c4ef0c17 --- /dev/null +++ b/libs/ui/DynamicDisplayInfo.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/DynamicDisplayInfo.h> + +#include <cstdint> + +#include <ui/FlattenableHelpers.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android::ui { + +std::optional<ui::DisplayMode> DynamicDisplayInfo::getActiveDisplayMode() const { + for (const auto& currMode : supportedDisplayModes) { + if (currMode.id == activeDisplayModeId) { + return currMode; + } + } + return {}; +} + +size_t DynamicDisplayInfo::getFlattenedSize() const { + return FlattenableHelpers::getFlattenedSize(supportedDisplayModes) + + FlattenableHelpers::getFlattenedSize(activeDisplayModeId) + + FlattenableHelpers::getFlattenedSize(supportedColorModes) + + FlattenableHelpers::getFlattenedSize(activeColorMode) + + FlattenableHelpers::getFlattenedSize(hdrCapabilities) + + FlattenableHelpers::getFlattenedSize(autoLowLatencyModeSupported) + + FlattenableHelpers::getFlattenedSize(gameContentTypeSupported); +} + +status_t DynamicDisplayInfo::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, supportedDisplayModes)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, activeDisplayModeId)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, supportedColorModes)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, activeColorMode)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, hdrCapabilities)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, autoLowLatencyModeSupported)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, gameContentTypeSupported)); + return OK; +} + +status_t DynamicDisplayInfo::unflatten(const void* buffer, size_t size) { + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &supportedDisplayModes)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &activeDisplayModeId)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &supportedColorModes)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &activeColorMode)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &hdrCapabilities)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &autoLowLatencyModeSupported)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &gameContentTypeSupported)); + return OK; +} + +} // namespace android::ui diff --git a/libs/ui/HdrCapabilities.cpp b/libs/ui/HdrCapabilities.cpp index a5b3e89314..aec2fac780 100644 --- a/libs/ui/HdrCapabilities.cpp +++ b/libs/ui/HdrCapabilities.cpp @@ -23,10 +23,6 @@ namespace android { #pragma clang diagnostic ignored "-Wundefined-reinterpret-cast" #endif -HdrCapabilities::~HdrCapabilities() = default; -HdrCapabilities::HdrCapabilities(HdrCapabilities&& other) noexcept = default; -HdrCapabilities& HdrCapabilities::operator=(HdrCapabilities&& other) noexcept = default; - size_t HdrCapabilities::getFlattenedSize() const { return sizeof(mMaxLuminance) + sizeof(mMaxAverageLuminance) + diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/StaticDisplayInfo.cpp index 73a78af186..b66b281394 100644 --- a/libs/ui/DisplayInfo.cpp +++ b/libs/ui/StaticDisplayInfo.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <ui/DisplayInfo.h> +#include <ui/StaticDisplayInfo.h> #include <cstdint> @@ -23,16 +23,16 @@ #define RETURN_IF_ERROR(op) \ if (const status_t status = (op); status != OK) return status; -namespace android { +namespace android::ui { -size_t DisplayInfo::getFlattenedSize() const { +size_t StaticDisplayInfo::getFlattenedSize() const { return FlattenableHelpers::getFlattenedSize(connectionType) + FlattenableHelpers::getFlattenedSize(density) + FlattenableHelpers::getFlattenedSize(secure) + FlattenableHelpers::getFlattenedSize(deviceProductInfo); } -status_t DisplayInfo::flatten(void* buffer, size_t size) const { +status_t StaticDisplayInfo::flatten(void* buffer, size_t size) const { if (size < getFlattenedSize()) { return NO_MEMORY; } @@ -43,7 +43,7 @@ status_t DisplayInfo::flatten(void* buffer, size_t size) const { return OK; } -status_t DisplayInfo::unflatten(void const* buffer, size_t size) { +status_t StaticDisplayInfo::unflatten(void const* buffer, size_t size) { RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType)); RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density)); RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure)); @@ -51,4 +51,4 @@ status_t DisplayInfo::unflatten(void const* buffer, size_t size) { return OK; } -} // namespace android +} // namespace android::ui diff --git a/libs/ui/include/ui/DisplayMode.h b/libs/ui/include/ui/DisplayMode.h index 145d7efd19..56f68e7bb2 100644 --- a/libs/ui/include/ui/DisplayMode.h +++ b/libs/ui/include/ui/DisplayMode.h @@ -16,15 +16,21 @@ #pragma once +#include <cstdint> #include <type_traits> #include <ui/Size.h> +#include <utils/Flattenable.h> #include <utils/Timers.h> namespace android::ui { +// This value is going to be serialized over binder so we prefer a fixed width type. +using DisplayModeId = int32_t; + // Mode supported by physical display. -struct DisplayMode { +struct DisplayMode : LightFlattenable<DisplayMode> { + DisplayModeId id; ui::Size resolution; float xDpi = 0; float yDpi = 0; @@ -33,9 +39,12 @@ struct DisplayMode { nsecs_t appVsyncOffset = 0; nsecs_t sfVsyncOffset = 0; nsecs_t presentationDeadline = 0; - int group = -1; -}; + int32_t group = -1; -static_assert(std::is_trivially_copyable_v<DisplayMode>); + bool isFixedSize() const { return false; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(const void* buffer, size_t size); +}; } // namespace android::ui diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h new file mode 100644 index 0000000000..a4c2f71a4c --- /dev/null +++ b/libs/ui/include/ui/DynamicDisplayInfo.h @@ -0,0 +1,60 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayMode.h" + +#include <cstdint> +#include <optional> +#include <vector> + +#include <ui/GraphicTypes.h> +#include <ui/HdrCapabilities.h> +#include <utils/Flattenable.h> + +namespace android::ui { + +// Information about a physical display which may change on hotplug reconnect. +struct DynamicDisplayInfo : LightFlattenable<DynamicDisplayInfo> { + std::vector<ui::DisplayMode> supportedDisplayModes; + + // This struct is going to be serialized over binder, so + // we can't use size_t because it may have different width + // in the client process. + int32_t activeDisplayModeId; + + std::vector<ui::ColorMode> supportedColorModes; + ui::ColorMode activeColorMode; + HdrCapabilities hdrCapabilities; + + // True if the display reports support for HDMI 2.1 Auto Low Latency Mode. + // For more information, see the HDMI 2.1 specification. + bool autoLowLatencyModeSupported; + + // True if the display reports support for Game Content Type. + // For more information, see the HDMI 1.4 specification. + bool gameContentTypeSupported; + + std::optional<ui::DisplayMode> getActiveDisplayMode() const; + + bool isFixedSize() const { return false; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(const void* buffer, size_t size); +}; + +} // namespace android::ui diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h index 69cadfe32e..4c9c7b7ef1 100644 --- a/libs/ui/include/ui/FloatRect.h +++ b/libs/ui/include/ui/FloatRect.h @@ -49,6 +49,8 @@ public: float top = 0.0f; float right = 0.0f; float bottom = 0.0f; + + constexpr bool isEmpty() const { return !(left < right && top < bottom); } }; inline bool operator==(const FloatRect& a, const FloatRect& b) { diff --git a/libs/ui/include/ui/HdrCapabilities.h b/libs/ui/include/ui/HdrCapabilities.h index 65ac26cf7e..813addeca6 100644 --- a/libs/ui/include/ui/HdrCapabilities.h +++ b/libs/ui/include/ui/HdrCapabilities.h @@ -36,18 +36,12 @@ public: mMaxAverageLuminance(maxAverageLuminance), mMinLuminance(minLuminance) {} - // Make this move-constructable and move-assignable - HdrCapabilities(HdrCapabilities&& other) noexcept; - HdrCapabilities& operator=(HdrCapabilities&& other) noexcept; - HdrCapabilities() : mSupportedHdrTypes(), mMaxLuminance(-1.0f), mMaxAverageLuminance(-1.0f), mMinLuminance(-1.0f) {} - ~HdrCapabilities(); - const std::vector<ui::Hdr>& getSupportedHdrTypes() const { return mSupportedHdrTypes; } diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/StaticDisplayInfo.h index 03e0a3886e..e86ca29a2a 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/StaticDisplayInfo.h @@ -17,17 +17,16 @@ #pragma once #include <optional> -#include <type_traits> #include <ui/DeviceProductInfo.h> #include <utils/Flattenable.h> -namespace android { +namespace android::ui { enum class DisplayConnectionType { Internal, External }; // Immutable information about physical display. -struct DisplayInfo : LightFlattenable<DisplayInfo> { +struct StaticDisplayInfo : LightFlattenable<StaticDisplayInfo> { DisplayConnectionType connectionType = DisplayConnectionType::Internal; float density = 0.f; bool secure = false; @@ -39,4 +38,4 @@ struct DisplayInfo : LightFlattenable<DisplayInfo> { status_t unflatten(void const* buffer, size_t size); }; -} // namespace android +} // namespace android::ui diff --git a/libs/ui/include/ui/StretchEffect.h b/libs/ui/include/ui/StretchEffect.h new file mode 100644 index 0000000000..0803df3828 --- /dev/null +++ b/libs/ui/include/ui/StretchEffect.h @@ -0,0 +1,58 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <utils/Flattenable.h> +#include "FloatRect.h" + +#include <math.h> +#include <type_traits> + +namespace android { + +struct StretchEffect : public LightFlattenablePod<StretchEffect> { + FloatRect area = {0, 0, 0, 0}; + float vectorX = 0; + float vectorY = 0; + float maxAmount = 0; + + bool operator==(const StretchEffect& other) const { + return area == other.area && vectorX == other.vectorX && vectorY == other.vectorY && + maxAmount == other.maxAmount; + } + + static bool isZero(float value) { + constexpr float NON_ZERO_EPSILON = 0.001f; + return fabsf(value) <= NON_ZERO_EPSILON; + } + + bool isNoOp() const { return isZero(vectorX) && isZero(vectorY); } + + bool hasEffect() const { return !isNoOp(); } + + void sanitize() { + // If the area is empty, or the max amount is zero, then reset back to defaults + if (area.isEmpty() || isZero(maxAmount)) { + *this = StretchEffect{}; + } + } +}; + +static_assert(std::is_trivially_copyable<StretchEffect>::value, + "StretchEffect must be trivially copyable to be flattenable"); + +} // namespace android
\ No newline at end of file diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h index 8e316d8ae0..378f37f077 100644 --- a/libs/ui/include_private/ui/FlattenableHelpers.h +++ b/libs/ui/include_private/ui/FlattenableHelpers.h @@ -29,20 +29,29 @@ namespace android { struct FlattenableHelpers { - // Helpers for reading and writing POD structures - template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + // Helpers for reading and writing POD structures which are not LightFlattenable. + template <class T, + typename = std::enable_if_t< + std::conjunction_v<std::is_trivially_copyable<T>, + std::negation<std::is_base_of<LightFlattenable<T>, T>>>>> static constexpr size_t getFlattenedSize(const T&) { return sizeof(T); } - template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + template <class T, + typename = std::enable_if_t< + std::conjunction_v<std::is_trivially_copyable<T>, + std::negation<std::is_base_of<LightFlattenable<T>, T>>>>> static status_t flatten(void** buffer, size_t* size, const T& value) { if (*size < sizeof(T)) return NO_MEMORY; FlattenableUtils::write(*buffer, *size, value); return OK; } - template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>> + template <class T, + typename = std::enable_if_t< + std::conjunction_v<std::is_trivially_copyable<T>, + std::negation<std::is_base_of<LightFlattenable<T>, T>>>>> static status_t unflatten(const void** buffer, size_t* size, T* value) { if (*size < sizeof(T)) return NO_MEMORY; FlattenableUtils::read(*buffer, *size, *value); diff --git a/libs/ui/include_vndk/ui/DisplayInfo.h b/libs/ui/include_vndk/ui/DisplayInfo.h deleted file mode 120000 index 75f14cf66d..0000000000 --- a/libs/ui/include_vndk/ui/DisplayInfo.h +++ /dev/null @@ -1 +0,0 @@ -../../include/ui/DisplayInfo.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/StaticDisplayInfo.h b/libs/ui/include_vndk/ui/StaticDisplayInfo.h new file mode 120000 index 0000000000..541a7a37b9 --- /dev/null +++ b/libs/ui/include_vndk/ui/StaticDisplayInfo.h @@ -0,0 +1 @@ +../../include/ui/StaticDisplayInfo.h
\ No newline at end of file diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index d005ce8e23..516aad824e 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_libs_ui_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_libs_ui_license"], +} + cc_test { name: "Region_test", shared_libs: ["libui"], diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp index db32bc7596..44e20b504d 100644 --- a/libs/ui/tests/FlattenableHelpers_test.cpp +++ b/libs/ui/tests/FlattenableHelpers_test.cpp @@ -42,7 +42,7 @@ struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> { } status_t unflatten(void const* buffer, size_t size) { - int value; + int32_t value; FlattenableUtils::read(buffer, size, value); ptr = std::make_unique<int32_t>(value); return OK; @@ -132,5 +132,66 @@ TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) { ASSERT_FALSE(valueRead.has_value()); } +// If a struct is both trivially copyable and light flattenable we should treat it +// as LigthFlattenable. +TEST_F(FlattenableHelpersTest, TriviallyCopyableAndLightFlattenableIsFlattenedAsLightFlattenable) { + static constexpr int32_t kSizeTag = 1234567; + static constexpr int32_t kFlattenTag = 987654; + static constexpr int32_t kUnflattenTag = 5926582; + + struct LightFlattenableAndTriviallyCopyable + : LightFlattenable<LightFlattenableAndTriviallyCopyable> { + int32_t value; + + bool isFixedSize() const { return true; } + size_t getFlattenedSize() const { return kSizeTag; } + + status_t flatten(void* buffer, size_t size) const { + FlattenableUtils::write(buffer, size, kFlattenTag); + return OK; + } + + status_t unflatten(void const*, size_t) { + value = kUnflattenTag; + return OK; + } + }; + + { + // Verify that getFlattenedSize uses the LightFlattenable overload + LightFlattenableAndTriviallyCopyable foo; + EXPECT_EQ(kSizeTag, FlattenableHelpers::getFlattenedSize(foo)); + } + + { + // Verify that flatten uses the LightFlattenable overload + std::vector<int8_t> buffer(sizeof(int32_t)); + auto rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + LightFlattenableAndTriviallyCopyable foo; + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, foo)); + + auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + int32_t value; + FlattenableHelpers::unflatten(&rawReadBuffer, &size, &value); + EXPECT_EQ(kFlattenTag, value); + } + + { + // Verify that unflatten uses the LightFlattenable overload + std::vector<int8_t> buffer(sizeof(int32_t)); + auto rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + int32_t value = 4; + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + + auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + + LightFlattenableAndTriviallyCopyable foo; + FlattenableHelpers::unflatten(&rawReadBuffer, &size, &foo); + EXPECT_EQ(kUnflattenTag, foo.value); + } +} + } // namespace } // namespace android diff --git a/libs/ui/tools/Android.bp b/libs/ui/tools/Android.bp index fb46c2b20c..c28c303c0c 100644 --- a/libs/ui/tools/Android.bp +++ b/libs/ui/tools/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_libs_ui_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_libs_ui_license"], +} + cc_defaults { name: "libui_tools_default", clang_cflags: [ diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp index 49bc6bf067..83c250a24f 100644 --- a/libs/vibrator/Android.bp +++ b/libs/vibrator/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libvibrator", vendor_available: true, diff --git a/libs/vibrator/fuzzer/Android.bp b/libs/vibrator/fuzzer/Android.bp index 802015180e..f2a313cb8a 100644 --- a/libs/vibrator/fuzzer/Android.bp +++ b/libs/vibrator/fuzzer/Android.bp @@ -17,6 +17,15 @@ ***************************************************************************** */ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_fuzz { name: "vibrator_fuzzer", diff --git a/libs/vr/Android.bp b/libs/vr/Android.bp index e8176cf6b6..b308895faf 100644 --- a/libs/vr/Android.bp +++ b/libs/vr/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-BSD + // legacy_notice + default_applicable_licenses: ["frameworks_native_license"], +} + subdirs = [ "*", ] diff --git a/libs/vr/libbroadcastring/Android.bp b/libs/vr/libbroadcastring/Android.bp index 13af470a26..2eb2f9f149 100644 --- a/libs/vr/libbroadcastring/Android.bp +++ b/libs/vr/libbroadcastring/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libbroadcastring", clang: true, diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index 37c19d43f7..45bdd35519 100644 --- a/libs/vr/libbufferhub/Android.bp +++ b/libs/vr/libbufferhub/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libbufferhub_headers", export_include_dirs: ["include"], diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp index 77c79112de..f372bd79c2 100644 --- a/libs/vr/libbufferhubqueue/Android.bp +++ b/libs/vr/libbufferhubqueue/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + sourceFiles = [ "buffer_hub_queue_client.cpp", "buffer_hub_queue_parcelable.cpp", diff --git a/libs/vr/libbufferhubqueue/benchmarks/Android.bp b/libs/vr/libbufferhubqueue/benchmarks/Android.bp index ef1eed6d0a..fc1f376a73 100644 --- a/libs/vr/libbufferhubqueue/benchmarks/Android.bp +++ b/libs/vr/libbufferhubqueue/benchmarks/Android.bp @@ -1,4 +1,15 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_benchmark { srcs: ["buffer_transport_benchmark.cpp"], shared_libs: [ diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp index a33792177b..e883916f1a 100644 --- a/libs/vr/libbufferhubqueue/tests/Android.bp +++ b/libs/vr/libbufferhubqueue/tests/Android.bp @@ -1,4 +1,15 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + header_libraries = [ "libdvr_headers", ] diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp index 8c354fbc18..365a676bc4 100644 --- a/libs/vr/libdisplay/Android.bp +++ b/libs/vr/libdisplay/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + sourceFiles = [ "display_client.cpp", "display_manager_client.cpp", diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp index d5a19d3e6c..83c30d7010 100644 --- a/libs/vr/libdvr/Android.bp +++ b/libs/vr/libdvr/Android.bp @@ -13,6 +13,17 @@ // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libdvr_headers", export_include_dirs: ["include"], diff --git a/libs/vr/libdvr/tests/Android.bp b/libs/vr/libdvr/tests/Android.bp index 3260447390..4ed80a4929 100644 --- a/libs/vr/libdvr/tests/Android.bp +++ b/libs/vr/libdvr/tests/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { srcs: [ "dvr_display_manager-test.cpp", diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp index e75176846e..9e1e516724 100644 --- a/libs/vr/libdvrcommon/Android.bp +++ b/libs/vr/libdvrcommon/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + localIncludeFiles = [ "include", ] diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp index 24ba83048d..c1f6da3b10 100644 --- a/libs/vr/libpdx/Android.bp +++ b/libs/vr/libpdx/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libpdx_headers", export_include_dirs: ["private"], diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp index b36e0deea0..cc32b1822b 100644 --- a/libs/vr/libpdx/fuzz/Android.bp +++ b/libs/vr/libpdx/fuzz/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_fuzz { name: "libpdx_service_dispatcher_fuzzer", clang: true, diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp index b3534de101..ea73d7a9db 100644 --- a/libs/vr/libpdx_default_transport/Android.bp +++ b/libs/vr/libpdx_default_transport/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "pdx_default_transport_compiler_defaults", clang: true, @@ -75,4 +86,3 @@ cc_binary { "libpdx_default_transport", ], } - diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp index 1d6eea29a6..532d1a767c 100644 --- a/libs/vr/libpdx_uds/Android.bp +++ b/libs/vr/libpdx_uds/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libpdx_uds", clang: true, diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp index 35d3dea9de..5beee359b5 100644 --- a/libs/vr/libperformance/Android.bp +++ b/libs/vr/libperformance/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + sourceFiles = [ "performance_client.cpp", "performance_rpc.cpp", diff --git a/libs/vr/libvr_manager/Android.bp b/libs/vr/libvr_manager/Android.bp index 2cd6a28e2f..6f2ada4633 100644 --- a/libs/vr/libvr_manager/Android.bp +++ b/libs/vr/libvr_manager/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libvr_manager", srcs: [ diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp index abc64bde5a..8aca9a5adc 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + sourceFiles = [ "acquired_buffer.cpp", "epoll_event_dispatcher.cpp", diff --git a/libs/vr/libvrflinger/tests/Android.bp b/libs/vr/libvrflinger/tests/Android.bp index 7fafd3bf50..dafd35454f 100644 --- a/libs/vr/libvrflinger/tests/Android.bp +++ b/libs/vr/libvrflinger/tests/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + shared_libs = [ "android.hardware.configstore-utils", "android.hardware.configstore@1.0", diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp index 85427906f1..8f566a0a20 100644 --- a/libs/vr/libvrsensor/Android.bp +++ b/libs/vr/libvrsensor/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + sourceFiles = [ "pose_client.cpp", "latency_model.cpp", @@ -52,4 +63,3 @@ cc_library { header_libs: ["libdvr_headers"], name: "libvrsensor", } - diff --git a/opengl/Android.bp b/opengl/Android.bp index 8b94f616c6..3878cb1899 100644 --- a/opengl/Android.bp +++ b/opengl/Android.bp @@ -12,6 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-BSD + // SPDX-license-identifier-MIT + // legacy_notice + default_applicable_licenses: ["frameworks_native_license"], +} + ndk_headers { name: "libEGL_headers", from: "include", diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index 5d17561248..7861d62311 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -1,4 +1,13 @@ // Build the ETC1 library +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libETC1", srcs: ["ETC1/etc1.cpp"], diff --git a/opengl/tests/Android.bp b/opengl/tests/Android.bp index 639f351c69..da717bddf8 100644 --- a/opengl/tests/Android.bp +++ b/opengl/tests/Android.bp @@ -1,4 +1,16 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-BSD + // SPDX-license-identifier-MIT + // legacy_notice + default_applicable_licenses: ["frameworks_native_license"], +} + subdirs = [ "angeles", "configdump", @@ -16,4 +28,3 @@ subdirs = [ "hwc", "lib", ] - diff --git a/opengl/tests/EGLTest/Android.bp b/opengl/tests/EGLTest/Android.bp index e3912a84e6..51c937614f 100644 --- a/opengl/tests/EGLTest/Android.bp +++ b/opengl/tests/EGLTest/Android.bp @@ -1,4 +1,13 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "EGL_test", diff --git a/opengl/tests/angeles/Android.bp b/opengl/tests/angeles/Android.bp index 5c398a6323..5b81501241 100644 --- a/opengl/tests/angeles/Android.bp +++ b/opengl/tests/angeles/Android.bp @@ -1,5 +1,50 @@ // Copyright 2006 The Android Open Source Project +package { + default_applicable_licenses: [ + "frameworks_native_opengl_tests_angeles_license", + ], +} + +// Added automatically by a large-scale-change that took the approach of +// 'apply every license found to every target'. While this makes sure we respect +// every license restriction, it may not be entirely correct. +// +// e.g. GPL in an MIT project might only apply to the contrib/ directory. +// +// Please consider splitting the single license below into multiple licenses, +// taking care not to lose any license_kind information, and overriding the +// default license using the 'licenses: [...]' property on targets as needed. +// +// For unused files, consider creating a 'fileGroup' with "//visibility:private" +// to attach the license to, and including a comment whether the files may be +// used in the current project. +// +// large-scale-change included anything that looked like it might be a license +// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc. +// +// Please consider removing redundant or irrelevant files from 'license_text:'. +// +// large-scale-change filtered out the below license kinds as false-positives: +// SPDX-license-identifier-LGPL +// SPDX-license-identifier-LGPL-2.1 +// SPDX-license-identifier-LGPL-3.0 +// See: http://go/android-license-faq +license { + name: "frameworks_native_opengl_tests_angeles_license", + visibility: [":__subpackages__"], + license_kinds: [ + "SPDX-license-identifier-BSD", + "SPDX-license-identifier-MIT", + "legacy_notice", + ], + license_text: [ + "license-BSD.txt", + "license-LGPL.txt", + "license.txt", + ], +} + cc_test { name: "angeles", diff --git a/opengl/tests/configdump/Android.bp b/opengl/tests/configdump/Android.bp index ee96797030..ffb0c1f0c4 100644 --- a/opengl/tests/configdump/Android.bp +++ b/opengl/tests/configdump/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-configdump", diff --git a/opengl/tests/fillrate/Android.bp b/opengl/tests/fillrate/Android.bp index 689cee42d5..e4bff014fc 100644 --- a/opengl/tests/fillrate/Android.bp +++ b/opengl/tests/fillrate/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-fillrate", diff --git a/opengl/tests/filter/Android.bp b/opengl/tests/filter/Android.bp index 23241e1116..3b92b37634 100644 --- a/opengl/tests/filter/Android.bp +++ b/opengl/tests/filter/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-filter", diff --git a/opengl/tests/finish/Android.bp b/opengl/tests/finish/Android.bp index be20851e8f..c2dfbc35b9 100644 --- a/opengl/tests/finish/Android.bp +++ b/opengl/tests/finish/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-finish", diff --git a/opengl/tests/gl2_basic/Android.bp b/opengl/tests/gl2_basic/Android.bp index f4538adb54..c54bdf335d 100644 --- a/opengl/tests/gl2_basic/Android.bp +++ b/opengl/tests/gl2_basic/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-gl2_basic", diff --git a/opengl/tests/gl2_cameraeye/Android.bp b/opengl/tests/gl2_cameraeye/Android.bp index 00e00dfc91..6b8ee85d9c 100644 --- a/opengl/tests/gl2_cameraeye/Android.bp +++ b/opengl/tests/gl2_cameraeye/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "GL2CameraEye", // Only compile source java files in this apk. diff --git a/opengl/tests/gl2_copyTexImage/Android.bp b/opengl/tests/gl2_copyTexImage/Android.bp index 87fa7ea37d..0a84d254a5 100644 --- a/opengl/tests/gl2_copyTexImage/Android.bp +++ b/opengl/tests/gl2_copyTexImage/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-gl2_copyTexImage", diff --git a/opengl/tests/gl2_java/Android.bp b/opengl/tests/gl2_java/Android.bp index a8e5d7d70b..a33075e3b7 100644 --- a/opengl/tests/gl2_java/Android.bp +++ b/opengl/tests/gl2_java/Android.bp @@ -1,6 +1,15 @@ //######################################################################## // OpenGL ES 2.0 Java sample //######################################################################## +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "GL2Java", srcs: ["**/*.java"], diff --git a/opengl/tests/gl2_jni/Android.bp b/opengl/tests/gl2_jni/Android.bp index 8d4323f9ca..79773cb05f 100644 --- a/opengl/tests/gl2_jni/Android.bp +++ b/opengl/tests/gl2_jni/Android.bp @@ -3,6 +3,15 @@ // This makefile builds both an activity and a shared library. //######################################################################## +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "GL2JNI", srcs: ["**/*.java"], diff --git a/opengl/tests/gl2_yuvtex/Android.bp b/opengl/tests/gl2_yuvtex/Android.bp index b64d94d320..fadf0e8fc5 100644 --- a/opengl/tests/gl2_yuvtex/Android.bp +++ b/opengl/tests/gl2_yuvtex/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-gl2_yuvtex", diff --git a/opengl/tests/gl_basic/Android.bp b/opengl/tests/gl_basic/Android.bp index 5eed17e876..f777401b6f 100644 --- a/opengl/tests/gl_basic/Android.bp +++ b/opengl/tests/gl_basic/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-gl_basic", diff --git a/opengl/tests/gl_jni/Android.bp b/opengl/tests/gl_jni/Android.bp index 0cb129a117..dc46483551 100644 --- a/opengl/tests/gl_jni/Android.bp +++ b/opengl/tests/gl_jni/Android.bp @@ -4,6 +4,15 @@ //######################################################################## // Build activity +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "GLJNI", srcs: ["**/*.java"], diff --git a/opengl/tests/gl_perf/Android.bp b/opengl/tests/gl_perf/Android.bp index 25a317c3e3..ca0f7e8e33 100644 --- a/opengl/tests/gl_perf/Android.bp +++ b/opengl/tests/gl_perf/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-gl2_perf", diff --git a/opengl/tests/gl_perfapp/Android.bp b/opengl/tests/gl_perfapp/Android.bp index 66afb6a82c..2f623462cf 100644 --- a/opengl/tests/gl_perfapp/Android.bp +++ b/opengl/tests/gl_perfapp/Android.bp @@ -2,6 +2,15 @@ // OpenGL ES Perf App // This makefile builds both an activity and a shared library. //######################################################################## +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "GLPerf", srcs: ["**/*.java"], diff --git a/opengl/tests/gl_yuvtex/Android.bp b/opengl/tests/gl_yuvtex/Android.bp index 9b4924ab82..784418679c 100644 --- a/opengl/tests/gl_yuvtex/Android.bp +++ b/opengl/tests/gl_yuvtex/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-gl_yuvtex", diff --git a/opengl/tests/gldual/Android.bp b/opengl/tests/gldual/Android.bp index 1006d44e47..3d6e677f0d 100644 --- a/opengl/tests/gldual/Android.bp +++ b/opengl/tests/gldual/Android.bp @@ -4,6 +4,15 @@ //######################################################################## // Build activity +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "GLDual", srcs: ["**/*.java"], diff --git a/opengl/tests/gralloc/Android.bp b/opengl/tests/gralloc/Android.bp index 33c3dbaa17..5fb4556697 100644 --- a/opengl/tests/gralloc/Android.bp +++ b/opengl/tests/gralloc/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "test-opengl-gralloc", diff --git a/opengl/tests/hwc/Android.bp b/opengl/tests/hwc/Android.bp index 55f058f922..719eb114a1 100644 --- a/opengl/tests/hwc/Android.bp +++ b/opengl/tests/hwc/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "hwc_tests_defaults", diff --git a/opengl/tests/lib/Android.bp b/opengl/tests/lib/Android.bp index 2f6095d8e7..05c9397dfa 100644 --- a/opengl/tests/lib/Android.bp +++ b/opengl/tests/lib/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libglTest", diff --git a/opengl/tests/lighting1709/Android.bp b/opengl/tests/lighting1709/Android.bp index e734dd1d2c..79daa26096 100644 --- a/opengl/tests/lighting1709/Android.bp +++ b/opengl/tests/lighting1709/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_test { name: "LightingTest", srcs: ["**/*.java"], diff --git a/opengl/tests/linetex/Android.bp b/opengl/tests/linetex/Android.bp index dbc2cdbd44..61976e599f 100644 --- a/opengl/tests/linetex/Android.bp +++ b/opengl/tests/linetex/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "test-opengl-linetex", srcs: ["linetex.cpp"], diff --git a/opengl/tests/swapinterval/Android.bp b/opengl/tests/swapinterval/Android.bp index eed4dff3a5..a76f4cfb9a 100644 --- a/opengl/tests/swapinterval/Android.bp +++ b/opengl/tests/swapinterval/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "test-opengl-swapinterval", srcs: ["swapinterval.cpp"], diff --git a/opengl/tests/testFramerate/Android.bp b/opengl/tests/testFramerate/Android.bp index 5aa83b0753..4334d88749 100644 --- a/opengl/tests/testFramerate/Android.bp +++ b/opengl/tests/testFramerate/Android.bp @@ -2,6 +2,15 @@ // Test framerate and look for hiccups //######################################################################## +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "TestFramerate", srcs: ["**/*.java"], diff --git a/opengl/tests/testLatency/Android.bp b/opengl/tests/testLatency/Android.bp index c516dc3d17..473cb426cf 100644 --- a/opengl/tests/testLatency/Android.bp +++ b/opengl/tests/testLatency/Android.bp @@ -1,6 +1,15 @@ //######################################################################## // Test end-to-end latency. //######################################################################## +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "TestLatency", sdk_version: "8", diff --git a/opengl/tests/testPauseResume/Android.bp b/opengl/tests/testPauseResume/Android.bp index 810e895ce2..8171e1fc8e 100644 --- a/opengl/tests/testPauseResume/Android.bp +++ b/opengl/tests/testPauseResume/Android.bp @@ -1,4 +1,13 @@ // OpenGL ES JNI sample +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "TestEGL", srcs: ["**/*.java"], diff --git a/opengl/tests/testViewport/Android.bp b/opengl/tests/testViewport/Android.bp index 629b573a2a..13ce3ad481 100644 --- a/opengl/tests/testViewport/Android.bp +++ b/opengl/tests/testViewport/Android.bp @@ -2,6 +2,15 @@ // OpenGL ES JNI sample // This makefile builds both an activity and a shared library. //######################################################################## +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + android_app { name: "TestViewport", srcs: ["**/*.java"], diff --git a/opengl/tests/textures/Android.bp b/opengl/tests/textures/Android.bp index 84adda2aa4..f113ff78d2 100644 --- a/opengl/tests/textures/Android.bp +++ b/opengl/tests/textures/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "test-opengl-textures", srcs: ["textures.cpp"], diff --git a/opengl/tests/tritex/Android.bp b/opengl/tests/tritex/Android.bp index 390397b9da..759582cab6 100644 --- a/opengl/tests/tritex/Android.bp +++ b/opengl/tests/tritex/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "test-opengl-tritex", srcs: ["tritex.cpp"], diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp index 12ad47e15a..e6fb2c3660 100644 --- a/services/audiomanager/Android.bp +++ b/services/audiomanager/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libaudiomanager", diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp index c3da216002..72bd29254d 100644 --- a/services/automotive/display/Android.bp +++ b/services/automotive/display/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "android.frameworks.automotive.display@1.0-service", defaults: ["hidl_defaults"], diff --git a/services/batteryservice/Android.bp b/services/batteryservice/Android.bp index 66ee8ff55e..1e3799185e 100644 --- a/services/batteryservice/Android.bp +++ b/services/batteryservice/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libbatteryservice_headers", vendor_available: true, diff --git a/services/displayservice/Android.bp b/services/displayservice/Android.bp index 4d2d87352b..8681784405 100644 --- a/services/displayservice/Android.bp +++ b/services/displayservice/Android.bp @@ -14,6 +14,15 @@ // limitations under the License. // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libdisplayservicehidl", diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index 9a9bca1478..b9b6a19606 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "gpuservice_defaults", cflags: [ diff --git a/services/gpuservice/bpfprogs/Android.bp b/services/gpuservice/bpfprogs/Android.bp index b8758146cc..9842ed7c3c 100644 --- a/services/gpuservice/bpfprogs/Android.bp +++ b/services/gpuservice/bpfprogs/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + bpf { name: "gpu_mem.o", srcs: ["gpu_mem.c"], diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp index b2230b6c01..830e53d534 100644 --- a/services/gpuservice/gpumem/Android.bp +++ b/services/gpuservice/gpumem/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libgpumem", srcs: [ diff --git a/services/gpuservice/gpustats/Android.bp b/services/gpuservice/gpustats/Android.bp index f52602ab78..54291ad6c6 100644 --- a/services/gpuservice/gpustats/Android.bp +++ b/services/gpuservice/gpustats/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libgfxstats", srcs: [ diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp index 9606daacec..6d87c45921 100644 --- a/services/gpuservice/tests/unittests/Android.bp +++ b/services/gpuservice/tests/unittests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "gpuservice_unittest", test_suites: ["device-tests"], diff --git a/services/gpuservice/tracing/Android.bp b/services/gpuservice/tracing/Android.bp index 919fed3cec..a1bc1edad8 100644 --- a/services/gpuservice/tracing/Android.bp +++ b/services/gpuservice/tracing/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libgpumemtracer", srcs: [ diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index b640e9c761..9b98a17b4d 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // Default flags to be used throughout all libraries in inputflinger. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "inputflinger_defaults", cflags: [ diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index bd275a76c1..ea37f4d424 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_benchmark { name: "inputflinger_benchmarks", srcs: [ diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index e5b131e95e..393f649746 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libinputdispatcher_headers", export_include_dirs: [ diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 499f42e962..45a007ba6e 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -272,6 +272,7 @@ struct CommandEntry { std::string obscuringPackage; bool enabled; int32_t pid; + nsecs_t consumeTime; // time when the event was consumed by InputConsumer }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 290993935a..9898e9d820 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1673,9 +1673,7 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, if (!mKeyIsWaitingForEventsTimeout.has_value()) { // Start the timer - ALOGD("Waiting to send key to %s because there are unprocessed events that may cause " - "focus to change", - focusedWindowName); + // Wait to send key because there are unprocessed events that may cause focus to change mKeyIsWaitingForEventsTimeout = currentTime + std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT) .count(); @@ -2834,10 +2832,6 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio * This includes situations like the soft BACK button key. When the user releases (lifts up the * finger) the back button, then navigation bar will inject KEYCODE_BACK with ACTION_UP. * Both of those ACTION_UP events would not be logged - * Monitors (both gesture and global): any gesture monitors or global monitors receiving events - * will not be logged. This is omitted to reduce the amount of data printed. - * If you see <none>, it's likely that one of the gesture monitors pilfered the event, and therefore - * gesture monitor is the only connection receiving the remainder of the gesture. */ void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry, const std::vector<InputTarget>& targets) { @@ -2867,8 +2861,8 @@ void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry, sp<IBinder> token = target.inputChannel->getConnectionToken(); sp<Connection> connection = getConnectionLocked(token); - if (connection == nullptr || connection->monitor) { - continue; // We only need to keep track of the non-monitor connections. + if (connection == nullptr) { + continue; } newConnectionTokens.insert(std::move(token)); newConnections.emplace_back(connection); @@ -2878,12 +2872,12 @@ void InputDispatcher::updateInteractionTokensLocked(const EventEntry& entry, } mInteractionConnectionTokens = newConnectionTokens; - std::string windowList; + std::string targetList; for (const sp<Connection>& connection : newConnections) { - windowList += connection->getWindowName() + ", "; + targetList += connection->getWindowName() + ", "; } - std::string message = "Interaction with windows: " + windowList; - if (windowList.empty()) { + std::string message = "Interaction with: " + targetList; + if (targetList.empty()) { message += "<none>"; } android_log_event_list(LOGTAG_INPUT_INTERACTION) << message << LOG_ID_EVENTS; @@ -3113,7 +3107,7 @@ const std::array<uint8_t, 32> InputDispatcher::getSignature( void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, - bool handled) { + bool handled, nsecs_t consumeTime) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", connection->getInputChannelName().c_str(), seq, toString(handled)); @@ -3125,7 +3119,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, } // Notify other system components and prepare to start the next dispatch cycle. - onDispatchCycleFinishedLocked(currentTime, connection, seq, handled); + onDispatchCycleFinishedLocked(currentTime, connection, seq, handled, consumeTime); } void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, @@ -3196,13 +3190,15 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { bool gotOne = false; status_t status; for (;;) { - uint32_t seq; - bool handled; - status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled); + std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback = + std::bind(&InputDispatcher::finishDispatchCycleLocked, d, currentTime, + connection, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3); + + status = connection->inputPublisher.receiveFinishedSignal(callback); if (status) { break; } - d->finishDispatchCycleLocked(currentTime, connection, seq, handled); gotOne = true; } if (gotOne) { @@ -3219,8 +3215,7 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { } } else { // Monitor channels are never explicitly unregistered. - // We do it automatically when the remote endpoint is closed so don't warn - // about them. + // We do it automatically when the remote endpoint is closed so don't warn about them. const bool stillHaveWindowHandle = d->getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr; @@ -4976,7 +4971,7 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection bool notify) { sp<Connection> connection = getConnectionLocked(connectionToken); if (connection == nullptr) { - ALOGW("Attempted to unregister already unregistered input channel"); + // Connection can be removed via socket hang up or an explicit call to 'removeInputChannel' return BAD_VALUE; } @@ -5040,9 +5035,11 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { } TouchState& state = stateIt->second; + std::shared_ptr<InputChannel> requestingChannel; std::optional<int32_t> foundDeviceId; for (const TouchedMonitor& touchedMonitor : state.gestureMonitors) { if (touchedMonitor.monitor.inputChannel->getConnectionToken() == token) { + requestingChannel = touchedMonitor.monitor.inputChannel; foundDeviceId = state.deviceId; } } @@ -5058,13 +5055,19 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { "gesture monitor stole pointer stream"); options.deviceId = deviceId; options.displayId = displayId; + std::string canceledWindows = "["; for (const TouchedWindow& window : state.windows) { std::shared_ptr<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken()); if (channel != nullptr) { synthesizeCancelationEventsForInputChannelLocked(channel, options); + canceledWindows += channel->getName() + ", "; } } + canceledWindows += "]"; + ALOGI("Monitor %s is stealing touch from %s", requestingChannel->getName().c_str(), + canceledWindows.c_str()); + // Then clear the current touch state so we stop dispatching to them as well. state.filterNonMonitors(); } @@ -5154,13 +5157,14 @@ void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) { void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, - bool handled) { + bool handled, nsecs_t consumeTime) { std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( &InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; commandEntry->eventTime = currentTime; commandEntry->seq = seq; commandEntry->handled = handled; + commandEntry->consumeTime = consumeTime; postCommandLocked(std::move(commandEntry)); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index c93d74eb90..83094c20ba 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -528,7 +528,7 @@ private: void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - uint32_t seq, bool handled) REQUIRES(mLock); + uint32_t seq, bool handled, nsecs_t consumeTime) REQUIRES(mLock); void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool notify) REQUIRES(mLock); void drainDispatchQueue(std::deque<DispatchEntry*>& queue); @@ -578,7 +578,8 @@ private: // Interesting events that we might like to log or tell the framework about. void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection, - uint32_t seq, bool handled) REQUIRES(mLock); + uint32_t seq, bool handled, nsecs_t consumeTime) + REQUIRES(mLock); void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock); diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp index 9e797e4862..18d0226818 100644 --- a/services/inputflinger/host/Android.bp +++ b/services/inputflinger/host/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libinputflingerhost", diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 69aea84cd0..3bf212a51b 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -113,6 +113,10 @@ public: /* Get battery status of a particular input device. */ virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0; + virtual std::vector<int32_t> getLightIds(int32_t deviceId) = 0; + + virtual const InputDeviceLightInfo* getLightInfo(int32_t deviceId, int32_t lightId) = 0; + /* Return true if the device can send input events to the specified display. */ virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0; @@ -126,6 +130,15 @@ public: /* Flush sensor data in input reader mapper. */ virtual void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) = 0; + + /* Set color for the light */ + virtual bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) = 0; + /* Set player ID for the light */ + virtual bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) = 0; + /* Get light color */ + virtual std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) = 0; + /* Get light player ID */ + virtual std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) = 0; }; // --- InputReaderConfiguration --- diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 7f979f2bd3..8a2828c1b4 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libinputreader_headers", export_include_dirs: [ @@ -36,6 +45,7 @@ filegroup { "mapper/InputMapper.cpp", "mapper/JoystickInputMapper.cpp", "mapper/KeyboardInputMapper.cpp", + "mapper/LightInputMapper.cpp", "mapper/MultiTouchInputMapper.cpp", "mapper/RotaryEncoderInputMapper.cpp", "mapper/SensorInputMapper.cpp", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 8f8c0513b2..e939d1cb91 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -48,6 +48,7 @@ #include <utils/Timers.h> #include <filesystem> +#include <regex> #include "EventHub.h" @@ -84,6 +85,30 @@ static const std::unordered_map<std::string, int32_t> BATTERY_LEVEL = {{"Critica {"Full", 100}, {"Unknown", 50}}; +// Mapping for input led class node names lookup. +// https://www.kernel.org/doc/html/latest/leds/leds-class.html +static const std::unordered_map<std::string, InputLightClass> LIGHT_CLASSES = + {{"red", InputLightClass::RED}, + {"green", InputLightClass::GREEN}, + {"blue", InputLightClass::BLUE}, + {"global", InputLightClass::GLOBAL}, + {"brightness", InputLightClass::BRIGHTNESS}, + {"multi_index", InputLightClass::MULTI_INDEX}, + {"multi_intensity", InputLightClass::MULTI_INTENSITY}, + {"max_brightness", InputLightClass::MAX_BRIGHTNESS}}; + +// Mapping for input multicolor led class node names. +// https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html +static const std::unordered_map<InputLightClass, std::string> LIGHT_NODES = + {{InputLightClass::BRIGHTNESS, "brightness"}, + {InputLightClass::MULTI_INDEX, "multi_index"}, + {InputLightClass::MULTI_INTENSITY, "multi_intensity"}}; + +// Mapping for light color name and the light color +const std::unordered_map<std::string, LightColor> LIGHT_COLORS = {{"red", LightColor::RED}, + {"green", LightColor::GREEN}, + {"blue", LightColor::BLUE}}; + static inline const char* toString(bool value) { return value ? "true" : "false"; } @@ -151,14 +176,14 @@ static nsecs_t processEventTimestamp(const struct input_event& event) { * Returns the sysfs root path of the input device * */ -static std::filesystem::path getSysfsRootPath(const char* devicePath) { +static std::optional<std::filesystem::path> getSysfsRootPath(const char* devicePath) { std::error_code errorCode; // Stat the device path to get the major and minor number of the character file struct stat statbuf; if (stat(devicePath, &statbuf) == -1) { ALOGE("Could not stat device %s due to error: %s.", devicePath, std::strerror(errno)); - return std::filesystem::path(); + return std::nullopt; } unsigned int major_num = major(statbuf.st_rdev); @@ -173,7 +198,7 @@ static std::filesystem::path getSysfsRootPath(const char* devicePath) { if (errorCode) { ALOGW("Could not run filesystem::canonical() due to error %d : %s.", errorCode.value(), errorCode.message().c_str()); - return std::filesystem::path(); + return std::nullopt; } // Continue to go up a directory until we reach a directory named "input" @@ -192,26 +217,68 @@ static std::filesystem::path getSysfsRootPath(const char* devicePath) { } // Not found - return std::filesystem::path(); + return std::nullopt; } return sysfsPath; } /** - * Returns the power supply node in sys fs - * + * Returns the list of files under a specified path. */ -static std::filesystem::path findPowerSupplyNode(const std::filesystem::path& sysfsRootPath) { - for (auto path = sysfsRootPath; path != "/"; path = path.parent_path()) { - std::error_code errorCode; - auto iter = std::filesystem::directory_iterator(path / "power_supply", errorCode); - if (!errorCode && iter != std::filesystem::directory_iterator()) { - return iter->path(); +static std::vector<std::filesystem::path> allFilesInPath(const std::filesystem::path& path) { + std::vector<std::filesystem::path> nodes; + std::error_code errorCode; + auto iter = std::filesystem::directory_iterator(path, errorCode); + while (!errorCode && iter != std::filesystem::directory_iterator()) { + nodes.push_back(iter->path()); + iter++; + } + return nodes; +} + +/** + * Returns the list of files under a specified directory in a sysfs path. + * Example: + * findSysfsNodes(sysfsRootPath, SysfsClass::LEDS) will return all led nodes under "leds" directory + * in the sysfs path. + */ +static std::vector<std::filesystem::path> findSysfsNodes(const std::filesystem::path& sysfsRoot, + SysfsClass clazz) { + std::string nodeStr = NamedEnum::string(clazz); + std::for_each(nodeStr.begin(), nodeStr.end(), + [](char& c) { c = std::tolower(static_cast<unsigned char>(c)); }); + std::vector<std::filesystem::path> nodes; + for (auto path = sysfsRoot; path != "/" && nodes.empty(); path = path.parent_path()) { + nodes = allFilesInPath(path / nodeStr); + } + return nodes; +} + +static std::optional<std::array<LightColor, COLOR_NUM>> getColorIndexArray( + std::filesystem::path path) { + std::string indexStr; + if (!base::ReadFileToString(path, &indexStr)) { + return std::nullopt; + } + + // Parse the multi color LED index file, refer to kernel docs + // leds/leds-class-multicolor.html + std::regex indexPattern("(red|green|blue)\\s(red|green|blue)\\s(red|green|blue)[\\n]"); + std::smatch results; + std::array<LightColor, COLOR_NUM> colors; + if (!std::regex_match(indexStr, results, indexPattern)) { + return std::nullopt; + } + + for (size_t i = 1; i < results.size(); i++) { + const auto it = LIGHT_COLORS.find(results[i].str()); + if (it != LIGHT_COLORS.end()) { + // intensities.emplace(it->second, 0); + colors[i - 1] = it->second; } } - // Not found - return std::filesystem::path(); + return colors; } // --- Global Functions --- @@ -280,6 +347,7 @@ EventHub::Device::Device(int fd, int32_t id, const std::string& path, virtualKeyMap(nullptr), ffEffectPlaying(false), ffEffectId(-1), + nextLightId(0), controllerNumber(0), enabled(true), isVirtual(fd < 0) {} @@ -469,6 +537,70 @@ status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { return NAME_NOT_FOUND; } +// Check the sysfs path for any input device batteries, returns true if battery found. +bool EventHub::Device::configureBatteryLocked() { + if (!sysfsRootPath.has_value()) { + return false; + } + // Check if device has any batteries. + std::vector<std::filesystem::path> batteryPaths = + findSysfsNodes(sysfsRootPath.value(), SysfsClass::POWER_SUPPLY); + // We only support single battery for an input device, if multiple batteries exist only the + // first one is supported. + if (batteryPaths.empty()) { + // Set path to be empty + sysfsBatteryPath = std::nullopt; + return false; + } + // If a battery exists + sysfsBatteryPath = batteryPaths[0]; + return true; +} + +// Check the sysfs path for any input device lights, returns true if lights found. +bool EventHub::Device::configureLightsLocked() { + if (!sysfsRootPath.has_value()) { + return false; + } + // Check if device has any lights. + const auto& paths = findSysfsNodes(sysfsRootPath.value(), SysfsClass::LEDS); + for (const auto& nodePath : paths) { + RawLightInfo info; + info.id = ++nextLightId; + info.path = nodePath; + info.name = nodePath.filename(); + info.maxBrightness = std::nullopt; + size_t nameStart = info.name.rfind(":"); + if (nameStart != std::string::npos) { + // Trim the name to color name + info.name = info.name.substr(nameStart + 1); + // Set InputLightClass flag for colors + const auto it = LIGHT_CLASSES.find(info.name); + if (it != LIGHT_CLASSES.end()) { + info.flags |= it->second; + } + } + // Scan the path for all the files + // Refer to https://www.kernel.org/doc/Documentation/leds/leds-class.txt + const auto& files = allFilesInPath(nodePath); + for (const auto& file : files) { + const auto it = LIGHT_CLASSES.find(file.filename().string()); + if (it != LIGHT_CLASSES.end()) { + info.flags |= it->second; + // If the node has maximum brightness, read it + if (it->second == InputLightClass::MAX_BRIGHTNESS) { + std::string str; + if (base::ReadFileToString(file, &str)) { + info.maxBrightness = std::stoi(str); + } + } + } + } + lightInfos.insert_or_assign(info.id, info); + } + return !lightInfos.empty(); +} + /** * Get the capabilities for the current process. * Crashes the system if unable to create / check / destroy the capabilities object. @@ -829,6 +961,161 @@ base::Result<std::pair<InputDeviceSensorType, int32_t>> EventHub::mapSensor(int3 return Errorf("Device not found or device has no key layout."); } +const std::vector<int32_t> EventHub::getRawLightIds(int32_t deviceId) { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + std::vector<int32_t> lightIds; + + if (device != nullptr) { + for (const auto [id, info] : device->lightInfos) { + lightIds.push_back(id); + } + } + return lightIds; +} + +std::optional<RawLightInfo> EventHub::getRawLightInfo(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + + if (device != nullptr) { + auto it = device->lightInfos.find(lightId); + if (it != device->lightInfos.end()) { + return it->second; + } + } + return std::nullopt; +} + +std::optional<int32_t> EventHub::getLightBrightness(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + return std::nullopt; + } + + auto it = device->lightInfos.find(lightId); + if (it == device->lightInfos.end()) { + return std::nullopt; + } + std::string buffer; + if (!base::ReadFileToString(it->second.path / LIGHT_NODES.at(InputLightClass::BRIGHTNESS), + &buffer)) { + return std::nullopt; + } + return std::stoi(buffer); +} + +std::optional<std::unordered_map<LightColor, int32_t>> EventHub::getLightIntensities( + int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + return std::nullopt; + } + + auto lightIt = device->lightInfos.find(lightId); + if (lightIt == device->lightInfos.end()) { + return std::nullopt; + } + + auto ret = + getColorIndexArray(lightIt->second.path / LIGHT_NODES.at(InputLightClass::MULTI_INDEX)); + + if (!ret.has_value()) { + return std::nullopt; + } + std::array<LightColor, COLOR_NUM> colors = ret.value(); + + std::string intensityStr; + if (!base::ReadFileToString(lightIt->second.path / + LIGHT_NODES.at(InputLightClass::MULTI_INTENSITY), + &intensityStr)) { + return std::nullopt; + } + + // Intensity node outputs 3 color values + std::regex intensityPattern("([0-9]+)\\s([0-9]+)\\s([0-9]+)[\\n]"); + std::smatch results; + + if (!std::regex_match(intensityStr, results, intensityPattern)) { + return std::nullopt; + } + std::unordered_map<LightColor, int32_t> intensities; + for (size_t i = 1; i < results.size(); i++) { + int value = std::stoi(results[i].str()); + intensities.emplace(colors[i - 1], value); + } + return intensities; +} + +void EventHub::setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + ALOGE("Device Id %d does not exist", deviceId); + return; + } + auto lightIt = device->lightInfos.find(lightId); + if (lightIt == device->lightInfos.end()) { + ALOGE("Light Id %d does not exist.", lightId); + return; + } + + if (!base::WriteStringToFile(std::to_string(brightness), + lightIt->second.path / + LIGHT_NODES.at(InputLightClass::BRIGHTNESS))) { + ALOGE("Can not write to file, error: %s", strerror(errno)); + } +} + +void EventHub::setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map<LightColor, int32_t> intensities) { + std::scoped_lock _l(mLock); + + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + ALOGE("Device Id %d does not exist", deviceId); + return; + } + auto lightIt = device->lightInfos.find(lightId); + if (lightIt == device->lightInfos.end()) { + ALOGE("Light Id %d does not exist.", lightId); + return; + } + + auto ret = + getColorIndexArray(lightIt->second.path / LIGHT_NODES.at(InputLightClass::MULTI_INDEX)); + + if (!ret.has_value()) { + return; + } + std::array<LightColor, COLOR_NUM> colors = ret.value(); + + std::string rgbStr; + for (size_t i = 0; i < COLOR_NUM; i++) { + auto it = intensities.find(colors[i]); + if (it != intensities.end()) { + rgbStr += std::to_string(it->second); + // Insert space between colors + if (i < COLOR_NUM - 1) { + rgbStr += " "; + } + } + } + // Append new line + rgbStr += "\n"; + + if (!base::WriteStringToFile(rgbStr, + lightIt->second.path / + LIGHT_NODES.at(InputLightClass::MULTI_INTENSITY))) { + ALOGE("Can not write to file, error: %s", strerror(errno)); + } +} + void EventHub::setExcludedDevices(const std::vector<std::string>& devices) { std::scoped_lock _l(mLock); @@ -1068,18 +1355,18 @@ std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId) const { Device* device = getDeviceLocked(deviceId); std::string buffer; - if (!device || (device->sysfsBatteryPath.empty())) { + if (device == nullptr || !device->sysfsBatteryPath.has_value()) { return std::nullopt; } // Some devices report battery capacity as an integer through the "capacity" file - if (base::ReadFileToString(device->sysfsBatteryPath / "capacity", &buffer)) { + if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity", &buffer)) { return std::stoi(buffer); } // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX // These values are taken from kernel source code include/linux/power_supply.h - if (base::ReadFileToString(device->sysfsBatteryPath / "capacity_level", &buffer)) { + if (base::ReadFileToString(device->sysfsBatteryPath.value() / "capacity_level", &buffer)) { const auto it = BATTERY_LEVEL.find(buffer); if (it != BATTERY_LEVEL.end()) { return it->second; @@ -1093,11 +1380,11 @@ std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId) const { Device* device = getDeviceLocked(deviceId); std::string buffer; - if (!device || (device->sysfsBatteryPath.empty())) { + if (device == nullptr || !device->sysfsBatteryPath.has_value()) { return std::nullopt; } - if (!base::ReadFileToString(device->sysfsBatteryPath / "status", &buffer)) { + if (!base::ReadFileToString(device->sysfsBatteryPath.value() / "status", &buffer)) { ALOGE("Failed to read sysfs battery info: %s", strerror(errno)); return std::nullopt; } @@ -1572,6 +1859,12 @@ status_t EventHub::openDeviceLocked(const std::string& devicePath) { // Load the configuration file for the device. device->loadConfigurationLocked(); + // Grab the device's sysfs path + device->sysfsRootPath = getSysfsRootPath(devicePath.c_str()); + // find related components + bool hasBattery = device->configureBatteryLocked(); + bool hasLights = device->configureLightsLocked(); + // Figure out the kinds of events the device reports. device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask); device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask); @@ -1721,16 +2014,14 @@ status_t EventHub::openDeviceLocked(const std::string& devicePath) { return -1; } - // Grab the device's sysfs path - device->sysfsRootPath = getSysfsRootPath(devicePath.c_str()); - - if (!device->sysfsRootPath.empty()) { - device->sysfsBatteryPath = findPowerSupplyNode(device->sysfsRootPath); + // Classify InputDeviceClass::BATTERY. + if (hasBattery) { + device->classes |= InputDeviceClass::BATTERY; + } - // Check if a battery exists - if (!device->sysfsBatteryPath.empty()) { - device->classes |= InputDeviceClass::BATTERY; - } + // Classify InputDeviceClass::LIGHT. + if (hasLights) { + device->classes |= InputDeviceClass::LIGHT; } // Determine whether the device has a mic. diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 8fc6f4a701..cbf3b69f29 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -27,6 +27,7 @@ #include "InputReaderContext.h" #include "JoystickInputMapper.h" #include "KeyboardInputMapper.h" +#include "LightInputMapper.h" #include "MultiTouchInputMapper.h" #include "RotaryEncoderInputMapper.h" #include "SensorInputMapper.h" @@ -161,9 +162,22 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr)); } - // Battery-like devices. + // Battery-like devices. Only one battery mapper for each EventHub device. if (classes.test(InputDeviceClass::BATTERY)) { - mappers.push_back(std::make_unique<BatteryInputMapper>(*contextPtr)); + InputDeviceInfo deviceInfo; + getDeviceInfo(&deviceInfo); + if (!deviceInfo.hasBattery()) { + mappers.push_back(std::make_unique<BatteryInputMapper>(*contextPtr)); + } + } + + // Light-containing devices. Only one light mapper for each EventHub device. + if (classes.test(InputDeviceClass::LIGHT)) { + InputDeviceInfo deviceInfo; + getDeviceInfo(&deviceInfo); + if (deviceInfo.getLightIds().empty()) { + mappers.push_back(std::make_unique<LightInputMapper>(*contextPtr)); + } } // Keyboard-like devices. @@ -505,6 +519,32 @@ std::optional<int32_t> InputDevice::getBatteryStatus() { return first_in_mappers<int32_t>([](InputMapper& mapper) { return mapper.getBatteryStatus(); }); } +bool InputDevice::setLightColor(int32_t lightId, int32_t color) { + bool success = true; + for_each_mapper([&success, lightId, color](InputMapper& mapper) { + success &= mapper.setLightColor(lightId, color); + }); + return success; +} + +bool InputDevice::setLightPlayerId(int32_t lightId, int32_t playerId) { + bool success = true; + for_each_mapper([&success, lightId, playerId](InputMapper& mapper) { + success &= mapper.setLightPlayerId(lightId, playerId); + }); + return success; +} + +std::optional<int32_t> InputDevice::getLightColor(int32_t lightId) { + return first_in_mappers<int32_t>( + [lightId](InputMapper& mapper) { return mapper.getLightColor(lightId); }); +} + +std::optional<int32_t> InputDevice::getLightPlayerId(int32_t lightId) { + return first_in_mappers<int32_t>( + [lightId](InputMapper& mapper) { return mapper.getLightPlayerId(lightId); }); +} + int32_t InputDevice::getMetaState() { int32_t result = 0; for_each_mapper([&result](InputMapper& mapper) { result |= mapper.getMetaState(); }); @@ -520,7 +560,8 @@ void InputDevice::bumpGeneration() { } void InputDevice::notifyReset(nsecs_t when) { - mContext->notifyDeviceReset(when, mId); + NotifyDeviceResetArgs args(mContext->getNextId(), when, mId); + mContext->getListener()->notifyDeviceReset(&args); } std::optional<int32_t> InputDevice::getAssociatedDisplayId() { diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index c044393c9e..2d0fdf7958 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -339,7 +339,8 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) { updateGlobalMetaStateLocked(); // Enqueue configuration changed. - mContext.notifyConfigurationChanged(when); + NotifyConfigurationChangedArgs args(mContext.getNextId(), when); + mQueuedListener->notifyConfigurationChanged(&args); } void InputReader::refreshConfigurationLocked(uint32_t changes) { @@ -366,7 +367,9 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { } if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) { - mContext.notifyPointerCaptureChanged(now, mConfig.pointerCapture); + const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now, + mConfig.pointerCapture); + mQueuedListener->notifyPointerCaptureChanged(&args); } } @@ -693,6 +696,70 @@ std::optional<int32_t> InputReader::getBatteryStatus(int32_t deviceId) { return std::nullopt; } +std::vector<int32_t> InputReader::getLightIds(int32_t deviceId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + InputDeviceInfo info; + device->getDeviceInfo(&info); + return info.getLightIds(); + } + return {}; +} + +const InputDeviceLightInfo* InputReader::getLightInfo(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + InputDeviceInfo info; + device->getDeviceInfo(&info); + return info.getLightInfo(lightId); + } + return nullptr; +} + +bool InputReader::setLightColor(int32_t deviceId, int32_t lightId, int32_t color) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->setLightColor(lightId, color); + } + return false; +} + +bool InputReader::setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->setLightPlayerId(lightId, playerId); + } + return false; +} + +std::optional<int32_t> InputReader::getLightColor(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->getLightColor(lightId); + } + return std::nullopt; +} + +std::optional<int32_t> InputReader::getLightPlayerId(int32_t deviceId, int32_t lightId) { + std::scoped_lock _l(mLock); + + InputDevice* device = findInputDeviceLocked(deviceId); + if (device) { + return device->getLightPlayerId(lightId); + } + return std::nullopt; +} + bool InputReader::isInputDeviceEnabled(int32_t deviceId) { std::scoped_lock _l(mLock); @@ -885,69 +952,16 @@ InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { return mReader->mPolicy.get(); } -EventHubInterface* InputReader::ContextImpl::getEventHub() { - return mReader->mEventHub.get(); -} - -void InputReader::ContextImpl::notifyConfigurationChanged(nsecs_t when) { - NotifyConfigurationChangedArgs args(mIdGenerator.nextId(), when); - mReader->mQueuedListener->notifyConfigurationChanged(&args); +InputListenerInterface* InputReader::ContextImpl::getListener() { + return mReader->mQueuedListener.get(); } -void InputReader::ContextImpl::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t flags, int32_t keyCode, int32_t scanCode, - int32_t metaState, nsecs_t downTime) { - NotifyKeyArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId, policyFlags, - action, flags, keyCode, scanCode, metaState, downTime); - mReader->mQueuedListener->notifyKey(&args); -} -void InputReader::ContextImpl::notifyMotion( - nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xPrecision, float yPrecision, - float xCursorPosition, float yCursorPosition, nsecs_t downTime, - const std::vector<TouchVideoFrame>& videoFrames) { - NotifyMotionArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId, - policyFlags, action, actionButton, flags, metaState, buttonState, - classification, edgeFlags, pointerCount, pointerProperties, pointerCoords, - xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime, - videoFrames); - mReader->mQueuedListener->notifyMotion(&args); -} - -void InputReader::ContextImpl::notifySensor(nsecs_t when, int32_t deviceId, - InputDeviceSensorType sensorType, - InputDeviceSensorAccuracy accuracy, - bool accuracyChanged, nsecs_t timestamp, - std::vector<float> values) { - NotifySensorArgs args(mIdGenerator.nextId(), when, deviceId, AINPUT_SOURCE_SENSOR, sensorType, - accuracy, accuracyChanged, timestamp, std::move(values)); - mReader->mQueuedListener->notifySensor(&args); -} - -void InputReader::ContextImpl::notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) { - NotifyVibratorStateArgs args(mIdGenerator.nextId(), when, deviceId, isOn); - mReader->mQueuedListener->notifyVibratorState(&args); -} - -void InputReader::ContextImpl::notifySwitch(nsecs_t eventTime, uint32_t switchValues, - uint32_t switchMask) { - NotifySwitchArgs args(mIdGenerator.nextId(), eventTime, 0 /*policyFlags*/, switchValues, - switchMask); - mReader->mQueuedListener->notifySwitch(&args); -} - -void InputReader::ContextImpl::notifyDeviceReset(nsecs_t when, int32_t deviceId) { - NotifyDeviceResetArgs args(mIdGenerator.nextId(), when, deviceId); - mReader->mQueuedListener->notifyDeviceReset(&args); +EventHubInterface* InputReader::ContextImpl::getEventHub() { + return mReader->mEventHub.get(); } -void InputReader::ContextImpl::notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) { - const NotifyPointerCaptureChangedArgs args(mIdGenerator.nextId(), when, hasCapture); - mReader->mQueuedListener->notifyPointerCaptureChanged(&args); +int32_t InputReader::ContextImpl::getNextId() { + return mIdGenerator.nextId(); } } // namespace android diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 30967dfdf2..e6164d38d4 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -47,6 +47,8 @@ namespace android { +/* Number of colors : {red, green, blue} */ +static constexpr size_t COLOR_NUM = 3; /* * A raw event as retrieved from the EventHub. */ @@ -127,6 +129,9 @@ enum class InputDeviceClass : uint32_t { /* The input device has a battery */ BATTERY = 0x00004000, + /* The input device has sysfs controllable lights */ + LIGHT = 0x00008000, + /* The input device is virtual (not a real device, not part of UI configuration). */ VIRTUAL = 0x40000000, @@ -134,6 +139,46 @@ enum class InputDeviceClass : uint32_t { EXTERNAL = 0x80000000, }; +enum class SysfsClass : uint32_t { + POWER_SUPPLY = 0, + LEDS = 1, +}; + +enum class LightColor : uint32_t { + RED = 0, + GREEN = 1, + BLUE = 2, +}; + +enum class InputLightClass : uint32_t { + /* The input light has brightness node. */ + BRIGHTNESS = 0x00000001, + /* The input light has red name. */ + RED = 0x00000002, + /* The input light has green name. */ + GREEN = 0x00000004, + /* The input light has blue name. */ + BLUE = 0x00000008, + /* The input light has global name. */ + GLOBAL = 0x00000010, + /* The input light has multi index node. */ + MULTI_INDEX = 0x00000020, + /* The input light has multi intensity node. */ + MULTI_INTENSITY = 0x00000040, + /* The input light has max brightness node. */ + MAX_BRIGHTNESS = 0x00000080, +}; + +/* Describes a raw light. */ +struct RawLightInfo { + int32_t id; + std::string name; + std::optional<int32_t> maxBrightness; + Flags<InputLightClass> flags; + std::array<int32_t, COLOR_NUM> rgbIndex; + std::filesystem::path path; +}; + /* * Gets the class that owns an axis, in cases where multiple classes might claim * the same axis for different purposes. @@ -214,7 +259,16 @@ public: virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) = 0; virtual base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor(int32_t deviceId, int32_t absCode) = 0; - + // Raw lights are sysfs led light nodes we found from the EventHub device sysfs node, + // containing the raw info of the sysfs node structure. + virtual const std::vector<int32_t> getRawLightIds(int32_t deviceId) = 0; + virtual std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) = 0; + virtual std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) = 0; + virtual void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) = 0; + virtual std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( + int32_t deviceId, int32_t lightId) = 0; + virtual void setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map<LightColor, int32_t> intensities) = 0; /* * Query current input state. */ @@ -377,6 +431,17 @@ public: base::Result<std::pair<InputDeviceSensorType, int32_t>> mapSensor( int32_t deviceId, int32_t absCode) override final; + const std::vector<int32_t> getRawLightIds(int32_t deviceId) override final; + + std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override final; + + std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override final; + void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override final; + std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( + int32_t deviceId, int32_t lightId) override final; + void setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map<LightColor, int32_t> intensities) override final; + void setExcludedDevices(const std::vector<std::string>& devices) override final; int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final; @@ -458,9 +523,12 @@ private: bool ffEffectPlaying; int16_t ffEffectId; // initially -1 - // The paths are invalid when .empty() returns true - std::filesystem::path sysfsRootPath; - std::filesystem::path sysfsBatteryPath; + // The paths are invalid when they're std::nullopt + std::optional<std::filesystem::path> sysfsRootPath; + std::optional<std::filesystem::path> sysfsBatteryPath; + // maps from light id to light info + std::unordered_map<int32_t, RawLightInfo> lightInfos; + int32_t nextLightId; int32_t controllerNumber; @@ -491,6 +559,8 @@ private: void setLedForControllerLocked(); status_t mapLed(int32_t led, int32_t* outScanCode) const; void setLedStateLocked(int32_t led, bool on); + bool configureBatteryLocked(); + bool configureLightsLocked(); }; status_t openDeviceLocked(const std::string& devicePath); diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index e4186c86cd..34c330b89c 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -95,6 +95,11 @@ public: std::optional<int32_t> getBatteryCapacity(); std::optional<int32_t> getBatteryStatus(); + bool setLightColor(int32_t lightId, int32_t color); + bool setLightPlayerId(int32_t lightId, int32_t playerId); + std::optional<int32_t> getLightColor(int32_t lightId); + std::optional<int32_t> getLightPlayerId(int32_t lightId); + int32_t getMetaState(); void updateMetaState(int32_t keyCode); @@ -254,6 +259,30 @@ public: return mEventHub->mapSensor(mId, absCode); } + inline const std::vector<int32_t> getRawLightIds() { return mEventHub->getRawLightIds(mId); } + + inline std::optional<RawLightInfo> getRawLightInfo(int32_t lightId) { + return mEventHub->getRawLightInfo(mId, lightId); + } + + inline std::optional<int32_t> getLightBrightness(int32_t lightId) { + return mEventHub->getLightBrightness(mId, lightId); + } + + inline void setLightBrightness(int32_t lightId, int32_t brightness) { + return mEventHub->setLightBrightness(mId, lightId, brightness); + } + + inline std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( + int32_t lightId) { + return mEventHub->getLightIntensities(mId, lightId); + } + + inline void setLightIntensities(int32_t lightId, + std::unordered_map<LightColor, int32_t> intensities) { + return mEventHub->setLightIntensities(mId, lightId, intensities); + } + inline std::vector<TouchVideoFrame> getVideoFrames() { return mEventHub->getVideoFrames(mId); } inline int32_t getScanCodeState(int32_t scanCode) const { return mEventHub->getScanCodeState(mId, scanCode); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 5f78149d57..1405671a50 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -99,6 +99,18 @@ public: std::optional<int32_t> getBatteryStatus(int32_t deviceId) override; + std::vector<int32_t> getLightIds(int32_t deviceId) override; + + const InputDeviceLightInfo* getLightInfo(int32_t deviceId, int32_t lightId) override; + + bool setLightColor(int32_t deviceId, int32_t lightId, int32_t color) override; + + bool setLightPlayerId(int32_t deviceId, int32_t lightId, int32_t playerId) override; + + std::optional<int32_t> getLightColor(int32_t deviceId, int32_t lightId) override; + + std::optional<int32_t> getLightPlayerId(int32_t deviceId, int32_t lightId) override; + protected: // These members are protected so they can be instrumented by test cases. virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId, @@ -131,31 +143,11 @@ protected: void dispatchExternalStylusState(const StylusState& outState) NO_THREAD_SAFETY_ANALYSIS override; InputReaderPolicyInterface* getPolicy() NO_THREAD_SAFETY_ANALYSIS override; + InputListenerInterface* getListener() NO_THREAD_SAFETY_ANALYSIS override; EventHubInterface* getEventHub() NO_THREAD_SAFETY_ANALYSIS override; + int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override; void updateLedMetaState(int32_t metaState) NO_THREAD_SAFETY_ANALYSIS override; int32_t getLedMetaState() NO_THREAD_SAFETY_ANALYSIS override; - - // Send events to InputListener interface - void notifyConfigurationChanged(nsecs_t when) override; - void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, nsecs_t downTime) override; - void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, - MotionClassification classification, int32_t edgeFlags, - uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xPrecision, float yPrecision, - float xCursorPosition, float yCursorPosition, nsecs_t downTime, - const std::vector<TouchVideoFrame>& videoFrames) override; - void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) override; - void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType, - InputDeviceSensorAccuracy accuracy, bool accuracyChanged, - nsecs_t timestamp, std::vector<float> values) override; - void notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) override; - void notifyDeviceReset(nsecs_t when, int32_t deviceId) override; - void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) override; - } mContext; friend class ContextImpl; diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index e6ea523c4f..dc807f7886 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -55,34 +55,13 @@ public: virtual void dispatchExternalStylusState(const StylusState& outState) = 0; virtual InputReaderPolicyInterface* getPolicy() = 0; + virtual InputListenerInterface* getListener() = 0; virtual EventHubInterface* getEventHub() = 0; + virtual int32_t getNextId() = 0; + virtual void updateLedMetaState(int32_t metaState) = 0; virtual int32_t getLedMetaState() = 0; - - // Send events to InputListener interface - - virtual void notifyConfigurationChanged(nsecs_t when) = 0; - virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0; - virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t actionButton, int32_t flags, int32_t metaState, - int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, - nsecs_t downTime, - const std::vector<TouchVideoFrame>& videoFrames) = 0; - virtual void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) = 0; - virtual void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType, - InputDeviceSensorAccuracy accuracy, bool accuracyChanged, - nsecs_t timestamp, std::vector<float> values) = 0; - virtual void notifyVibratorState(nsecs_t when, int32_t deviceId, bool isOn) = 0; - virtual void notifyDeviceReset(nsecs_t when, int32_t deviceId) = 0; - virtual void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp index afdc5abdc2..e4fb3a6c9f 100644 --- a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp +++ b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp @@ -24,7 +24,7 @@ BatteryInputMapper::BatteryInputMapper(InputDeviceContext& deviceContext) : InputMapper(deviceContext) {} uint32_t BatteryInputMapper::getSources() { - return 0; + return AINPUT_SOURCE_UNKNOWN; } void BatteryInputMapper::populateDeviceInfo(InputDeviceInfo* info) { diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 7f7b33cc3e..254b64b4d9 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -175,7 +175,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } bumpGeneration(); if (changes) { - getContext()->notifyDeviceReset(when, getDeviceId()); + NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); + getListener()->notifyDeviceReset(&args); } } @@ -382,35 +383,40 @@ void CursorInputMapper::sync(nsecs_t when) { while (!released.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); + NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&releaseArgs); } } - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - motionEventAction, 0, 0, metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); if (buttonsPressed) { BitSet32 pressed(buttonsPressed); while (!pressed.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&pressArgs); } } @@ -418,12 +424,13 @@ void CursorInputMapper::sync(nsecs_t when) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, + NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + 0, metaState, currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&hoverArgs); } // Send scroll events. @@ -431,12 +438,13 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); + NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); } } diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index 44af998693..bd543e5798 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -19,6 +19,7 @@ #include "EventHub.h" #include "InputDevice.h" +#include "InputListener.h" #include "InputReaderContext.h" #include "StylusState.h" #include "VibrationElement.h" @@ -47,6 +48,7 @@ public: inline const std::string getDeviceName() { return mDeviceContext.getName(); } inline InputReaderContext* getContext() { return mDeviceContext.getContext(); } inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); } + inline InputListenerInterface* getListener() { return getContext()->getListener(); } virtual uint32_t getSources() = 0; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); @@ -75,6 +77,11 @@ public: virtual std::optional<int32_t> getBatteryCapacity() { return std::nullopt; } virtual std::optional<int32_t> getBatteryStatus() { return std::nullopt; } + virtual bool setLightColor(int32_t lightId, int32_t color) { return true; } + virtual bool setLightPlayerId(int32_t lightId, int32_t playerId) { return true; } + virtual std::optional<int32_t> getLightColor(int32_t lightId) { return std::nullopt; } + virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) { return std::nullopt; } + virtual int32_t getMetaState(); virtual void updateMetaState(int32_t keyCode); diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 28f29e0a0a..37aa140c16 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -337,12 +337,13 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { // TODO: Use the input device configuration to control this behavior more finely. uint32_t policyFlags = 0; - getContext()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, - policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &pointerProperties, &pointerCoords, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, + ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&args); } void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 03d740529f..8b9f235438 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -350,9 +350,10 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } - getContext()->notifyKey(when, getDeviceId(), mSource, getDisplayId(), policyFlags, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); + NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(), + policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); + getListener()->notifyKey(&args); } ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { diff --git a/services/inputflinger/reader/mapper/LightInputMapper.cpp b/services/inputflinger/reader/mapper/LightInputMapper.cpp new file mode 100644 index 0000000000..be1f722e2e --- /dev/null +++ b/services/inputflinger/reader/mapper/LightInputMapper.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <locale> +#include <regex> + +#include "../Macros.h" + +#include "LightInputMapper.h" +#include "input/NamedEnum.h" + +// Log detailed debug messages about input device lights. +static constexpr bool DEBUG_LIGHT_DETAILS = false; + +namespace android { + +static inline int32_t getAlpha(int32_t color) { + return (color >> 24) & 0xff; +} + +static inline int32_t getRed(int32_t color) { + return (color >> 16) & 0xff; +} + +static inline int32_t getGreen(int32_t color) { + return (color >> 8) & 0xff; +} + +static inline int32_t getBlue(int32_t color) { + return color & 0xff; +} + +static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) { + return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff); +} + +/** + * Light input mapper owned by InputReader device, implements the native API for querying input + * lights, getting and setting the lights brightness and color, by interacting with EventHub + * devices. + * TODO b/180342233: Reconsider the inputflinger design to accommodate the device class + * like lights and battery. + */ +LightInputMapper::LightInputMapper(InputDeviceContext& deviceContext) + : InputMapper(deviceContext) {} + +LightInputMapper::~LightInputMapper() {} + +std::optional<std::int32_t> LightInputMapper::Light::getRawLightBrightness(int32_t rawLightId) { + std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId); + std::optional<int32_t> ret = context.getLightBrightness(rawLightId); + if (!rawInfo.has_value() || !ret.has_value()) { + return std::nullopt; + } + int brightness = ret.value(); + + // If the light node doesn't have max brightness, use the default max brightness. + int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS); + float ratio = MAX_BRIGHTNESS / rawMaxBrightness; + // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255] + if (rawMaxBrightness != MAX_BRIGHTNESS) { + brightness = brightness * ratio; + } + if (DEBUG_LIGHT_DETAILS) { + ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId, + brightness, ratio); + } + return brightness; +} + +void LightInputMapper::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) { + std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId); + if (!rawInfo.has_value()) { + return; + } + // If the light node doesn't have max brightness, use the default max brightness. + int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS); + float ratio = MAX_BRIGHTNESS / rawMaxBrightness; + // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness] + if (rawMaxBrightness != MAX_BRIGHTNESS) { + brightness = ceil(brightness / ratio); + } + if (DEBUG_LIGHT_DETAILS) { + ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId, + brightness, ratio); + } + context.setLightBrightness(rawLightId, brightness); +} + +bool LightInputMapper::SingleLight::setLightColor(int32_t color) { + int32_t brightness = getAlpha(color); + setRawLightBrightness(rawId, brightness); + + return true; +} + +bool LightInputMapper::RgbLight::setLightColor(int32_t color) { + // Compose color value as per: + // https://developer.android.com/reference/android/graphics/Color?hl=en + // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff); + // The alpha component is used to scale the R,G,B leds brightness, with the ratio to + // MAX_BRIGHTNESS. + brightness = getAlpha(color); + int32_t red = 0; + int32_t green = 0; + int32_t blue = 0; + if (brightness > 0) { + float ratio = MAX_BRIGHTNESS / brightness; + red = ceil(getRed(color) / ratio); + green = ceil(getGreen(color) / ratio); + blue = ceil(getBlue(color) / ratio); + } + setRawLightBrightness(rawRgbIds.at(LightColor::RED), red); + setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green); + setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue); + if (rawGlobalId.has_value()) { + setRawLightBrightness(rawGlobalId.value(), brightness); + } + + return true; +} + +bool LightInputMapper::MultiColorLight::setLightColor(int32_t color) { + std::unordered_map<LightColor, int32_t> intensities; + intensities.emplace(LightColor::RED, getRed(color)); + intensities.emplace(LightColor::GREEN, getGreen(color)); + intensities.emplace(LightColor::BLUE, getBlue(color)); + + context.setLightIntensities(rawId, intensities); + setRawLightBrightness(rawId, getAlpha(color)); + return true; +} + +std::optional<int32_t> LightInputMapper::SingleLight::getLightColor() { + std::optional<int32_t> brightness = getRawLightBrightness(rawId); + if (!brightness.has_value()) { + return std::nullopt; + } + + return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */); +} + +std::optional<int32_t> LightInputMapper::RgbLight::getLightColor() { + // If the Alpha component is zero, then return color 0. + if (brightness == 0) { + return 0; + } + // Compose color value as per: + // https://developer.android.com/reference/android/graphics/Color?hl=en + // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff); + std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED)); + std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN)); + std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE)); + // If we can't get brightness for any of the RGB light + if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) { + return std::nullopt; + } + + // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha + // value, scale it back to return the nominal color value. + float ratio = MAX_BRIGHTNESS / brightness; + int32_t red = round(redOr.value() * ratio); + int32_t green = round(greenOr.value() * ratio); + int32_t blue = round(blueOr.value() * ratio); + + if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) { + // Previously stored brightness isn't valid for current LED values, so just reset to max + // brightness since an app couldn't have provided these values in the first place. + red = redOr.value(); + green = greenOr.value(); + blue = blueOr.value(); + brightness = MAX_BRIGHTNESS; + } + + return toArgb(brightness, red, green, blue); +} + +std::optional<int32_t> LightInputMapper::MultiColorLight::getLightColor() { + auto ret = context.getLightIntensities(rawId); + if (!ret.has_value()) { + return std::nullopt; + } + std::unordered_map<LightColor, int32_t> intensities = ret.value(); + // Get red, green, blue colors + int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */, + intensities.at(LightColor::GREEN) /* green */, + intensities.at(LightColor::BLUE) /* blue */); + // Get brightness + std::optional<int32_t> brightness = getRawLightBrightness(rawId); + if (brightness.has_value()) { + return toArgb(brightness.value() /* A */, 0, 0, 0) | color; + } + return std::nullopt; +} + +bool LightInputMapper::PlayerIdLight::setLightPlayerId(int32_t playerId) { + if (rawLightIds.find(playerId) == rawLightIds.end()) { + return false; + } + for (const auto& [id, rawId] : rawLightIds) { + if (playerId == id) { + setRawLightBrightness(rawId, MAX_BRIGHTNESS); + } else { + setRawLightBrightness(rawId, 0); + } + } + return true; +} + +std::optional<int32_t> LightInputMapper::PlayerIdLight::getLightPlayerId() { + for (const auto& [id, rawId] : rawLightIds) { + std::optional<int32_t> brightness = getRawLightBrightness(rawId); + if (brightness.has_value() && brightness.value() > 0) { + return id; + } + } + return std::nullopt; +} + +void LightInputMapper::SingleLight::dump(std::string& dump) { + dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0)); +} + +void LightInputMapper::PlayerIdLight::dump(std::string& dump) { + dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1)); + dump += StringPrintf(INDENT4 "Raw Player ID LEDs:"); + for (const auto& [id, rawId] : rawLightIds) { + dump += StringPrintf("id %d -> %d ", id, rawId); + } + dump += "\n"; +} + +void LightInputMapper::RgbLight::dump(std::string& dump) { + dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0)); + dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED), + rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE)); + if (rawGlobalId.has_value()) { + dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value()); + } + dump += "\n"; +} + +void LightInputMapper::MultiColorLight::dump(std::string& dump) { + dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0)); +} + +uint32_t LightInputMapper::getSources() { + return AINPUT_SOURCE_UNKNOWN; +} + +void LightInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + for (const auto& [lightId, light] : mLights) { + // Input device light doesn't support ordinal, always pass 1. + InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */); + info->addLightInfo(lightInfo); + } +} + +void LightInputMapper::dump(std::string& dump) { + dump += INDENT2 "Light Input Mapper:\n"; + dump += INDENT3 "Lights:\n"; + for (const auto& [lightId, light] : mLights) { + dump += StringPrintf(INDENT4 "Id: %d", lightId); + dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str()); + dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str()); + light->dump(dump); + } + // Dump raw lights + dump += INDENT3 "RawLights:\n"; + dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n"; + const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds(); + // Map from raw light id to raw light info + std::unordered_map<int32_t, RawLightInfo> rawInfos; + for (const auto& rawId : rawLightIds) { + std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId); + if (!rawInfo.has_value()) { + continue; + } + dump += StringPrintf(INDENT4 "%d", rawId); + dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str()); + dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str()); + dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS)); + dump += StringPrintf(INDENT4 "%d\n", + getDeviceContext().getLightBrightness(rawId).value_or(-1)); + } +} + +void LightInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + bool hasRedLed = false; + bool hasGreenLed = false; + bool hasBlueLed = false; + std::optional<int32_t> rawGlobalId = std::nullopt; + // Player ID light common name string + std::string playerIdName; + // Raw RGB color to raw light ID + std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds; + // Map from player Id to raw light Id + std::unordered_map<int32_t, int32_t> playerIdLightIds; + mLights.clear(); + + // Check raw lights + const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds(); + // Map from raw light id to raw light info + std::unordered_map<int32_t, RawLightInfo> rawInfos; + for (const auto& rawId : rawLightIds) { + std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId); + if (!rawInfo.has_value()) { + continue; + } + rawInfos.insert_or_assign(rawId, rawInfo.value()); + // Check if this is a group LEDs for player ID + std::regex lightPattern("([a-z]+)([0-9]+)"); + std::smatch results; + if (std::regex_match(rawInfo->name, results, lightPattern)) { + std::string commonName = results[1].str(); + int32_t playerId = std::stoi(results[2]); + if (playerIdLightIds.empty()) { + playerIdName = commonName; + playerIdLightIds.insert_or_assign(playerId, rawId); + } else { + // Make sure the player ID leds have common string name + if (playerIdName.compare(commonName) == 0 && + playerIdLightIds.find(playerId) == playerIdLightIds.end()) { + playerIdLightIds.insert_or_assign(playerId, rawId); + } + } + } + // Check if this is an LED of RGB light + if (rawInfo->flags.test(InputLightClass::RED)) { + hasRedLed = true; + rawRgbIds.emplace(LightColor::RED, rawId); + } + if (rawInfo->flags.test(InputLightClass::GREEN)) { + hasGreenLed = true; + rawRgbIds.emplace(LightColor::GREEN, rawId); + } + if (rawInfo->flags.test(InputLightClass::BLUE)) { + hasBlueLed = true; + rawRgbIds.emplace(LightColor::BLUE, rawId); + } + if (rawInfo->flags.test(InputLightClass::GLOBAL)) { + rawGlobalId = rawId; + } + if (DEBUG_LIGHT_DETAILS) { + ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id, + rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS), + rawInfo->flags.string().c_str()); + } + } + + // Construct a player ID light + if (playerIdLightIds.size() > 1) { + std::unique_ptr<Light> light = + std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId, + playerIdLightIds); + mLights.insert_or_assign(light->id, std::move(light)); + // Remove these raw lights from raw light info as they've been used to compose a + // Player ID light, so we do not expose these raw lights as single lights. + for (const auto& [playerId, rawId] : playerIdLightIds) { + rawInfos.erase(rawId); + } + } + // Construct a RGB light for composed RGB light + if (hasRedLed && hasGreenLed && hasBlueLed) { + if (DEBUG_LIGHT_DETAILS) { + ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED), + rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE)); + } + std::unique_ptr<Light> light = std::make_unique<RgbLight>(getDeviceContext(), ++mNextId, + rawRgbIds, rawGlobalId); + mLights.insert_or_assign(light->id, std::move(light)); + // Remove from raw light info as they've been composed a RBG light. + rawInfos.erase(rawRgbIds.at(LightColor::RED)); + rawInfos.erase(rawRgbIds.at(LightColor::GREEN)); + rawInfos.erase(rawRgbIds.at(LightColor::BLUE)); + if (rawGlobalId.has_value()) { + rawInfos.erase(rawGlobalId.value()); + } + } + + // Check the rest of raw light infos + for (const auto& [rawId, rawInfo] : rawInfos) { + // If the node is multi-color led, construct a MULTI_COLOR light + if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) && + rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) { + if (DEBUG_LIGHT_DETAILS) { + ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); + } + std::unique_ptr<Light> light = + std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, + ++mNextId, rawInfo.id); + mLights.insert_or_assign(light->id, std::move(light)); + continue; + } + // Construct a single LED light + if (DEBUG_LIGHT_DETAILS) { + ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str()); + } + std::unique_ptr<Light> light = + std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId, + rawInfo.id); + + mLights.insert_or_assign(light->id, std::move(light)); + } + } +} + +void LightInputMapper::reset(nsecs_t when) { + InputMapper::reset(when); +} + +void LightInputMapper::process(const RawEvent* rawEvent) {} + +bool LightInputMapper::setLightColor(int32_t lightId, int32_t color) { + auto it = mLights.find(lightId); + if (it == mLights.end()) { + return false; + } + auto& light = it->second; + if (DEBUG_LIGHT_DETAILS) { + ALOGD("setLightColor lightId %d type %s color 0x%x", lightId, + NamedEnum::string(light->type).c_str(), color); + } + return light->setLightColor(color); +} + +std::optional<int32_t> LightInputMapper::getLightColor(int32_t lightId) { + auto it = mLights.find(lightId); + if (it == mLights.end()) { + return std::nullopt; + } + auto& light = it->second; + std::optional<int32_t> color = light->getLightColor(); + if (DEBUG_LIGHT_DETAILS) { + ALOGD("getLightColor lightId %d type %s color 0x%x", lightId, + NamedEnum::string(light->type).c_str(), color.value_or(0)); + } + return color; +} + +bool LightInputMapper::setLightPlayerId(int32_t lightId, int32_t playerId) { + auto it = mLights.find(lightId); + if (it == mLights.end()) { + return false; + } + auto& light = it->second; + return light->setLightPlayerId(playerId); +} + +std::optional<int32_t> LightInputMapper::getLightPlayerId(int32_t lightId) { + auto it = mLights.find(lightId); + if (it == mLights.end()) { + return std::nullopt; + } + auto& light = it->second; + return light->getLightPlayerId(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/LightInputMapper.h b/services/inputflinger/reader/mapper/LightInputMapper.h new file mode 100644 index 0000000000..9254720385 --- /dev/null +++ b/services/inputflinger/reader/mapper/LightInputMapper.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H +#define _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class LightInputMapper : public InputMapper { + // Refer to https://developer.android.com/reference/kotlin/android/graphics/Color + /* Number of colors : {red, green, blue} */ + static constexpr size_t COLOR_NUM = 3; + static constexpr int32_t MAX_BRIGHTNESS = 0xff; + +public: + explicit LightInputMapper(InputDeviceContext& deviceContext); + ~LightInputMapper() override; + + uint32_t getSources() override; + void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + void dump(std::string& dump) override; + void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) override; + void reset(nsecs_t when) override; + void process(const RawEvent* rawEvent) override; + bool setLightColor(int32_t lightId, int32_t color) override; + bool setLightPlayerId(int32_t lightId, int32_t playerId) override; + std::optional<int32_t> getLightColor(int32_t lightId) override; + std::optional<int32_t> getLightPlayerId(int32_t lightId) override; + +private: + struct Light { + explicit Light(InputDeviceContext& context, std::string name, int32_t id, + InputDeviceLightType type) + : context(context), name(name), id(id), type(type) {} + virtual ~Light() {} + InputDeviceContext& context; + std::string name; + int32_t id; + InputDeviceLightType type; + + virtual bool setLightColor(int32_t color) { return false; } + virtual std::optional<int32_t> getLightColor() { return std::nullopt; } + virtual bool setLightPlayerId(int32_t playerId) { return false; } + virtual std::optional<int32_t> getLightPlayerId() { return std::nullopt; } + + virtual void dump(std::string& dump) {} + + std::optional<std::int32_t> getRawLightBrightness(int32_t rawLightId); + void setRawLightBrightness(int32_t rawLightId, int32_t brightness); + }; + + struct SingleLight : public Light { + explicit SingleLight(InputDeviceContext& context, std::string name, int32_t id, + int32_t rawId) + : Light(context, name, id, InputDeviceLightType::SINGLE), rawId(rawId) {} + int32_t rawId; + + bool setLightColor(int32_t color) override; + std::optional<int32_t> getLightColor() override; + void dump(std::string& dump) override; + }; + + struct RgbLight : public Light { + explicit RgbLight(InputDeviceContext& context, int32_t id, + std::unordered_map<LightColor, int32_t> rawRgbIds, + std::optional<int32_t> rawGlobalId) + : Light(context, "RGB", id, InputDeviceLightType::RGB), + rawRgbIds(rawRgbIds), + rawGlobalId(rawGlobalId) { + brightness = rawGlobalId.has_value() + ? getRawLightBrightness(rawGlobalId.value()).value_or(MAX_BRIGHTNESS) + : MAX_BRIGHTNESS; + } + // Map from color to raw light id. + std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds; + // Optional global control raw light id. + std::optional<int32_t> rawGlobalId; + int32_t brightness; + + bool setLightColor(int32_t color) override; + std::optional<int32_t> getLightColor() override; + void dump(std::string& dump) override; + }; + + struct MultiColorLight : public Light { + explicit MultiColorLight(InputDeviceContext& context, std::string name, int32_t id, + int32_t rawId) + : Light(context, name, id, InputDeviceLightType::MULTI_COLOR), rawId(rawId) {} + int32_t rawId; + + bool setLightColor(int32_t color) override; + std::optional<int32_t> getLightColor() override; + void dump(std::string& dump) override; + }; + + struct PlayerIdLight : public Light { + explicit PlayerIdLight(InputDeviceContext& context, std::string name, int32_t id, + std::unordered_map<int32_t, int32_t> rawLightIds) + : Light(context, name, id, InputDeviceLightType::PLAYER_ID), + rawLightIds(rawLightIds) {} + // Map from player Id to raw light Id + std::unordered_map<int32_t, int32_t> rawLightIds; + + bool setLightPlayerId(int32_t palyerId) override; + std::optional<int32_t> getLightPlayerId() override; + void dump(std::string& dump) override; + }; + + int32_t mNextId = 0; + + // Light color map from light color to the color index. + static const std::unordered_map<std::string, size_t> LIGHT_COLORS; + + // Light map from light ID to Light + std::unordered_map<int32_t, std::unique_ptr<Light>> mLights; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_LIGHT_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index 3f8a36433f..594ff42f87 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -121,12 +121,13 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { int32_t metaState = getContext()->getGlobalMetaState(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); } mRotaryEncoderScrollAccumulator.finishSync(); diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index 68c1e40357..7ac2dec895 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -405,10 +405,13 @@ void SensorInputMapper::sync(nsecs_t when, bool force) { // Convert to Android unit convertFromLinuxToAndroid(values, sensorType); // Notify dispatcher for sensor event - getContext()->notifySensor(when, getDeviceId(), sensorType, sensor.sensorInfo.accuracy, - sensor.accuracy != - sensor.sensorInfo.accuracy /* accuracyChanged */, - timestamp /* hwTimestamp */, std::move(values)); + NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(), + AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy, + sensor.accuracy != + sensor.sensorInfo.accuracy /* accuracyChanged */, + timestamp /* hwTimestamp */, values); + + getListener()->notifySensor(&args); sensor.lastSampleTimeNs = timestamp; sensor.accuracy = sensor.sensorInfo.accuracy; } diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 07de244c31..4f736810bc 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -56,7 +56,10 @@ void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { void SwitchInputMapper::sync(nsecs_t when) { if (mUpdatedSwitchMask) { uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - getContext()->notifySwitch(when, updatedSwitchValues, mUpdatedSwitchMask); + NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/, + updatedSwitchValues, mUpdatedSwitchMask); + getListener()->notifySwitch(&args); + mUpdatedSwitchMask = 0; } } diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index ff6341fbdb..a86443dee1 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -66,8 +66,9 @@ static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nse (currentButtonState & buttonState)) || (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && !(currentButtonState & buttonState))) { - context->notifyKey(when, deviceId, source, displayId, policyFlags, action, 0 /*flags*/, - keyCode, 0 /*scanCode*/, context->getGlobalMetaState(), when); + NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags, + action, 0, keyCode, 0, context->getGlobalMetaState(), when); + context->getListener()->notifyKey(&args); } } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 1a17bef399..d1df37b686 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -397,7 +397,8 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c if (changes && resetNeeded) { // Send reset, unless this is the first time the device has been configured, // in which case the reader will call reset itself after all mappers are ready. - getContext()->notifyDeviceReset(when, getDeviceId()); + NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); + getListener()->notifyDeviceReset(&args); } } @@ -1845,9 +1846,10 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t metaState = getContext()->getGlobalMetaState(); policyFlags |= POLICY_FLAG_VIRTUAL; - getContext()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId, - policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, - metaState, downTime); + NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, + mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, + scanCode, metaState, downTime); + getListener()->notifyKey(&args); } void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { @@ -2526,11 +2528,12 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); const int32_t displayId = mPointerController->getDisplayId(); - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &pointerProperties, &pointerCoords, 0, 0, x, y, - mPointerGesture.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + 1, &pointerProperties, &pointerCoords, 0, 0, x, y, + mPointerGesture.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); } // Update state. @@ -3445,28 +3448,28 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.down = false; // Send up. - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_UP, 0, 0, metaState, - mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, - /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); } if (mPointerSimple.hovering && !hovering) { mPointerSimple.hovering = false; // Send hover exit. - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, - mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, - /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); } if (down) { @@ -3475,24 +3478,25 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); } // Send move. - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); } if (hovering) { @@ -3500,24 +3504,25 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); } // Send hover move. - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); } if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { @@ -3532,14 +3537,14 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &pointerCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); + NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, + policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); } // Save state. @@ -3610,11 +3615,12 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames(); std::for_each(frames.begin(), frames.end(), [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - getContext()->notifyMotion(when, deviceId, source, displayId, policyFlags, action, actionButton, - flags, metaState, buttonState, MotionClassification::NONE, edgeFlags, - pointerCount, pointerProperties, pointerCoords, xPrecision, - yPrecision, xCursorPosition, yCursorPosition, downTime, - std::move(frames)); + NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags, + action, actionButton, flags, metaState, buttonState, + MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, + pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, + downTime, std::move(frames)); + getListener()->notifyMotion(&args); } bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 2e4ab45b35..3df6f36db5 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -53,7 +53,8 @@ void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t rep mIndex = -1; // Request InputReader to notify InputManagerService for vibration started. - getContext()->notifyVibratorState(systemTime(), getDeviceId(), true); + NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), true); + getListener()->notifyVibratorState(&args); nextStep(); } @@ -131,7 +132,8 @@ void VibratorInputMapper::stopVibrating() { getDeviceContext().cancelVibrate(); // Request InputReader to notify InputManagerService for vibration complete. - getContext()->notifyVibratorState(systemTime(), getDeviceId(), false); + NotifyVibratorStateArgs args(getContext()->getNextId(), systemTime(), getDeviceId(), false); + getListener()->notifyVibratorState(&args); } void VibratorInputMapper::dump(std::string& dump) { diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp index fbc51dadc0..74307310c5 100644 --- a/services/inputflinger/reporter/Android.bp +++ b/services/inputflinger/reporter/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "libinputreporter_headers", export_include_dirs: ["."], @@ -46,4 +55,3 @@ cc_library_shared { "libinputreporter_headers", ], } - diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 959dadc9ef..42b54c75b6 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "inputflinger_tests", defaults: [ diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 01945dbbf1..209639c880 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -31,6 +31,7 @@ using android::base::StringPrintf; using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; +using android::os::TouchOcclusionMode; using namespace android::flag_operators; namespace android::inputdispatcher { @@ -727,8 +728,9 @@ public: ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } - void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, - int32_t expectedFlags) { + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, + std::optional<int32_t> expectedDisplayId, + std::optional<int32_t> expectedFlags) { InputEvent* event = consume(); ASSERT_NE(nullptr, event) << mName.c_str() @@ -737,19 +739,25 @@ public: << mName.c_str() << " expected " << inputEventTypeToString(expectedEventType) << " event, got " << inputEventTypeToString(event->getType()) << " event"; - EXPECT_EQ(expectedDisplayId, event->getDisplayId()); + if (expectedDisplayId.has_value()) { + EXPECT_EQ(expectedDisplayId, event->getDisplayId()); + } switch (expectedEventType) { case AINPUT_EVENT_TYPE_KEY: { const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event); EXPECT_EQ(expectedAction, keyEvent.getAction()); - EXPECT_EQ(expectedFlags, keyEvent.getFlags()); + if (expectedFlags.has_value()) { + EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags()); + } break; } case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event); EXPECT_EQ(expectedAction, motionEvent.getAction()); - EXPECT_EQ(expectedFlags, motionEvent.getFlags()); + if (expectedFlags.has_value()) { + EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags()); + } break; } case AINPUT_EVENT_TYPE_FOCUS: { @@ -854,6 +862,7 @@ public: mInfo.name = name; mInfo.type = InputWindowInfo::Type::APPLICATION; mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; + mInfo.alpha = 1.0; mInfo.frameLeft = 0; mInfo.frameTop = 0; mInfo.frameRight = WIDTH; @@ -883,6 +892,12 @@ public: void setPaused(bool paused) { mInfo.paused = paused; } + void setAlpha(float alpha) { mInfo.alpha = alpha; } + + void setTouchOcclusionMode(android::os::TouchOcclusionMode mode) { + mInfo.touchOcclusionMode = mode; + } + void setFrame(const Rect& frame) { mInfo.frameLeft = frame.left; mInfo.frameTop = frame.top; @@ -928,6 +943,11 @@ public: void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, int32_t expectedFlags = 0) { + consumeAnyMotionDown(expectedDisplayId, expectedFlags); + } + + void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt, + std::optional<int32_t> expectedFlags = std::nullopt) { consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); } @@ -971,8 +991,9 @@ public: mInputReceiver->consumeCaptureEvent(hasCapture); } - void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId, - int32_t expectedFlags) { + void consumeEvent(int32_t expectedEventType, int32_t expectedAction, + std::optional<int32_t> expectedDisplayId, + std::optional<int32_t> expectedFlags) { ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver"; mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId, expectedFlags); @@ -4240,4 +4261,264 @@ TEST_F(InputDispatcherPointerCaptureTests, OutOfOrderRequests) { mSecondWindow->consumeCaptureEvent(true); } +class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest { +protected: + constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8; + static const int32_t TOUCHED_APP_UID = 10001; + static const int32_t APP_B_UID = 10002; + static const int32_t APP_C_UID = 10003; + + sp<FakeWindowHandle> mTouchWindow; + + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + mTouchWindow = getWindow(TOUCHED_APP_UID, "Touched"); + mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK); + mDispatcher->setMaximumObscuringOpacityForTouch(MAXIMUM_OBSCURING_OPACITY); + } + + virtual void TearDown() override { + InputDispatcherTest::TearDown(); + mTouchWindow.clear(); + } + + sp<FakeWindowHandle> getOccludingWindow(int32_t uid, std::string name, + os::TouchOcclusionMode mode, float alpha = 1.0f) { + sp<FakeWindowHandle> window = getWindow(uid, name); + window->setFlags(InputWindowInfo::Flag::NOT_TOUCHABLE); + window->setTouchOcclusionMode(mode); + window->setAlpha(alpha); + return window; + } + + sp<FakeWindowHandle> getWindow(int32_t uid, std::string name) { + std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(app, mDispatcher, name, ADISPLAY_ID_DEFAULT); + // Generate an arbitrary PID based on the UID + window->setOwnerInfo(1777 + (uid % 10000), uid); + return window; + } + + void touch(const std::vector<PointF>& points = {PointF{100, 200}}) { + NotifyMotionArgs args = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, points); + mDispatcher->notifyMotion(&args); + } +}; + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithBlockUntrustedOcclusionMode_BlocksTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithAllowOcclusionMode_AllowsTouch) { + const sp<FakeWindowHandle>& w = getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::ALLOW); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, TouchOutsideOccludingWindow_AllowsTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED); + w->setFrame(Rect(0, 0, 50, 50)); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch({PointF{100, 100}}); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowFromSameUid_AllowsTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(TOUCHED_APP_UID, "A", TouchOcclusionMode::BLOCK_UNTRUSTED); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithZeroOpacity_AllowsTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::BLOCK_UNTRUSTED, 0.0f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityBelowThreshold_AllowsTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAtThreshold_AllowsTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, + MAXIMUM_OBSCURING_OPACITY); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityAboveThreshold_BlocksTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowsWithCombinedOpacityAboveThreshold_BlocksTouch) { + // Resulting opacity = 1 - (1 - 0.7)*(1 - 0.7) = .91 + const sp<FakeWindowHandle>& w1 = + getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY, 0.7f); + const sp<FakeWindowHandle>& w2 = + getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY, 0.7f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}}); + + touch(); + + mTouchWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowsWithCombinedOpacityBelowThreshold_AllowsTouch) { + // Resulting opacity = 1 - (1 - 0.5)*(1 - 0.5) = .75 + const sp<FakeWindowHandle>& w1 = + getOccludingWindow(APP_B_UID, "B1", TouchOcclusionMode::USE_OPACITY, 0.5f); + const sp<FakeWindowHandle>& w2 = + getOccludingWindow(APP_B_UID, "B2", TouchOcclusionMode::USE_OPACITY, 0.5f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w1, w2, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, + WindowsFromDifferentAppsEachBelowThreshold_AllowsTouch) { + const sp<FakeWindowHandle>& wB = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f); + const sp<FakeWindowHandle>& wC = + getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY, 0.7f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, WindowsFromDifferentAppsOneAboveThreshold_BlocksTouch) { + const sp<FakeWindowHandle>& wB = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f); + const sp<FakeWindowHandle>& wC = + getOccludingWindow(APP_C_UID, "C", TouchOcclusionMode::USE_OPACITY, 0.9f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wB, wC, mTouchWindow}}}); + + touch(); + + mTouchWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, + WindowWithOpacityAboveThresholdAndSelfWindow_BlocksTouch) { + const sp<FakeWindowHandle>& wA = + getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.7f); + const sp<FakeWindowHandle>& wB = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}}); + + touch(); + + mTouchWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, + WindowWithOpacityBelowThresholdAndSelfWindow_AllowsTouch) { + const sp<FakeWindowHandle>& wA = + getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.9f); + const sp<FakeWindowHandle>& wB = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.7f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wA, wB, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, SelfWindowWithOpacityAboveThreshold_AllowsTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::USE_OPACITY, 0.9f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, SelfWindowWithBlockUntrustedMode_AllowsTouch) { + const sp<FakeWindowHandle>& w = + getOccludingWindow(TOUCHED_APP_UID, "T", TouchOcclusionMode::BLOCK_UNTRUSTED); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, + OpacityThresholdIs0AndWindowAboveThreshold_BlocksTouch) { + mDispatcher->setMaximumObscuringOpacityForTouch(0.0f); + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.1f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, OpacityThresholdIs0AndWindowAtThreshold_AllowsTouch) { + mDispatcher->setMaximumObscuringOpacityForTouch(0.0f); + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.0f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + +TEST_F(InputDispatcherUntrustedTouchesTest, + OpacityThresholdIs1AndWindowBelowThreshold_AllowsTouch) { + mDispatcher->setMaximumObscuringOpacityForTouch(1.0f); + const sp<FakeWindowHandle>& w = + getOccludingWindow(APP_B_UID, "B", TouchOcclusionMode::USE_OPACITY, 0.9f); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {w, mTouchWindow}}}); + + touch(); + + mTouchWindow->consumeAnyMotionDown(); +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 409c62a0e2..2836516a41 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -22,6 +22,7 @@ #include <InputReaderBase.h> #include <InputReaderFactory.h> #include <KeyboardInputMapper.h> +#include <LightInputMapper.h> #include <MultiTouchInputMapper.h> #include <SensorInputMapper.h> #include <SingleTouchInputMapper.h> @@ -36,6 +37,7 @@ #include <math.h> #include <memory> +#include <regex> #include "input/DisplayViewport.h" #include "input/Input.h" @@ -70,6 +72,9 @@ static constexpr int32_t SECOND_TRACKING_ID = 1; static constexpr int32_t THIRD_TRACKING_ID = 2; static constexpr int32_t BATTERY_STATUS = 4; static constexpr int32_t BATTERY_CAPACITY = 66; +static constexpr int32_t LIGHT_BRIGHTNESS = 0x55000000; +static constexpr int32_t LIGHT_COLOR = 0x7F448866; +static constexpr int32_t LIGHT_PLAYER_ID = 2; // Error tolerance for floating point assertions. static const float EPSILON = 0.001f; @@ -83,6 +88,10 @@ static inline float avg(float x, float y) { return (x + y) / 2; } +// Mapping for light color name and the light color +const std::unordered_map<std::string, LightColor> LIGHT_COLORS = {{"red", LightColor::RED}, + {"green", LightColor::GREEN}, + {"blue", LightColor::BLUE}}; // --- FakePointerController --- @@ -412,6 +421,12 @@ class FakeEventHub : public EventHubInterface { std::vector<RawEvent> mEvents GUARDED_BY(mLock); std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames; std::vector<int32_t> mVibrators = {0, 1}; + std::unordered_map<int32_t, RawLightInfo> mRawLightInfos; + // Simulates a device light brightness, from light id to light brightness. + std::unordered_map<int32_t /* lightId */, int32_t /* brightness*/> mLightBrightness; + // Simulates a device light intensities, from light id to light intensities map. + std::unordered_map<int32_t /* lightId */, std::unordered_map<LightColor, int32_t>> + mLightIntensities; public: virtual ~FakeEventHub() { @@ -562,6 +577,19 @@ public: device->mscBitmask.loadFromBuffer(buffer); } + void addRawLightInfo(int32_t rawId, RawLightInfo&& info) { + mRawLightInfos.emplace(rawId, std::move(info)); + } + + void fakeLightBrightness(int32_t rawId, int32_t brightness) { + mLightBrightness.emplace(rawId, brightness); + } + + void fakeLightIntensities(int32_t rawId, + const std::unordered_map<LightColor, int32_t> intensities) { + mLightIntensities.emplace(rawId, std::move(intensities)); + } + bool getLedState(int32_t deviceId, int32_t led) { Device* device = getDevice(deviceId); return device->leds.valueFor(led); @@ -869,6 +897,48 @@ private: std::optional<int32_t> getBatteryStatus(int32_t) const override { return BATTERY_STATUS; } + const std::vector<int32_t> getRawLightIds(int32_t deviceId) override { + std::vector<int32_t> ids; + for (const auto& [rawId, info] : mRawLightInfos) { + ids.push_back(rawId); + } + return ids; + } + + std::optional<RawLightInfo> getRawLightInfo(int32_t deviceId, int32_t lightId) override { + auto it = mRawLightInfos.find(lightId); + if (it == mRawLightInfos.end()) { + return std::nullopt; + } + return it->second; + } + + void setLightBrightness(int32_t deviceId, int32_t lightId, int32_t brightness) override { + mLightBrightness.emplace(lightId, brightness); + } + + void setLightIntensities(int32_t deviceId, int32_t lightId, + std::unordered_map<LightColor, int32_t> intensities) override { + mLightIntensities.emplace(lightId, intensities); + }; + + std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) override { + auto lightIt = mLightBrightness.find(lightId); + if (lightIt == mLightBrightness.end()) { + return std::nullopt; + } + return lightIt->second; + } + + std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( + int32_t deviceId, int32_t lightId) override { + auto lightIt = mLightIntensities.find(lightId); + if (lightIt == mLightIntensities.end()) { + return std::nullopt; + } + return lightIt->second; + }; + virtual bool isExternal(int32_t) const { return false; } @@ -1976,6 +2046,49 @@ TEST_F(InputReaderTest, BatteryGetStatus) { ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS); } +class FakeLightInputMapper : public FakeInputMapper { +public: + FakeLightInputMapper(InputDeviceContext& deviceContext, uint32_t sources) + : FakeInputMapper(deviceContext, sources) {} + + bool setLightColor(int32_t lightId, int32_t color) override { + getDeviceContext().setLightBrightness(lightId, color >> 24); + return true; + } + + std::optional<int32_t> getLightColor(int32_t lightId) override { + std::optional<int32_t> result = getDeviceContext().getLightBrightness(lightId); + if (!result.has_value()) { + return std::nullopt; + } + return result.value() << 24; + } +}; + +TEST_F(InputReaderTest, LightGetColor) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::LIGHT; + constexpr int32_t eventHubId = 1; + const char* DEVICE_LOCATION = "BLUETOOTH"; + std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION); + FakeLightInputMapper& mapper = + device->addMapper<FakeLightInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD); + mReader->pushNextDevice(device); + RawLightInfo info = {.id = 1, + .name = "Mono", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + mFakeEventHub->addRawLightInfo(1 /* rawId */, std::move(info)); + mFakeEventHub->fakeLightBrightness(1 /* rawId */, 0x55); + + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled()); + + ASSERT_TRUE(mReader->setLightColor(deviceId, 1 /* lightId */, LIGHT_BRIGHTNESS)); + ASSERT_EQ(mReader->getLightColor(deviceId, 1 /* lightId */), LIGHT_BRIGHTNESS); +} + // --- InputReaderIntegrationTest --- // These tests create and interact with the InputReader only through its interface. @@ -2883,14 +2996,136 @@ TEST_F(BatteryInputMapperTest, GetBatteryCapacity) { BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>(); ASSERT_TRUE(mapper.getBatteryCapacity()); - ASSERT_EQ(*mapper.getBatteryCapacity(), BATTERY_CAPACITY); + ASSERT_EQ(mapper.getBatteryCapacity().value_or(-1), BATTERY_CAPACITY); } TEST_F(BatteryInputMapperTest, GetBatteryStatus) { BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>(); ASSERT_TRUE(mapper.getBatteryStatus()); - ASSERT_EQ(*mapper.getBatteryStatus(), BATTERY_STATUS); + ASSERT_EQ(mapper.getBatteryStatus().value_or(-1), BATTERY_STATUS); +} + +// --- LightInputMapperTest --- +class LightInputMapperTest : public InputMapperTest { +protected: + void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::LIGHT); } +}; + +TEST_F(LightInputMapperTest, GetSources) { + LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>(); + + ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources()); +} + +TEST_F(LightInputMapperTest, SingleLight) { + RawLightInfo infoSingle = {.id = 1, + .name = "Mono", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + mFakeEventHub->addRawLightInfo(infoSingle.id, std::move(infoSingle)); + + LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>(); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + const auto& ids = info.getLightIds(); + ASSERT_EQ(1UL, ids.size()); + ASSERT_EQ(InputDeviceLightType::SINGLE, info.getLightInfo(ids[0])->type); + + ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_BRIGHTNESS)); + ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_BRIGHTNESS); +} + +TEST_F(LightInputMapperTest, RGBLight) { + RawLightInfo infoRed = {.id = 1, + .name = "red", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::RED, + .path = ""}; + RawLightInfo infoGreen = {.id = 2, + .name = "green", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::GREEN, + .path = ""}; + RawLightInfo infoBlue = {.id = 3, + .name = "blue", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | InputLightClass::BLUE, + .path = ""}; + mFakeEventHub->addRawLightInfo(infoRed.id, std::move(infoRed)); + mFakeEventHub->addRawLightInfo(infoGreen.id, std::move(infoGreen)); + mFakeEventHub->addRawLightInfo(infoBlue.id, std::move(infoBlue)); + + LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>(); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + const auto& ids = info.getLightIds(); + ASSERT_EQ(1UL, ids.size()); + ASSERT_EQ(InputDeviceLightType::RGB, info.getLightInfo(ids[0])->type); + + ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR)); + ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR); +} + +TEST_F(LightInputMapperTest, MultiColorRGBLight) { + RawLightInfo infoColor = {.id = 1, + .name = "red", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS | + InputLightClass::MULTI_INTENSITY | + InputLightClass::MULTI_INDEX, + .path = ""}; + + mFakeEventHub->addRawLightInfo(infoColor.id, std::move(infoColor)); + + LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>(); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + const auto& ids = info.getLightIds(); + ASSERT_EQ(1UL, ids.size()); + ASSERT_EQ(InputDeviceLightType::MULTI_COLOR, info.getLightInfo(ids[0])->type); + + ASSERT_TRUE(mapper.setLightColor(ids[0], LIGHT_COLOR)); + ASSERT_EQ(mapper.getLightColor(ids[0]).value_or(-1), LIGHT_COLOR); +} + +TEST_F(LightInputMapperTest, PlayerIdLight) { + RawLightInfo info1 = {.id = 1, + .name = "player1", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info2 = {.id = 2, + .name = "player2", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info3 = {.id = 3, + .name = "player3", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + RawLightInfo info4 = {.id = 4, + .name = "player4", + .maxBrightness = 255, + .flags = InputLightClass::BRIGHTNESS, + .path = ""}; + mFakeEventHub->addRawLightInfo(info1.id, std::move(info1)); + mFakeEventHub->addRawLightInfo(info2.id, std::move(info2)); + mFakeEventHub->addRawLightInfo(info3.id, std::move(info3)); + mFakeEventHub->addRawLightInfo(info4.id, std::move(info4)); + + LightInputMapper& mapper = addMapperAndConfigure<LightInputMapper>(); + InputDeviceInfo info; + mapper.populateDeviceInfo(&info); + const auto& ids = info.getLightIds(); + ASSERT_EQ(1UL, ids.size()); + ASSERT_EQ(InputDeviceLightType::PLAYER_ID, info.getLightInfo(ids[0])->type); + + ASSERT_FALSE(mapper.setLightColor(ids[0], LIGHT_COLOR)); + ASSERT_TRUE(mapper.setLightPlayerId(ids[0], LIGHT_PLAYER_ID)); + ASSERT_EQ(mapper.getLightPlayerId(ids[0]).value_or(-1), LIGHT_PLAYER_ID); } // --- KeyboardInputMapperTest --- diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 68f4f283ee..1d3e5b53fa 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libpowermanager", diff --git a/services/schedulerservice/Android.bp b/services/schedulerservice/Android.bp index 73802dbc9f..4ef72d0c83 100644 --- a/services/schedulerservice/Android.bp +++ b/services/schedulerservice/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libschedulerservicehidl", srcs: [ diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 532a2e5a13..ca9ff7c8c3 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + subdirs = [ "hidl" ] diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 2810bffdd3..23893ea1fd 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -914,6 +914,21 @@ bool SensorDevice::isSensorActive(int handle) const { return mActivationCount.valueAt(activationIndex).numActiveClients() > 0; } +void SensorDevice::onMicSensorAccessChanged(void* ident, int handle, nsecs_t samplingPeriodNs) { + Mutex::Autolock _l(mLock); + ssize_t activationIndex = mActivationCount.indexOfKey(handle); + if (activationIndex < 0) { + ALOGW("Handle %d cannot be found in activation record", handle); + return; + } + Info& info(mActivationCount.editValueAt(activationIndex)); + if (info.hasBatchParamsForIdent(ident)) { + ssize_t index = info.batchParams.indexOfKey(ident); + BatchParams& params = info.batchParams.editValueAt(index); + params.mTSample = samplingPeriodNs; + } +} + void SensorDevice::enableAllSensors() { if (mSensors == nullptr) return; Mutex::Autolock _l(mLock); diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h index 5e7d3da398..75da7bb0a1 100644 --- a/services/sensorservice/SensorDevice.h +++ b/services/sensorservice/SensorDevice.h @@ -125,6 +125,10 @@ public: bool isSensorActive(int handle) const; + // To update the BatchParams of a SensorEventConnection when the mic toggle changes its state + // while the Sensors Off toggle is on. + void onMicSensorAccessChanged(void* ident, int handle, nsecs_t samplingPeriodNs); + // Dumpable virtual std::string dump() const override; virtual void dump(util::ProtoOutputStream* proto) const override; diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp index e4c33dafcb..af86d09784 100644 --- a/services/sensorservice/SensorDirectConnection.cpp +++ b/services/sensorservice/SensorDirectConnection.cpp @@ -32,6 +32,8 @@ SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorSer : mService(service), mUid(uid), mMem(*mem), mHalChannelHandle(halChannelHandle), mOpPackageName(opPackageName), mDestroyed(false) { + mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName); + mUserId = multiuser_get_user_id(mUid); ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection"); } @@ -101,6 +103,14 @@ void SensorService::SensorDirectConnection::onSensorAccessChanged(bool hasAccess } } +void SensorService::SensorDirectConnection::onMicSensorAccessChanged(bool isMicToggleOn) { + if (isMicToggleOn) { + capRates(); + } else { + uncapRates(); + } +} + bool SensorService::SensorDirectConnection::hasSensorAccess() const { return mService->hasSensorAccess(mUid, mOpPackageName); } @@ -134,6 +144,7 @@ int32_t SensorService::SensorDirectConnection::configureChannel(int handle, int if (handle == -1 && rateLevel == SENSOR_DIRECT_RATE_STOP) { stopAll(); + mMicRateBackup.clear(); return NO_ERROR; } @@ -157,6 +168,14 @@ int32_t SensorService::SensorDirectConnection::configureChannel(int handle, int return INVALID_OPERATION; } + int requestedRateLevel = rateLevel; + if (mService->isSensorInCappedSet(s.getType()) && rateLevel != SENSOR_DIRECT_RATE_STOP) { + status_t err = mService->adjustRateLevelBasedOnMicAndPermission(&rateLevel, mOpPackageName); + if (err != OK) { + return err; + } + } + struct sensors_direct_cfg_t config = { .rate_level = rateLevel }; @@ -168,18 +187,100 @@ int32_t SensorService::SensorDirectConnection::configureChannel(int handle, int if (rateLevel == SENSOR_DIRECT_RATE_STOP) { if (ret == NO_ERROR) { mActivated.erase(handle); + mMicRateBackup.erase(handle); } else if (ret > 0) { ret = UNKNOWN_ERROR; } } else { if (ret > 0) { mActivated[handle] = rateLevel; + if (mService->isSensorInCappedSet(s.getType())) { + // Back up the rates that the app is allowed to have if the mic toggle is off + // This is used in the uncapRates() function. + if (!mIsRateCappedBasedOnPermission || + requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) { + mMicRateBackup[handle] = requestedRateLevel; + } else { + mMicRateBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL; + } + } } } return ret; } +void SensorService::SensorDirectConnection::capRates() { + Mutex::Autolock _l(mConnectionLock); + const struct sensors_direct_cfg_t capConfig = { + .rate_level = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL + }; + + const struct sensors_direct_cfg_t stopConfig = { + .rate_level = SENSOR_DIRECT_RATE_STOP + }; + + // If our requests are in the backup, then we shouldn't activate sensors from here + bool temporarilyStopped = mActivated.empty() && !mActivatedBackup.empty(); + std::unordered_map<int, int>& existingConnections = + (!temporarilyStopped) ? mActivated : mActivatedBackup; + + SensorDevice& dev(SensorDevice::getInstance()); + for (auto &i : existingConnections) { + int handle = i.first; + int rateLevel = i.second; + sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle); + if (si != nullptr) { + const Sensor& s = si->getSensor(); + if (mService->isSensorInCappedSet(s.getType()) && + rateLevel > SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) { + mMicRateBackup[handle] = rateLevel; + // Modify the rate kept by the existing map + existingConnections[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL; + // Only reconfigure the channel if it's ongoing + if (!temporarilyStopped) { + // Stopping before reconfiguring is the well-tested path in CTS + dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig); + dev.configureDirectChannel(handle, getHalChannelHandle(), &capConfig); + } + } + } + } +} + +void SensorService::SensorDirectConnection::uncapRates() { + Mutex::Autolock _l(mConnectionLock); + + // If our requests are in the backup, then we shouldn't activate sensors from here + bool temporarilyStopped = mActivated.empty() && !mActivatedBackup.empty(); + std::unordered_map<int, int>& existingConnections = + (!temporarilyStopped) ? mActivated : mActivatedBackup; + + const struct sensors_direct_cfg_t stopConfig = { + .rate_level = SENSOR_DIRECT_RATE_STOP + }; + SensorDevice& dev(SensorDevice::getInstance()); + for (auto &i : mMicRateBackup) { + int handle = i.first; + int rateLevel = i.second; + + const struct sensors_direct_cfg_t config = { + .rate_level = rateLevel + }; + + // Modify the rate kept by the existing map + existingConnections[handle] = rateLevel; + + // Only reconfigure the channel if it's ongoing + if (!temporarilyStopped) { + // Stopping before reconfiguring is the well-tested path in CTS + dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig); + dev.configureDirectChannel(handle, getHalChannelHandle(), &config); + } + } + mMicRateBackup.clear(); +} + void SensorService::SensorDirectConnection::stopAll(bool backupRecord) { Mutex::Autolock _l(mConnectionLock); stopAllLocked(backupRecord); diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h index 4181b65b7d..a3f348b668 100644 --- a/services/sensorservice/SensorDirectConnection.h +++ b/services/sensorservice/SensorDirectConnection.h @@ -50,6 +50,8 @@ public: // regained due to changes in the sensor restricted/privacy mode or the // app changed to idle/active status. void onSensorAccessChanged(bool hasAccess); + void onMicSensorAccessChanged(bool isMicToggleOn); + userid_t getUserId() const { return mUserId; } protected: virtual ~SensorDirectConnection(); @@ -82,6 +84,11 @@ private: // If no requests are backed up by stopAll(), this method is no-op. void recoverAll(); + // Limits all active sensor direct report requests when the mic toggle is flipped to on. + void capRates(); + // Recover sensor requests previously capped by capRates(). + void uncapRates(); + const sp<SensorService> mService; const uid_t mUid; const sensors_direct_mem_t mMem; @@ -91,9 +98,12 @@ private: mutable Mutex mConnectionLock; std::unordered_map<int, int> mActivated; std::unordered_map<int, int> mActivatedBackup; + std::unordered_map<int, int> mMicRateBackup; + std::atomic_bool mIsRateCappedBasedOnPermission; mutable Mutex mDestroyLock; bool mDestroyed; + userid_t mUserId; }; } // namepsace android diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 6810c1b781..5225dd7a64 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -38,12 +38,14 @@ constexpr int kTargetSdkUnknown = 0; SensorService::SensorEventConnection::SensorEventConnection( const sp<SensorService>& service, uid_t uid, String8 packageName, bool isDataInjectionMode, - const String16& opPackageName) + const String16& opPackageName, const String16& attributionTag) : mService(service), mUid(uid), mWakeLockRefCount(0), mHasLooperCallbacks(false), mDead(false), mDataInjectionMode(isDataInjectionMode), mEventCache(nullptr), mCacheSize(0), mMaxCacheSize(0), mTimeOfLastEventDrop(0), mEventsDropped(0), - mPackageName(packageName), mOpPackageName(opPackageName), mTargetSdk(kTargetSdkUnknown), - mDestroyed(false) { + mPackageName(packageName), mOpPackageName(opPackageName), mAttributionTag(attributionTag), + mTargetSdk(kTargetSdkUnknown), mDestroyed(false) { + mIsRateCappedBasedOnPermission = mService->isRateCappedBasedOnPermission(mOpPackageName); + mUserId = multiuser_get_user_id(mUid); mChannel = new BitTube(mService->mSocketBufferSize); #if DEBUG_CONNECTIONS mEventsReceived = mEventsSentFromCache = mEventsSent = 0; @@ -285,6 +287,29 @@ bool SensorService::SensorEventConnection::incrementPendingFlushCountIfHasAccess } } +// TODO(b/179649922): A better algorithm to guarantee that capped connections will get a sampling +// rate close to 200 Hz. With the current algorithm, apps might be punished unfairly: E.g.,two apps +// make requests to the sensor service at the same time, one is not capped and uses 250 Hz, and one +//is capped, the capped connection will only get 125 Hz. +void SensorService::SensorEventConnection::addSensorEventsToBuffer(bool shouldResample, + const sensors_event_t& sensorEvent, sensors_event_t* buffer, int* index) { + if (!shouldResample || !mService->isSensorInCappedSet(sensorEvent.type)) { + buffer[(*index)++] = sensorEvent; + } else { + int64_t lastTimestamp = -1; + auto entry = mSensorLastTimestamp.find(sensorEvent.sensor); + if (entry != mSensorLastTimestamp.end()) { + lastTimestamp = entry->second; + } + // Allow 10% headroom here because the clocks are not perfect. + if (lastTimestamp == -1 || sensorEvent.timestamp - lastTimestamp + >= 0.9 * SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { + mSensorLastTimestamp[sensorEvent.sensor] = sensorEvent.timestamp; + buffer[(*index)++] = sensorEvent; + } + } +} + status_t SensorService::SensorEventConnection::sendEvents( sensors_event_t const* buffer, size_t numEvents, sensors_event_t* scratch, @@ -293,6 +318,8 @@ status_t SensorService::SensorEventConnection::sendEvents( std::unique_ptr<sensors_event_t[]> sanitizedBuffer; + bool shouldResample = mService->isMicSensorPrivacyEnabledForUid(mUid) || + mIsRateCappedBasedOnPermission; int count = 0; Mutex::Autolock _l(mConnectionLock); if (scratch) { @@ -346,7 +373,7 @@ status_t SensorService::SensorEventConnection::sendEvents( // Regular sensor event, just copy it to the scratch buffer after checking // the AppOp. if (hasSensorAccess() && noteOpIfRequired(buffer[i])) { - scratch[count++] = buffer[i]; + addSensorEventsToBuffer(shouldResample, buffer[i], scratch, &count); } } i++; @@ -356,12 +383,13 @@ status_t SensorService::SensorEventConnection::sendEvents( buffer[i].meta_data.sensor == sensor_handle))); } } else { + sanitizedBuffer.reset(new sensors_event_t[numEvents]); + scratch = sanitizedBuffer.get(); if (hasSensorAccess()) { - scratch = const_cast<sensors_event_t *>(buffer); - count = numEvents; + for (size_t i = 0; i < numEvents; i++) { + addSensorEventsToBuffer(shouldResample, buffer[i], scratch, &count); + } } else { - sanitizedBuffer.reset(new sensors_event_t[numEvents]); - scratch = sanitizedBuffer.get(); for (size_t i = 0; i < numEvents; i++) { if (buffer[i].type == SENSOR_TYPE_META_DATA) { scratch[count++] = buffer[i++]; @@ -465,7 +493,8 @@ bool SensorService::SensorEventConnection::noteOpIfRequired(const sensors_event_ noteMsg.append(String16(mService->getSensorStringType(sensorHandle))); noteMsg.append(String16(")")); int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid, - mOpPackageName, {}, noteMsg); + mOpPackageName, mAttributionTag, + noteMsg); success = (appOpMode == AppOpsManager::MODE_ALLOWED); } } @@ -684,24 +713,118 @@ status_t SensorService::SensorEventConnection::enableDisable( status_t err; if (enabled) { + nsecs_t requestedSamplingPeriodNs = samplingPeriodNs; + bool isSensorCapped = false; + sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle); + if (si != nullptr) { + const Sensor& s = si->getSensor(); + if (mService->isSensorInCappedSet(s.getType())) { + isSensorCapped = true; + } + } + if (isSensorCapped) { + err = mService->adjustSamplingPeriodBasedOnMicAndPermission(&samplingPeriodNs, + String16(mOpPackageName)); + if (err != OK) { + return err; + } + } err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs, reservedFlags, mOpPackageName); + if (err == OK && isSensorCapped) { + if (!mIsRateCappedBasedOnPermission || + requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { + mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs; + } else { + mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; + } + } } else { err = mService->disable(this, handle); + mMicSamplingPeriodBackup.erase(handle); } return err; } -status_t SensorService::SensorEventConnection::setEventRate( - int handle, nsecs_t samplingPeriodNs) -{ +status_t SensorService::SensorEventConnection::setEventRate(int handle, nsecs_t samplingPeriodNs) { if (mDestroyed) { android_errorWriteLog(0x534e4554, "168211968"); return DEAD_OBJECT; } - return mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName); + nsecs_t requestedSamplingPeriodNs = samplingPeriodNs; + bool isSensorCapped = false; + sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle); + if (si != nullptr) { + const Sensor& s = si->getSensor(); + if (mService->isSensorInCappedSet(s.getType())) { + isSensorCapped = true; + } + } + if (isSensorCapped) { + status_t err = mService->adjustSamplingPeriodBasedOnMicAndPermission(&samplingPeriodNs, + String16(mOpPackageName)); + if (err != OK) { + return err; + } + } + status_t ret = mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName); + if (ret == OK && isSensorCapped) { + if (!mIsRateCappedBasedOnPermission || + requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { + mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs; + } else { + mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; + } + } + return ret; +} + +void SensorService::SensorEventConnection::onMicSensorAccessChanged(bool isMicToggleOn) { + if (isMicToggleOn) { + capRates(); + } else { + uncapRates(); + } +} + +void SensorService::SensorEventConnection::capRates() { + Mutex::Autolock _l(mConnectionLock); + SensorDevice& dev(SensorDevice::getInstance()); + for (auto &i : mMicSamplingPeriodBackup) { + int handle = i.first; + nsecs_t samplingPeriodNs = i.second; + if (samplingPeriodNs < SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { + if (hasSensorAccess()) { + mService->setEventRate(this, handle, SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS, + mOpPackageName); + } else { + // Update SensorDevice with the capped rate so that when sensor access is restored, + // the correct event rate is used. + dev.onMicSensorAccessChanged(this, handle, + SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS); + } + } + } +} + +void SensorService::SensorEventConnection::uncapRates() { + Mutex::Autolock _l(mConnectionLock); + SensorDevice& dev(SensorDevice::getInstance()); + for (auto &i : mMicSamplingPeriodBackup) { + int handle = i.first; + nsecs_t samplingPeriodNs = i.second; + if (samplingPeriodNs < SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { + if (hasSensorAccess()) { + mService->setEventRate(this, handle, samplingPeriodNs, mOpPackageName); + } else { + // Update SensorDevice with the uncapped rate so that when sensor access is + // restored, the correct event rate is used. + dev.onMicSensorAccessChanged(this, handle, samplingPeriodNs); + } + } + } } status_t SensorService::SensorEventConnection::flush() { diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h index 9487a39a92..4e3f120ccc 100644 --- a/services/sensorservice/SensorEventConnection.h +++ b/services/sensorservice/SensorEventConnection.h @@ -50,7 +50,8 @@ class SensorService::SensorEventConnection: public: SensorEventConnection(const sp<SensorService>& service, uid_t uid, String8 packageName, - bool isDataInjectionMode, const String16& opPackageName); + bool isDataInjectionMode, const String16& opPackageName, + const String16& attributionTag); status_t sendEvents(sensors_event_t const* buffer, size_t count, sensors_event_t* scratch, wp<const SensorEventConnection> const * mapFlushEventsToConnections = nullptr); @@ -68,6 +69,9 @@ public: String8 getPackageName() const; uid_t getUid() const { return mUid; } + // cap/uncap existing connection depending on the state of the mic toggle. + void onMicSensorAccessChanged(bool isMicToggleOn); + userid_t getUserId() const { return mUserId; } private: virtual ~SensorEventConnection(); @@ -137,10 +141,20 @@ private: // Call noteOp for the sensor if the sensor requires a permission bool noteOpIfRequired(const sensors_event_t& event); - + // Limits all active connections when the mic toggle is flipped to on. + void capRates(); + // Recover sensor connection previously capped by capRates(). + void uncapRates(); + + // Add sensorEvent to buffer at position index if the sensorEvent satisfies throttling rules. + void addSensorEventsToBuffer(bool shouldResample, const sensors_event_t& sensorEvent, + sensors_event_t* buffer, int* index); sp<SensorService> const mService; sp<BitTube> mChannel; uid_t mUid; + std::atomic_bool mIsRateCappedBasedOnPermission; + // Store a mapping of sensor to the timestamp of their last sensor event. + std::unordered_map<int, int64_t> mSensorLastTimestamp; mutable Mutex mConnectionLock; // Number of events from wake up sensors which are still pending and haven't been delivered to // the corresponding application. It is incremented by one unit for each write to the socket. @@ -177,6 +191,7 @@ private: int mEventsDropped; String8 mPackageName; const String16 mOpPackageName; + const String16 mAttributionTag; int mTargetSdk; #if DEBUG_CONNECTIONS int mEventsReceived, mEventsSent, mEventsSentFromCache; @@ -189,6 +204,9 @@ private: // Store a mapping of sensor handles to required AppOp for a sensor. This map only contains a // valid mapping for sensors that require a permission in order to reduce the lookup time. std::unordered_map<int32_t, int32_t> mHandleToAppOp; + // Mapping of sensor handles to its rate before being capped by the mic toggle. + std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup; + userid_t mUserId; }; } // namepsace android diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 8f25bdba4f..942b7ae76a 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include <android-base/strings.h> #include <android/content/pm/IPackageManagerNative.h> #include <android/util/ProtoOutputStream.h> #include <frameworks/base/core/proto/android/service/sensor_service.proto.h> @@ -88,6 +89,8 @@ AppOpsManager SensorService::sAppOpsManager; #define SENSOR_SERVICE_SCHED_FIFO_PRIORITY 10 // Permissions. +static const String16 sAccessHighSensorSamplingRatePermission( + "android.permission.HIGH_SAMPLING_RATE_SENSORS"); static const String16 sDumpPermission("android.permission.DUMP"); static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE"); static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS"); @@ -366,6 +369,9 @@ SensorService::~SensorService() { } mUidPolicy->unregisterSelf(); mSensorPrivacyPolicy->unregisterSelf(); + for (auto const& [userId, policy] : mMicSensorPrivacyPolicies) { + policy->unregisterSelf(); + } } status_t SensorService::dump(int fd, const Vector<String16>& args) { @@ -695,6 +701,35 @@ void SensorService::enableAllSensorsLocked(ConnectionSafeAutolock* connLock) { } } +void SensorService::capRates(userid_t userId) { + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + for (const sp<SensorDirectConnection>& conn : connLock.getDirectConnections()) { + if (conn->getUserId() == userId) { + conn->onMicSensorAccessChanged(true); + } + } + + for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) { + if (conn->getUserId() == userId) { + conn->onMicSensorAccessChanged(true); + } + } +} + +void SensorService::uncapRates(userid_t userId) { + ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); + for (const sp<SensorDirectConnection>& conn : connLock.getDirectConnections()) { + if (conn->getUserId() == userId) { + conn->onMicSensorAccessChanged(false); + } + } + + for (const sp<SensorEventConnection>& conn : connLock.getActiveConnections()) { + if (conn->getUserId() == userId) { + conn->onMicSensorAccessChanged(false); + } + } +} // NOTE: This is a remote API - make sure all args are validated status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& args) { @@ -1209,14 +1244,20 @@ void SensorService::makeUuidsIntoIdsForSensorList(Vector<Sensor> &sensorList) co } } -Vector<Sensor> SensorService::getSensorList(const String16& /* opPackageName */) { +Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) { char value[PROPERTY_VALUE_MAX]; property_get("debug.sensors", value, "0"); const Vector<Sensor>& initialSensorList = (atoi(value)) ? mSensors.getUserDebugSensors() : mSensors.getUserSensors(); Vector<Sensor> accessibleSensorList; + + bool isCapped = isRateCappedBasedOnPermission(opPackageName); for (size_t i = 0; i < initialSensorList.size(); i++) { Sensor sensor = initialSensorList[i]; + if (isCapped && isSensorInCappedSet(sensor.getType())) { + sensor.capMinDelayMicros(SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS / 1000); + sensor.capHighestDirectReportRateLevel(SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL); + } accessibleSensorList.add(sensor); } makeUuidsIntoIdsForSensorList(accessibleSensorList); @@ -1244,7 +1285,7 @@ Vector<Sensor> SensorService::getDynamicSensorList(const String16& opPackageName } sp<ISensorEventConnection> SensorService::createSensorEventConnection(const String8& packageName, - int requestedMode, const String16& opPackageName) { + int requestedMode, const String16& opPackageName, const String16& attributionTag) { // Only 2 modes supported for a SensorEventConnection ... NORMAL and DATA_INJECTION. if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) { return nullptr; @@ -1266,7 +1307,7 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri String16 connOpPackageName = (opPackageName == String16("")) ? String16(connPackageName) : opPackageName; sp<SensorEventConnection> result(new SensorEventConnection(this, uid, connPackageName, - requestedMode == DATA_INJECTION, connOpPackageName)); + requestedMode == DATA_INJECTION, connOpPackageName, attributionTag)); if (requestedMode == DATA_INJECTION) { mConnectionHolder.addEventConnectionIfNotPresent(result); // Add the associated file descriptor to the Looper for polling whenever there is data to @@ -2024,6 +2065,69 @@ bool SensorService::isUidActive(uid_t uid) { return mUidPolicy->isUidActive(uid); } +bool SensorService::isRateCappedBasedOnPermission(const String16& opPackageName) { + int targetSdk = getTargetSdkVersion(opPackageName); + bool hasSamplingRatePermission = PermissionCache::checkCallingPermission( + sAccessHighSensorSamplingRatePermission); + if (targetSdk < __ANDROID_API_S__ || + (targetSdk >= __ANDROID_API_S__ && hasSamplingRatePermission)) { + return false; + } + return true; +} + +bool SensorService::isSensorInCappedSet(int sensorType) { + return (sensorType == SENSOR_TYPE_ACCELEROMETER + || sensorType == SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED + || sensorType == SENSOR_TYPE_GYROSCOPE + || sensorType == SENSOR_TYPE_GYROSCOPE_UNCALIBRATED + || sensorType == SENSOR_TYPE_MAGNETIC_FIELD + || sensorType == SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED); +} + +status_t SensorService::adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs, + const String16& opPackageName) { + uid_t uid = IPCThreadState::self()->getCallingUid(); + bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName); + if (*requestedPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) { + return OK; + } + if (shouldCapBasedOnPermission) { + *requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; + if (isPackageDebuggable(opPackageName)) { + return PERMISSION_DENIED; + } + return OK; + } + if (isMicSensorPrivacyEnabledForUid(uid)) { + *requestedPeriodNs = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS; + return OK; + } + return OK; +} + +status_t SensorService::adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel, + const String16& opPackageName) { + uid_t uid = IPCThreadState::self()->getCallingUid(); + bool shouldCapBasedOnPermission = isRateCappedBasedOnPermission(opPackageName); + + if (*requestedRateLevel <= SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL) { + return OK; + } + if (shouldCapBasedOnPermission) { + *requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL; + if (isPackageDebuggable(opPackageName)) { + return PERMISSION_DENIED; + } + return OK; + } + if (isMicSensorPrivacyEnabledForUid(uid)) { + *requestedRateLevel = SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL; + return OK; + } + return OK; +} + void SensorService::SensorPrivacyPolicy::registerSelf() { SensorPrivacyManager spm; mSensorPrivacyEnabled = spm.isSensorPrivacyEnabled(); @@ -2042,16 +2146,56 @@ bool SensorService::SensorPrivacyPolicy::isSensorPrivacyEnabled() { binder::Status SensorService::SensorPrivacyPolicy::onSensorPrivacyChanged(bool enabled) { mSensorPrivacyEnabled = enabled; sp<SensorService> service = mService.promote(); + if (service != nullptr) { - if (enabled) { - service->disableAllSensors(); + if (mIsIndividualMic) { + if (enabled) { + service->capRates(mUserId); + } else { + service->uncapRates(mUserId); + } } else { - service->enableAllSensors(); + if (enabled) { + service->disableAllSensors(); + } else { + service->enableAllSensors(); + } } } return binder::Status::ok(); } +status_t SensorService::SensorPrivacyPolicy::registerSelfForIndividual(int userId) { + Mutex::Autolock _l(mSensorPrivacyLock); + + SensorPrivacyManager spm; + status_t err = spm.addIndividualSensorPrivacyListener(userId, + SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE, this); + + if (err != OK) { + ALOGE("Cannot register a mic listener."); + return err; + } + mSensorPrivacyEnabled = spm.isIndividualSensorPrivacyEnabled(userId, + SensorPrivacyManager::INDIVIDUAL_SENSOR_MICROPHONE); + + mIsIndividualMic = true; + mUserId = userId; + return OK; +} + +bool SensorService::isMicSensorPrivacyEnabledForUid(uid_t uid) { + userid_t userId = multiuser_get_user_id(uid); + if (mMicSensorPrivacyPolicies.find(userId) == mMicSensorPrivacyPolicies.end()) { + sp<SensorPrivacyPolicy> userPolicy = new SensorPrivacyPolicy(this); + if (userPolicy->registerSelfForIndividual(userId) != OK) { + return false; + } + mMicSensorPrivacyPolicies[userId] = userPolicy; + } + return mMicSensorPrivacyPolicies[userId]->isSensorPrivacyEnabled(); +} + SensorService::ConnectionSafeAutolock::ConnectionSafeAutolock( SensorService::SensorConnectionHolder& holder, Mutex& mutex) : mConnectionHolder(holder), mAutolock(mutex) {} @@ -2109,4 +2253,17 @@ SensorService::ConnectionSafeAutolock SensorService::SensorConnectionHolder::loc return ConnectionSafeAutolock(*this, mutex); } +bool SensorService::isPackageDebuggable(const String16& opPackageName) { + bool debugMode = false; + sp<IBinder> binder = defaultServiceManager()->getService(String16("package_native")); + if (binder != nullptr) { + sp<content::pm::IPackageManagerNative> packageManager = + interface_cast<content::pm::IPackageManagerNative>(binder); + if (packageManager != nullptr) { + binder::Status status = packageManager->isPackageDebuggable( + opPackageName, &debugMode); + } + } + return debugMode; +} } // namespace android diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 50c7c2f383..9c5060a577 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -61,6 +61,15 @@ #define SENSOR_REGISTRATIONS_BUF_SIZE 200 +// Apps that targets S+ and do not have HIGH_SAMPLING_RATE_SENSORS permission will be capped +// at 200 Hz. The cap also applies to all requests when the mic toggle is flipped to on, regardless +// of their target SDKs and permission. +// Capped sampling periods for apps that have non-direct sensor connections. +#define SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS (5 * 1000 * 1000) +// Capped sampling rate level for apps that have direct sensor connections. +// The enum SENSOR_DIRECT_RATE_NORMAL corresponds to a rate value of at most 110 Hz. +#define SENSOR_SERVICE_CAPPED_SAMPLING_RATE_LEVEL SENSOR_DIRECT_RATE_NORMAL + namespace android { // --------------------------------------------------------------------------- class SensorInterface; @@ -95,6 +104,8 @@ public: status_t flushSensor(const sp<SensorEventConnection>& connection, const String16& opPackageName); + // Returns true if a sensor should be throttled according to our rate-throttling rules. + static bool isSensorInCappedSet(int sensorType); virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args); @@ -212,17 +223,23 @@ private: // connections will be allowed again. class SensorPrivacyPolicy : public hardware::BnSensorPrivacyListener { public: - explicit SensorPrivacyPolicy(wp<SensorService> service) : mService(service) {} + explicit SensorPrivacyPolicy(wp<SensorService> service) + : mService(service), mIsIndividualMic(false), mUserId(0) {} void registerSelf(); void unregisterSelf(); + status_t registerSelfForIndividual(int userId); + bool isSensorPrivacyEnabled(); binder::Status onSensorPrivacyChanged(bool enabled); private: wp<SensorService> mService; + Mutex mSensorPrivacyLock; std::atomic_bool mSensorPrivacyEnabled; + bool mIsIndividualMic; + userid_t mUserId; }; enum Mode { @@ -286,7 +303,7 @@ private: virtual Vector<Sensor> getDynamicSensorList(const String16& opPackageName); virtual sp<ISensorEventConnection> createSensorEventConnection( const String8& packageName, - int requestedMode, const String16& opPackageName); + int requestedMode, const String16& opPackageName, const String16& attributionTag); virtual int isDataInjectionEnabled(); virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName, uint32_t size, int32_t type, int32_t format, const native_handle *resource); @@ -346,6 +363,13 @@ private: // whitelisted). mLock must be held to invoke this method. bool isOperationRestrictedLocked(const String16& opPackageName); + status_t adjustSamplingPeriodBasedOnMicAndPermission(nsecs_t* requestedPeriodNs, + const String16& opPackageName); + status_t adjustRateLevelBasedOnMicAndPermission(int* requestedRateLevel, + const String16& opPackageName); + bool isRateCappedBasedOnPermission(const String16& opPackageName); + bool isPackageDebuggable(const String16& opPackageName); + // Reset the state of SensorService to NORMAL mode. status_t resetToNormalMode(); status_t resetToNormalModeLocked(); @@ -385,6 +409,11 @@ private: void enableAllSensors(); void enableAllSensorsLocked(ConnectionSafeAutolock* connLock); + // Caps active direct connections (when the mic toggle is flipped to on) + void capRates(userid_t userId); + // Removes the capped rate on active direct connections (when the mic toggle is flipped to off) + void uncapRates(userid_t userId); + static uint8_t sHmacGlobalKey[128]; static bool sHmacGlobalKeyIsValid; @@ -426,6 +455,11 @@ private: static std::map<String16, int> sPackageTargetVersion; static Mutex sPackageTargetVersionLock; static String16 sSensorInterfaceDescriptorPrefix; + + // Map from user to SensorPrivacyPolicy + std::map<userid_t, sp<SensorPrivacyPolicy>> mMicSensorPrivacyPolicies; + // Checks if the mic sensor privacy is enabled for the uid + bool isMicSensorPrivacyEnabledForUid(uid_t uid); }; } // namespace android diff --git a/services/sensorservice/hidl/Android.bp b/services/sensorservice/hidl/Android.bp index 0e1af595f0..9bafb3cf95 100644 --- a/services/sensorservice/hidl/Android.bp +++ b/services/sensorservice/hidl/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libsensorservicehidl", srcs: [ diff --git a/services/sensorservice/tests/Android.bp b/services/sensorservice/tests/Android.bp index d33c0ca59d..ddc03a17e1 100644 --- a/services/sensorservice/tests/Android.bp +++ b/services/sensorservice/tests/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "test-sensorservice", srcs: ["sensorservicetest.cpp"], diff --git a/services/stats/Android.bp b/services/stats/Android.bp index 1ce0524299..a472c5f38b 100644 --- a/services/stats/Android.bp +++ b/services/stats/Android.bp @@ -1,11 +1,23 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libstatshidl", srcs: [ + "StatsAidl.cpp", "StatsHal.cpp", ], cflags: ["-Wall", "-Werror"], shared_libs: [ "android.frameworks.stats@1.0", + "android.frameworks.stats-V1-ndk_platform", + "libbinder_ndk", "libhidlbase", "liblog", "libstatslog", @@ -13,7 +25,11 @@ cc_library_shared { "libutils", ], export_include_dirs: [ - "include/", + "include/", + ], + export_shared_lib_headers: [ + "android.frameworks.stats@1.0", + "android.frameworks.stats-V1-ndk_platform", ], local_include_dirs: [ "include/stats", diff --git a/services/stats/StatsAidl.cpp b/services/stats/StatsAidl.cpp new file mode 100644 index 0000000000..a3b68f1dab --- /dev/null +++ b/services/stats/StatsAidl.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 DEBUG false // STOPSHIP if true +#define LOG_TAG "StatsAidl" + +#include <log/log.h> +#include <statslog.h> + +#include "StatsAidl.h" + +namespace aidl { +namespace android { +namespace frameworks { +namespace stats { + +StatsHal::StatsHal() {} + +ndk::ScopedAStatus StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { + std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName; + if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) { + ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "Not a valid vendor atom ID"); + } + if (reverseDomainName.length() > 50) { + ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str()); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + -1, "Vendor atom reverse domain name is too long"); + } + AStatsEvent* event = AStatsEvent_obtain(); + AStatsEvent_setAtomId(event, vendorAtom.atomId); + AStatsEvent_writeString(event, vendorAtom.reverseDomainName.c_str()); + for (const auto& atomValue : vendorAtom.values) { + switch (atomValue.getTag()) { + case VendorAtomValue::intValue: + AStatsEvent_writeInt32(event, + atomValue.get<VendorAtomValue::intValue>()); + break; + case VendorAtomValue::longValue: + AStatsEvent_writeInt64(event, + atomValue.get<VendorAtomValue::longValue>()); + break; + case VendorAtomValue::floatValue: + AStatsEvent_writeFloat(event, + atomValue.get<VendorAtomValue::floatValue>()); + break; + case VendorAtomValue::stringValue: + AStatsEvent_writeString(event, + atomValue.get<VendorAtomValue::stringValue>().c_str()); + break; + } + } + AStatsEvent_build(event); + const int ret = AStatsEvent_write(event); + AStatsEvent_release(event); + + return ret <= 0 ? + ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(ret, "report atom failed") : + ndk::ScopedAStatus::ok(); +} + +} // namespace stats +} // namespace frameworks +} // namespace android +} // namespace aidl diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml index bb02f66a28..5fd361c57d 100644 --- a/services/stats/android.frameworks.stats@1.0-service.xml +++ b/services/stats/android.frameworks.stats@1.0-service.xml @@ -8,4 +8,10 @@ <instance>default</instance> </interface> </hal> + + <hal format="aidl"> + <name>android.frameworks.stats</name> + <version>1</version> + <fqname>IStats/default</fqname> + </hal> </manifest> diff --git a/services/stats/include/stats/StatsAidl.h b/services/stats/include/stats/StatsAidl.h new file mode 100644 index 0000000000..219e71e7b0 --- /dev/null +++ b/services/stats/include/stats/StatsAidl.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <aidl/android/frameworks/stats/BnStats.h> + +namespace aidl { +namespace android { +namespace frameworks { +namespace stats { + +class StatsHal : public BnStats { +public: + StatsHal(); + + /** + * Binder call to get vendor atom. + */ + virtual ndk::ScopedAStatus reportVendorAtom( + const VendorAtom& in_vendorAtom) override; +}; + +} // namespace stats +} // namespace frameworks +} // namespace android +} // namespace aidl diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 8d1258a713..625f315b00 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "surfaceflinger_defaults", cflags: [ @@ -106,6 +115,10 @@ cc_defaults { thin: true, }, whole_program_vtables: true, // Requires ThinLTO + pgo: { + sampling: true, + profile_file: "surfaceflinger/surfaceflinger.profdata", + }, // TODO(b/131771163): Fix broken fuzzer support with LTO. sanitize: { fuzzer: false, diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index ec81ff7b14..097f7de891 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -366,8 +366,11 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, mFrameTracker.setFrameReadyTime(desiredPresentTime); } + const Fps refreshRate = mFlinger->mRefreshRateConfigs->getCurrentRefreshRate().getFps(); + const std::optional<Fps> renderRate = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); if (presentFence->isValid()) { - mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence); + mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence, + refreshRate, renderRate); mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); @@ -378,7 +381,8 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, // The HWC doesn't support present fences, so use the refresh // timestamp instead. const nsecs_t actualPresentTime = display->getRefreshTimestamp(); - mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime); + mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime, + refreshRate, renderRate); mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, actualPresentTime, FrameTracer::FrameEvent::PRESENT_FENCE); diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index cf87f62745..f30e1eb1c3 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -35,6 +35,7 @@ #include <renderengine/Image.h> #include "EffectLayer.h" +#include "FrameTracer/FrameTracer.h" #include "TimeStats/TimeStats.h" namespace android { @@ -235,10 +236,6 @@ void BufferStateLayer::pushPendingState() { bool BufferStateLayer::applyPendingStates(Layer::State* stateToCommit) { mCurrentStateModified = mCurrentState.modified; bool stateUpdateAvailable = Layer::applyPendingStates(stateToCommit); - if (stateUpdateAvailable && mCallbackHandleAcquireTime != -1) { - // Update the acquire fence time if we have a buffer - mSurfaceFrame->setAcquireFenceTime(mCallbackHandleAcquireTime); - } mCurrentStateModified = stateUpdateAvailable && mCurrentStateModified; mCurrentState.modified = false; return stateUpdateAvailable; @@ -337,7 +334,7 @@ bool BufferStateLayer::addFrameEvent(const sp<Fence>& acquireFence, nsecs_t post bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber, - std::optional<nsecs_t> /* dequeueTime */, + std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info) { ATRACE_CALL(); @@ -377,6 +374,14 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence setFrameTimelineVsyncForBufferTransaction(info, postTime); } + if (dequeueTime && *dequeueTime != 0) { + const uint64_t bufferId = buffer->getId(); + mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str()); + mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime, + FrameTracer::FrameEvent::DEQUEUE); + mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime, + FrameTracer::FrameEvent::QUEUE); + } return true; } @@ -452,6 +457,7 @@ bool BufferStateLayer::setTransactionCompletedListeners( if (willPresent) { // If this transaction set an acquire fence on this layer, set its acquire time handle->acquireTime = mCallbackHandleAcquireTime; + handle->frameNumber = mCurrentState.frameNumber; // Notify the transaction completed thread that there is a pending latched callback // handle @@ -602,11 +608,12 @@ bool BufferStateLayer::hasFrameUpdate() const { } std::optional<nsecs_t> BufferStateLayer::nextPredictedPresentTime() const { - if (!getDrawingState().isAutoTimestamp || !mSurfaceFrame) { + const State& drawingState(getDrawingState()); + if (!drawingState.isAutoTimestamp || !drawingState.bufferSurfaceFrameTX) { return std::nullopt; } - return mSurfaceFrame->getPredictions().presentTime; + return drawingState.bufferSurfaceFrameTX->getPredictions().presentTime; } status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime, @@ -623,14 +630,22 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse } for (auto& handle : mDrawingState.callbackHandles) { - handle->latchTime = latchTime; - handle->frameNumber = mDrawingState.frameNumber; + if (handle->frameNumber == mDrawingState.frameNumber) { + handle->latchTime = latchTime; + } } const int32_t layerId = getSequence(); - mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber, - std::make_shared<FenceTime>(mDrawingState.acquireFence)); - mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime); + const uint64_t bufferId = mDrawingState.buffer->getId(); + const uint64_t frameNumber = mDrawingState.frameNumber; + const auto acquireFence = std::make_shared<FenceTime>(mDrawingState.acquireFence); + mFlinger->mTimeStats->setAcquireFence(layerId, frameNumber, acquireFence); + mFlinger->mTimeStats->setLatchTime(layerId, frameNumber, latchTime); + + mFlinger->mFrameTracer->traceFence(layerId, bufferId, frameNumber, acquireFence, + FrameTracer::FrameEvent::ACQUIRE_FENCE); + mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, latchTime, + FrameTracer::FrameEvent::LATCH); auto& bufferSurfaceFrame = mDrawingState.bufferSurfaceFrameTX; if (bufferSurfaceFrame != nullptr && diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index ea832a2078..175a40b80e 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -124,6 +124,7 @@ protected: private: friend class SlotGenerationTest; + friend class TransactionFrameTracerTest; friend class TransactionSurfaceFrameTest; inline void tracePendingBufferCount(); diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp index a5be01c75c..2ae49fa6bb 100644 --- a/services/surfaceflinger/ClientCache.cpp +++ b/services/surfaceflinger/ClientCache.cpp @@ -25,6 +25,8 @@ namespace android { +using base::StringAppendF; + ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache); ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {} @@ -203,4 +205,18 @@ void ClientCache::CacheDeathRecipient::binderDied(const wp<IBinder>& who) { ClientCache::getInstance().removeProcess(who); } +void ClientCache::dump(std::string& result) { + std::lock_guard lock(mMutex); + for (auto i : mBuffers) { + const sp<IBinder>& cacheOwner = i.second.first; + StringAppendF(&result," Cache owner: %p\n", cacheOwner.get()); + auto &buffers = i.second.second; + for (auto& [id, clientCacheBuffer] : buffers) { + StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id, + (int)clientCacheBuffer.buffer->getWidth(), + (int)clientCacheBuffer.buffer->getHeight()); + } + } +} + }; // namespace android diff --git a/services/surfaceflinger/ClientCache.h b/services/surfaceflinger/ClientCache.h index d7af7c0e97..0d597c8e05 100644 --- a/services/surfaceflinger/ClientCache.h +++ b/services/surfaceflinger/ClientCache.h @@ -53,6 +53,8 @@ public: void unregisterErasedRecipient(const client_cache_t& cacheId, const wp<ErasedRecipient>& recipient); + void dump(std::string& result); + private: std::mutex mMutex; diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 57dc60bbac..50bc5edfc6 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "libcompositionengine_defaults", defaults: ["surfaceflinger_defaults"], diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h index 95ba9f0429..633668e1c2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h @@ -21,9 +21,9 @@ #include <string> #include <ui/DisplayId.h> -#include <ui/DisplayInfo.h> #include <ui/PixelFormat.h> #include <ui/Size.h> +#include <ui/StaticDisplayInfo.h> #include "DisplayHardware/DisplayIdentification.h" #include "DisplayHardware/PowerAdvisor.h" @@ -39,7 +39,7 @@ class CompositionEngine; struct DisplayCreationArgs { struct Physical { DisplayId id; - DisplayConnectionType type; + ui::DisplayConnectionType type; }; // Required for physical displays. Gives the HWC display id for the existing diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index c445d5b615..8402149c57 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -34,6 +34,7 @@ #include <gui/BufferQueue.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> +#include <ui/StretchEffect.h> #include "DisplayHardware/Hal.h" @@ -123,6 +124,8 @@ struct LayerFECompositionState { // List of regions that require blur std::vector<BlurRegion> blurRegions; + StretchEffect stretchEffect; + /* * Geometry state */ diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h index 782c8d772f..6b9597b2d5 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h @@ -24,6 +24,7 @@ #include <ui/FloatRect.h> #include <ui/Rect.h> #include <ui/Region.h> +#include <ui/StretchEffect.h> #include <ui/Transform.h> namespace android::compositionengine::impl { @@ -58,5 +59,6 @@ void dumpVal(std::string& out, const char* name, const ui::Transform&); void dumpVal(std::string& out, const char* name, const ui::Size&); void dumpVal(std::string& out, const char* name, const mat4& tr); +void dumpVal(std::string& out, const char* name, const StretchEffect&); } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 0b0b8d5e9c..a605fe1dc3 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -55,7 +55,8 @@ void Display::setConfiguration(const compositionengine::DisplayCreationArgs& arg editState().isSecure = args.isSecure; editState().displaySpace.bounds = Rect(args.pixels); setLayerStackFilter(args.layerStackId, - args.physical && args.physical->type == DisplayConnectionType::Internal); + args.physical && + args.physical->type == ui::DisplayConnectionType::Internal); setName(args.name); mGpuVirtualDisplayIdGenerator = args.gpuVirtualDisplayIdGenerator; diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp index 9d1bb02e7c..0cc2c6e637 100644 --- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp @@ -100,4 +100,10 @@ void dumpVal(std::string& out, const char* name, const mat4& tr) { ); /* clang-format on */ } +void dumpVal(std::string& out, const char* name, const StretchEffect& effect) { + StringAppendF(&out, "%s={ area=[%f, %f, %f, %f], vec=(%f, %f), max=%f } ", name, + effect.area.left, effect.area.top, effect.area.right, effect.area.bottom, + effect.vectorX, effect.vectorY, effect.maxAmount); +} + } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp index 1338538861..430945ab4c 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp @@ -74,6 +74,9 @@ void LayerFECompositionState::dump(std::string& out) const { dumpVal(out, "blend", toString(blendMode), blendMode); dumpVal(out, "alpha", alpha); dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius); + if (stretchEffect.hasEffect()) { + dumpVal(out, "stretchEffect", stretchEffect); + } if (!metadata.empty()) { out.append("\n metadata {"); diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 348ec398ae..8a83639ce5 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -35,8 +35,8 @@ #include <compositionengine/mock/RenderSurface.h> #include <gtest/gtest.h> #include <renderengine/mock/RenderEngine.h> -#include <ui/DisplayInfo.h> #include <ui/Rect.h> +#include <ui/StaticDisplayInfo.h> #include "MockHWC2.h" #include "MockHWComposer.h" @@ -169,7 +169,7 @@ struct DisplayTestCommon : public testing::Test { DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() { return DisplayCreationArgsBuilder() - .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal}) + .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal}) .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888)) .setIsSecure(true) @@ -265,7 +265,7 @@ TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) { mDisplay->setConfiguration( DisplayCreationArgsBuilder() .setUseHwcVirtualDisplays(true) - .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal}) + .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal}) .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH)) .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888)) .setIsSecure(true) @@ -286,7 +286,7 @@ TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) { mDisplay->setConfiguration( DisplayCreationArgsBuilder() .setUseHwcVirtualDisplays(true) - .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::External}) + .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::External}) .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH)) .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888)) .setIsSecure(false) @@ -1018,7 +1018,7 @@ struct DisplayFunctionalTest : public testing::Test { std::shared_ptr<Display> mDisplay = impl::createDisplayTemplated< Display>(mCompositionEngine, DisplayCreationArgsBuilder() - .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal}) + .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal}) .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888)) .setIsSecure(true) diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index ab003852de..bac894a6dc 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -91,7 +91,7 @@ public: MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId)); MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent)); MOCK_CONST_METHOD0(isUsingVrComposer, bool()); - MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(PhysicalDisplayId)); + MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId)); MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId)); MOCK_CONST_METHOD2(getDisplayVsyncPeriod, status_t(PhysicalDisplayId, nsecs_t*)); MOCK_METHOD4(setActiveModeWithConstraints, diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index a785968515..36c4c4d5ba 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -173,7 +173,7 @@ const DisplayModes& DisplayDevice::getSupportedModes() const { DisplayModePtr DisplayDevice::getMode(DisplayModeId modeId) const { const auto id = modeId.value(); - if (id < mSupportedModes.size()) { + if (static_cast<size_t>(id) < mSupportedModes.size()) { return mSupportedModes[id]; } return nullptr; @@ -254,7 +254,7 @@ ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() { std::string DisplayDevice::getDebugName() const { const char* type = "virtual"; if (mConnectionType) { - type = *mConnectionType == DisplayConnectionType::Internal ? "internal" : "external"; + type = *mConnectionType == ui::DisplayConnectionType::Internal ? "internal" : "external"; } return base::StringPrintf("DisplayDevice{%s, %s%s, \"%s\"}", to_string(getId()).c_str(), type, @@ -265,7 +265,8 @@ void DisplayDevice::dump(std::string& result) const { StringAppendF(&result, "+ %s\n", getDebugName().c_str()); StringAppendF(&result, " powerMode=%s (%d)\n", to_string(mPowerMode).c_str(), static_cast<int32_t>(mPowerMode)); - StringAppendF(&result, " activeMode=%s\n", to_string(*getActiveMode()).c_str()); + StringAppendF(&result, " activeMode=%s\n", + mSupportedModes.size() ? to_string(*getActiveMode()).c_str() : "none"); result.append(" supportedModes=\n"); diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index b4db933e19..a94bfa269d 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -28,11 +28,11 @@ #include <renderengine/RenderEngine.h> #include <system/window.h> #include <ui/DisplayId.h> -#include <ui/DisplayInfo.h> #include <ui/DisplayState.h> #include <ui/GraphicTypes.h> #include <ui/HdrCapabilities.h> #include <ui/Region.h> +#include <ui/StaticDisplayInfo.h> #include <ui/Transform.h> #include <utils/Errors.h> #include <utils/Mutex.h> @@ -74,7 +74,7 @@ public: return mCompositionDisplay; } - std::optional<DisplayConnectionType> getConnectionType() const { return mConnectionType; } + std::optional<ui::DisplayConnectionType> getConnectionType() const { return mConnectionType; } bool isVirtual() const { return !mConnectionType; } bool isPrimary() const { return mIsPrimary; } @@ -195,7 +195,7 @@ private: HWComposer& mHwComposer; const wp<IBinder> mDisplayToken; const int32_t mSequenceId; - const std::optional<DisplayConnectionType> mConnectionType; + const std::optional<ui::DisplayConnectionType> mConnectionType; const std::shared_ptr<compositionengine::Display> mCompositionDisplay; @@ -222,7 +222,7 @@ private: struct DisplayDeviceState { struct Physical { PhysicalDisplayId id; - DisplayConnectionType type; + ui::DisplayConnectionType type; hardware::graphics::composer::hal::HWDisplayId hwcDisplayId; std::optional<DeviceProductInfo> deviceProductInfo; DisplayModes supportedModes; @@ -263,7 +263,7 @@ struct DisplayDeviceCreationArgs { const std::shared_ptr<compositionengine::Display> compositionDisplay; int32_t sequenceId{0}; - std::optional<DisplayConnectionType> connectionType; + std::optional<ui::DisplayConnectionType> connectionType; bool isSecure{false}; sp<ANativeWindow> nativeWindow; sp<compositionengine::DisplaySurface> displaySurface; diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h index 1f0f3c3c33..853c05b244 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayMode.h +++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h @@ -22,6 +22,7 @@ #include <android-base/stringprintf.h> #include <android/configuration.h> +#include <ui/DisplayMode.h> #include <ui/Size.h> #include <utils/Timers.h> @@ -36,7 +37,7 @@ namespace hal = android::hardware::graphics::composer::hal; class DisplayMode; using DisplayModePtr = std::shared_ptr<const DisplayMode>; using DisplayModes = std::vector<DisplayModePtr>; -using DisplayModeId = StrongTyping<size_t, struct DisplayModeIdTag, Compare, Hash>; +using DisplayModeId = StrongTyping<ui::DisplayModeId, struct DisplayModeIdTag, Compare, Hash>; class DisplayMode { public: @@ -139,7 +140,7 @@ private: }; inline std::string to_string(const DisplayMode& mode) { - return base::StringPrintf("{id=%zu, hwcId=%d, width=%d, height=%d, refreshRate=%s, " + return base::StringPrintf("{id=%d, hwcId=%d, width=%d, height=%d, refreshRate=%s, " "dpiX=%.2f, dpiY=%.2f, group=%d}", mode.getId().value(), mode.getHwcId(), mode.getWidth(), mode.getHeight(), to_string(mode.getFps()).c_str(), mode.getDpiX(), diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 71a3276d1b..d04b5f7316 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -264,7 +264,7 @@ Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests, return Error::NONE; } -Error Display::getConnectionType(android::DisplayConnectionType* outType) const { +Error Display::getConnectionType(ui::DisplayConnectionType* outType) const { if (mType != DisplayType::PHYSICAL) return Error::BAD_DISPLAY; using ConnectionType = Hwc2::IComposerClient::DisplayConnectionType; @@ -274,9 +274,8 @@ Error Display::getConnectionType(android::DisplayConnectionType* outType) const return error; } - *outType = connectionType == ConnectionType::INTERNAL - ? android::DisplayConnectionType::Internal - : android::DisplayConnectionType::External; + *outType = connectionType == ConnectionType::INTERNAL ? ui::DisplayConnectionType::Internal + : ui::DisplayConnectionType::External; return Error::NONE; } diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 4c7f28412d..e7bf286d08 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -18,9 +18,9 @@ #include <gui/HdrMetadata.h> #include <math/mat4.h> -#include <ui/DisplayInfo.h> #include <ui/HdrCapabilities.h> #include <ui/Region.h> +#include <ui/StaticDisplayInfo.h> #include <utils/Log.h> #include <utils/StrongPointer.h> #include <utils/Timers.h> @@ -104,7 +104,7 @@ public: hal::DisplayRequest* outDisplayRequests, std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) = 0; [[clang::warn_unused_result]] virtual hal::Error getConnectionType( - android::DisplayConnectionType*) const = 0; + ui::DisplayConnectionType*) const = 0; [[clang::warn_unused_result]] virtual hal::Error supportsDoze(bool* outSupport) const = 0; [[clang::warn_unused_result]] virtual hal::Error getHdrCapabilities( android::HdrCapabilities* outCapabilities) const = 0; @@ -175,7 +175,7 @@ public: hal::Error getRequests( hal::DisplayRequest* outDisplayRequests, std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) override; - hal::Error getConnectionType(android::DisplayConnectionType*) const override; + hal::Error getConnectionType(ui::DisplayConnectionType*) const override; hal::Error supportsDoze(bool* outSupport) const override; hal::Error getHdrCapabilities(android::HdrCapabilities* outCapabilities) const override; hal::Error getDisplayedContentSamplingAttributes(hal::PixelFormat* outFormat, diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index b9a8e4be37..ccfaa76374 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -369,16 +369,16 @@ std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displ // Composer 2.4 -DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId displayId) const { - RETURN_IF_INVALID_DISPLAY(displayId, DisplayConnectionType::Internal); +ui::DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, ui::DisplayConnectionType::Internal); const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay; - DisplayConnectionType type; + ui::DisplayConnectionType type; const auto error = hwcDisplay->getConnectionType(&type); const auto FALLBACK_TYPE = hwcDisplay->getId() == mInternalHwcDisplayId - ? DisplayConnectionType::Internal - : DisplayConnectionType::External; + ? ui::DisplayConnectionType::Internal + : ui::DisplayConnectionType::External; RETURN_IF_HWC_ERROR(error, displayId, FALLBACK_TYPE); return type; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index f9c8e2efef..cf6bc68897 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -204,7 +204,7 @@ public: ui::RenderIntent) = 0; // Composer 2.4 - virtual DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0; + virtual ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0; virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0; virtual status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId, nsecs_t* outVsyncPeriod) const = 0; @@ -335,7 +335,7 @@ public: status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override; // Composer 2.4 - DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; + ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override; status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId, nsecs_t* outVsyncPeriod) const override; diff --git a/services/surfaceflinger/Fps.h b/services/surfaceflinger/Fps.h index 38a9af0149..e9f06e5519 100644 --- a/services/surfaceflinger/Fps.h +++ b/services/surfaceflinger/Fps.h @@ -27,7 +27,7 @@ namespace android { // Value which represents "frames per second". This class is a wrapper around // float, providing some useful utilities, such as comparisons with tolerance -// and converting between period duruation and frequency. +// and converting between period duration and frequency. class Fps { public: static constexpr Fps fromPeriodNsecs(nsecs_t period) { return Fps(1e9f / period, period); } diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 3f833f4890..da04202ef2 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -30,6 +30,7 @@ namespace android::frametimeline { using base::StringAppendF; using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent; +using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource; void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals, const std::string& indent, PredictionState predictionState, nsecs_t baseTime) { @@ -305,11 +306,17 @@ void SurfaceFrame::setActualQueueTime(nsecs_t actualQueueTime) { std::scoped_lock lock(mMutex); mActualQueueTime = actualQueueTime; } + void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) { std::scoped_lock lock(mMutex); mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime); } +void SurfaceFrame::setDropTime(nsecs_t dropTime) { + std::scoped_lock lock(mMutex); + mDropTime = dropTime; +} + void SurfaceFrame::setPresentState(PresentState presentState, nsecs_t lastLatchTime) { std::scoped_lock lock(mMutex); LOG_ALWAYS_FATAL_IF(mPresentState != PresentState::Unknown, @@ -320,9 +327,19 @@ void SurfaceFrame::setPresentState(PresentState presentState, nsecs_t lastLatchT mLastLatchTime = lastLatchTime; } +void SurfaceFrame::setRenderRate(Fps renderRate) { + std::lock_guard<std::mutex> lock(mMutex); + mRenderRate = renderRate; +} + std::optional<int32_t> SurfaceFrame::getJankType() const { std::scoped_lock lock(mMutex); + if (mPresentState == PresentState::Dropped) { + // Return no jank if it's a dropped frame since we cannot attribute a jank to a it. + return JankType::None; + } if (mActuals.presentTime == 0) { + // Frame hasn't been presented yet. return std::nullopt; } return mJankType; @@ -338,6 +355,11 @@ TimelineItem SurfaceFrame::getActuals() const { return mActuals; } +PredictionState SurfaceFrame::getPredictionState() const { + std::scoped_lock lock(mMutex); + return mPredictionState; +} + SurfaceFrame::PresentState SurfaceFrame::getPresentState() const { std::scoped_lock lock(mMutex); return mPresentState; @@ -353,6 +375,11 @@ FrameReadyMetadata SurfaceFrame::getFrameReadyMetadata() const { return mFrameReadyMetadata; } +nsecs_t SurfaceFrame::getDropTime() const { + std::scoped_lock lock(mMutex); + return mDropTime; +} + void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) const { std::scoped_lock lock(mMutex); StringAppendF(&result, "%s", indent.c_str()); @@ -367,8 +394,17 @@ void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t StringAppendF(&result, "%s", indent.c_str()); StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid); StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Scheduled rendering rate: %d fps\n", + mRenderRate ? mRenderRate->getIntValue() : 0); + StringAppendF(&result, "%s", indent.c_str()); StringAppendF(&result, "Present State : %s\n", toString(mPresentState).c_str()); StringAppendF(&result, "%s", indent.c_str()); + if (mPresentState == PresentState::Dropped) { + std::chrono::nanoseconds dropTime(mDropTime - baseTime); + StringAppendF(&result, "Drop time : %10f\n", + std::chrono::duration<double, std::milli>(dropTime).count()); + StringAppendF(&result, "%s", indent.c_str()); + } StringAppendF(&result, "Prediction State : %s\n", toString(mPredictionState).c_str()); StringAppendF(&result, "%s", indent.c_str()); StringAppendF(&result, "Jank Type : %s\n", jankTypeBitmaskToString(mJankType).c_str()); @@ -391,9 +427,10 @@ void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime); } -void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, - nsecs_t vsyncPeriod) { +void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate, + nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta) { std::scoped_lock lock(mMutex); + if (mPresentState != PresentState::Presented) { // No need to update dropped buffers return; @@ -412,13 +449,16 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, mJankType = JankType::Unknown; mFramePresentMetadata = FramePresentMetadata::UnknownPresent; mFrameReadyMetadata = FrameReadyMetadata::UnknownFinish; - mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType); + const constexpr nsecs_t kAppDeadlineDelta = -1; + mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName, + mJankType, displayDeadlineDelta, displayPresentDelta, + kAppDeadlineDelta}); return; } const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime; const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime; - const nsecs_t deltaToVsync = std::abs(presentDelta) % vsyncPeriod; + const nsecs_t deltaToVsync = std::abs(presentDelta) % refreshRate.getPeriodNsecs(); if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) { mFrameReadyMetadata = FrameReadyMetadata::LateFinish; @@ -440,7 +480,8 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) { // Finish on time, Present early if (deltaToVsync < mJankClassificationThresholds.presentThreshold || - deltaToVsync >= vsyncPeriod - mJankClassificationThresholds.presentThreshold) { + deltaToVsync >= refreshRate.getPeriodNsecs() - + mJankClassificationThresholds.presentThreshold) { // Delta factor of vsync mJankType = JankType::SurfaceFlingerScheduling; } else { @@ -463,7 +504,8 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, mJankType |= displayFrameJankType; } else { if (deltaToVsync < mJankClassificationThresholds.presentThreshold || - deltaToVsync >= vsyncPeriod - mJankClassificationThresholds.presentThreshold) { + deltaToVsync >= refreshRate.getPeriodNsecs() - + mJankClassificationThresholds.presentThreshold) { // Delta factor of vsync mJankType |= JankType::SurfaceFlingerScheduling; } else { @@ -482,30 +524,16 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, } } } - mTimeStats->incrementJankyFrames(mOwnerUid, mLayerName, mJankType); + mTimeStats->incrementJankyFrames({refreshRate, mRenderRate, mOwnerUid, mLayerName, mJankType, + displayDeadlineDelta, displayPresentDelta, deadlineDelta}); } -/** - * TODO(b/178637512): add inputEventId to the perfetto trace. - */ -void SurfaceFrame::trace(int64_t displayFrameToken) { - using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource; - +void SurfaceFrame::tracePredictions(int64_t displayFrameToken) const { int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); - bool missingToken = false; + // Expected timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { std::scoped_lock lock(mMutex); - if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) { - ALOGD("Cannot trace SurfaceFrame - %s with invalid token", mLayerName.c_str()); - missingToken = true; - return; - } else if (displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) { - ALOGD("Cannot trace SurfaceFrame - %s with invalid displayFrameToken", - mLayerName.c_str()); - missingToken = true; - return; - } auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime)); @@ -522,12 +550,6 @@ void SurfaceFrame::trace(int64_t displayFrameToken) { expectedSurfaceFrameStartEvent->set_layer_name(mDebugName); }); - if (missingToken) { - // If one packet can't be traced because of missing token, then no packets can be traced. - // Exit early in this case. - return; - } - // Expected timeline end FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { std::scoped_lock lock(mMutex); @@ -540,15 +562,26 @@ void SurfaceFrame::trace(int64_t displayFrameToken) { expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie); }); +} +void SurfaceFrame::traceActuals(int64_t displayFrameToken) const { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { std::scoped_lock lock(mMutex); auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); // Actual start time is not yet available, so use expected start instead - packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime)); + 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. + packet->set_timestamp( + static_cast<uint64_t>(mActuals.endTime - kPredictionExpiredStartTimeDelta)); + } else { + packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime)); + } auto* event = packet->set_frame_timeline_event(); auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start(); @@ -573,12 +606,17 @@ void SurfaceFrame::trace(int64_t displayFrameToken) { actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition); actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); }); + // 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_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime)); + if (mPresentState == PresentState::Dropped) { + packet->set_timestamp(static_cast<uint64_t>(mDropTime)); + } else { + packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime)); + } auto* event = packet->set_frame_timeline_event(); auto* actualSurfaceFrameEndEvent = event->set_frame_end(); @@ -587,6 +625,23 @@ void SurfaceFrame::trace(int64_t displayFrameToken) { }); } +/** + * TODO(b/178637512): add inputEventId to the perfetto trace. + */ +void SurfaceFrame::trace(int64_t displayFrameToken) const { + if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID || + displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) { + // No packets can be traced with a missing token. + return; + } + if (getPredictionState() != PredictionState::Expired) { + // Expired predictions have zeroed timestamps. This cannot be used in any meaningful way in + // a trace. + tracePredictions(displayFrameToken); + } + traceActuals(displayFrameToken); +} + namespace impl { int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) { @@ -684,10 +739,10 @@ void FrameTimeline::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) mCurrentDisplayFrame->addSurfaceFrame(surfaceFrame); } -void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, nsecs_t vsyncPeriod) { +void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime, Fps refreshRate) { ATRACE_CALL(); std::scoped_lock lock(mMutex); - mCurrentDisplayFrame->onSfWakeUp(token, vsyncPeriod, + mCurrentDisplayFrame->onSfWakeUp(token, refreshRate, mTokenManager.getPredictionsForToken(token), wakeUpTime); } @@ -705,11 +760,11 @@ void FrameTimeline::DisplayFrame::addSurfaceFrame(std::shared_ptr<SurfaceFrame> mSurfaceFrames.push_back(surfaceFrame); } -void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, nsecs_t vsyncPeriod, +void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions, nsecs_t wakeUpTime) { mToken = token; - mVsyncPeriod = vsyncPeriod; + mRefreshRate = refreshRate; if (!predictions) { mPredictionState = PredictionState::Expired; } else { @@ -719,11 +774,6 @@ void FrameTimeline::DisplayFrame::onSfWakeUp(int64_t token, nsecs_t vsyncPeriod, mSurfaceFlingerActuals.startTime = wakeUpTime; } -void FrameTimeline::DisplayFrame::setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod) { - mToken = token; - mVsyncPeriod = vsyncPeriod; -} - void FrameTimeline::DisplayFrame::setPredictions(PredictionState predictionState, TimelineItem predictions) { mPredictionState = predictionState; @@ -740,14 +790,21 @@ void FrameTimeline::DisplayFrame::setActualEndTime(nsecs_t actualEndTime) { void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) { mSurfaceFlingerActuals.presentTime = signalTime; - int32_t totalJankReasons = JankType::None; + if (mPredictionState == PredictionState::Expired) { + // Cannot do jank classification with expired predictions + mJankType = JankType::Unknown; + return; + } // Delta between the expected present and the actual present const nsecs_t presentDelta = mSurfaceFlingerActuals.presentTime - mSurfaceFlingerPredictions.presentTime; + const nsecs_t deadlineDelta = + mSurfaceFlingerActuals.endTime - mSurfaceFlingerPredictions.endTime; + // How far off was the presentDelta when compared to the vsyncPeriod. Used in checking if there // was a prediction error or not. - nsecs_t deltaToVsync = std::abs(presentDelta) % mVsyncPeriod; + nsecs_t deltaToVsync = std::abs(presentDelta) % mRefreshRate.getPeriodNsecs(); if (std::abs(presentDelta) > mJankClassificationThresholds.presentThreshold) { mFramePresentMetadata = presentDelta > 0 ? FramePresentMetadata::LatePresent : FramePresentMetadata::EarlyPresent; @@ -776,8 +833,8 @@ void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) { if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) { // Finish on time, Present early if (deltaToVsync < mJankClassificationThresholds.presentThreshold || - deltaToVsync >= - (mVsyncPeriod - mJankClassificationThresholds.presentThreshold)) { + deltaToVsync >= (mRefreshRate.getPeriodNsecs() - + mJankClassificationThresholds.presentThreshold)) { // Delta is a factor of vsync if its within the presentTheshold on either side // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms. @@ -797,8 +854,8 @@ void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) { if (mFrameReadyMetadata == FrameReadyMetadata::OnTimeFinish) { // Finish on time, Present late if (deltaToVsync < mJankClassificationThresholds.presentThreshold || - deltaToVsync >= - (mVsyncPeriod - mJankClassificationThresholds.presentThreshold)) { + deltaToVsync >= (mRefreshRate.getPeriodNsecs() - + mJankClassificationThresholds.presentThreshold)) { // Delta is a factor of vsync if its within the presentTheshold on either side // of the vsyncPeriod. Example: 0-2ms and 9-11ms are both within the threshold // of the vsyncPeriod if the threshold was 2ms and the vsyncPeriod was 11ms. @@ -819,29 +876,17 @@ void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) { mJankType = JankType::Unknown; } } - totalJankReasons |= mJankType; - for (auto& surfaceFrame : mSurfaceFrames) { - surfaceFrame->onPresent(signalTime, mJankType, mVsyncPeriod); - auto surfaceFrameJankType = surfaceFrame->getJankType(); - if (surfaceFrameJankType != std::nullopt) { - totalJankReasons |= *surfaceFrameJankType; - } + surfaceFrame->onPresent(signalTime, mJankType, mRefreshRate, deadlineDelta, deltaToVsync); } - mTimeStats->incrementJankyFrames(totalJankReasons); } -void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { +void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid) const { int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); - bool missingToken = false; + // Expected timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); - if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) { - ALOGD("Cannot trace DisplayFrame with invalid token"); - missingToken = true; - return; - } packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime)); @@ -854,12 +899,6 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid); }); - if (missingToken) { - // If one packet can't be traced because of missing token, then no packets can be traced. - // Exit early in this case. - return; - } - // Expected timeline end FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); @@ -871,9 +910,12 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie); }); +} +void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid) const { int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); - // Expected timeline start + + // Actual timeline start FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); @@ -893,7 +935,8 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { actualDisplayFrameStartEvent->set_gpu_composition(mGpuComposition); actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); }); - // Expected timeline end + + // Actual timeline end FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); @@ -904,6 +947,21 @@ void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); }); +} + +void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { + if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID) { + // DisplayFrame should not have an invalid token. + ALOGE("Cannot trace DisplayFrame with invalid token"); + return; + } + + if (mPredictionState == PredictionState::Valid) { + // Expired and unknown predictions have zeroed timestamps. This cannot be used in any + // meaningful way in a trace. + tracePredictions(surfaceFlingerPid); + } + traceActuals(surfaceFlingerPid); for (auto& surfaceFrame : mSurfaceFrames) { surfaceFrame->trace(mToken); @@ -988,7 +1046,7 @@ void FrameTimeline::DisplayFrame::dump(std::string& result, nsecs_t baseTime) co StringAppendF(&result, "Present Metadata : %s\n", toString(mFramePresentMetadata).c_str()); StringAppendF(&result, "Finish Metadata: %s\n", toString(mFrameReadyMetadata).c_str()); StringAppendF(&result, "Start Metadata: %s\n", toString(mFrameStartMetadata).c_str()); - std::chrono::nanoseconds vsyncPeriod(mVsyncPeriod); + std::chrono::nanoseconds vsyncPeriod(mRefreshRate.getPeriodNsecs()); StringAppendF(&result, "Vsync Period: %10f\n", std::chrono::duration<double, std::milli>(vsyncPeriod).count()); nsecs_t presentDelta = @@ -996,7 +1054,7 @@ void FrameTimeline::DisplayFrame::dump(std::string& result, nsecs_t baseTime) co std::chrono::nanoseconds presentDeltaNs(std::abs(presentDelta)); StringAppendF(&result, "Present delta: %10f\n", std::chrono::duration<double, std::milli>(presentDeltaNs).count()); - std::chrono::nanoseconds deltaToVsync(std::abs(presentDelta) % mVsyncPeriod); + std::chrono::nanoseconds deltaToVsync(std::abs(presentDelta) % mRefreshRate.getPeriodNsecs()); StringAppendF(&result, "Present delta %% refreshrate: %10f\n", std::chrono::duration<double, std::milli>(deltaToVsync).count()); dumpTable(result, mSurfaceFlingerPredictions, mSurfaceFlingerActuals, "", mPredictionState, diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 54e8efbc92..8f3157d80c 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -16,6 +16,7 @@ #pragma once +#include <../Fps.h> #include <../TimeStats/TimeStats.h> #include <gui/ISurfaceComposer.h> #include <gui/JankInfo.h> @@ -170,34 +171,50 @@ public: TimelineItem getPredictions() const { return mPredictions; }; // Actual timestamps of the app are set individually at different functions. // Start time (if the app provides) and Queue time are accessible after queueing the frame, - // whereas Acquire Fence time is available only during latch. + // whereas Acquire Fence time is available only during latch. Drop time is available at the time + // the buffer was dropped. void setActualStartTime(nsecs_t actualStartTime); void setActualQueueTime(nsecs_t actualQueueTime); void setAcquireFenceTime(nsecs_t acquireFenceTime); + void setDropTime(nsecs_t dropTime); void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0); + void setRenderRate(Fps renderRate); // Functions called by FrameTimeline // BaseTime is the smallest timestamp in this SurfaceFrame. // Used for dumping all timestamps relative to the oldest, making it easy to read. nsecs_t getBaseTime() const; // Sets the actual present time, appropriate metadata and classifies the jank. - void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, nsecs_t vsyncPeriod); + // displayRefreshRate, displayDeadlineDelta, and displayPresentDelta are propagated from the + // display frame. + void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate, + nsecs_t displayDeadlineDelta, nsecs_t displayPresentDelta); // All the timestamps are dumped relative to the baseTime void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const; // Emits a packet for perfetto tracing. The function body will be executed only if tracing is // enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding // DisplayFrame at the trace processor side. - void trace(int64_t displayFrameToken); + void trace(int64_t displayFrameToken) const; - // Getter functions used only by FrameTimelineTests + // Getter functions used only by FrameTimelineTests and SurfaceFrame internally TimelineItem getActuals() const; pid_t getOwnerPid() const { return mOwnerPid; }; - PredictionState getPredictionState() const { return mPredictionState; }; + PredictionState getPredictionState() const; PresentState getPresentState() const; FrameReadyMetadata getFrameReadyMetadata() const; FramePresentMetadata getFramePresentMetadata() const; + nsecs_t getDropTime() const; + + // For prediction expired frames, this delta is subtracted from the actual end time to get a + // start time decent enough to see in traces. + // TODO(b/172587309): Remove this when we have actual start times. + static constexpr nsecs_t kPredictionExpiredStartTimeDelta = + std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); private: + void tracePredictions(int64_t displayFrameToken) const; + void traceActuals(int64_t displayFrameToken) const; + const int64_t mToken; const int32_t mInputEventId; const pid_t mOwnerPid; @@ -211,11 +228,14 @@ private: std::shared_ptr<TimeStats> mTimeStats; const JankClassificationThresholds mJankClassificationThresholds; nsecs_t mActualQueueTime GUARDED_BY(mMutex) = 0; + nsecs_t mDropTime GUARDED_BY(mMutex) = 0; mutable std::mutex mMutex; // Bitmask for the type of jank int32_t mJankType GUARDED_BY(mMutex) = JankType::None; // Indicates if this frame was composited by the GPU or not bool mGpuComposition GUARDED_BY(mMutex) = false; + // Rendering rate for this frame. + std::optional<Fps> mRenderRate GUARDED_BY(mMutex); // Enum for the type of present FramePresentMetadata mFramePresentMetadata GUARDED_BY(mMutex) = FramePresentMetadata::UnknownPresent; @@ -255,7 +275,7 @@ public: // The first function called by SF for the current DisplayFrame. Fetches SF predictions based on // the token and sets the actualSfWakeTime for the current DisplayFrame. - virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) = 0; + virtual void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) = 0; // Sets the sfPresentTime and finalizes the current DisplayFrame. Tracks the given present fence // until it's signaled, and updates the present timestamps of all presented SurfaceFrames in @@ -325,14 +345,13 @@ public: // is enabled. void trace(pid_t surfaceFlingerPid) const; // Sets the token, vsyncPeriod, predictions and SF start time. - void onSfWakeUp(int64_t token, nsecs_t vsyncPeriod, std::optional<TimelineItem> predictions, + void onSfWakeUp(int64_t token, Fps refreshRate, std::optional<TimelineItem> predictions, nsecs_t wakeUpTime); // Sets the appropriate metadata, classifies the jank and returns the classified jankType. void onPresent(nsecs_t signalTime); // Adds the provided SurfaceFrame to the current display frame. void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame); - void setTokenAndVsyncPeriod(int64_t token, nsecs_t vsyncPeriod); void setPredictions(PredictionState predictionState, TimelineItem predictions); void setActualStartTime(nsecs_t actualStartTime); void setActualEndTime(nsecs_t actualEndTime); @@ -353,6 +372,8 @@ public: private: void dump(std::string& result, nsecs_t baseTime) const; + void tracePredictions(pid_t surfaceFlingerPid) const; + void traceActuals(pid_t surfaceFlingerPid) const; int64_t mToken = FrameTimelineInfo::INVALID_VSYNC_ID; @@ -382,7 +403,7 @@ public: FrameStartMetadata mFrameStartMetadata = FrameStartMetadata::UnknownStart; // The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's // timeline - nsecs_t mVsyncPeriod = 0; + Fps mRefreshRate; // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. // Using a reference here because the counter is owned by FrameTimeline, which outlives // DisplayFrame. @@ -398,7 +419,7 @@ public: const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName) override; void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override; - void setSfWakeUp(int64_t token, nsecs_t wakeupTime, nsecs_t vsyncPeriod) override; + void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence) override; void parseArgs(const Vector<String16>& args, std::string& result) override; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index df14003c75..141a112af1 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -25,6 +25,7 @@ #include "Layer.h" +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android/native_window.h> #include <binder/IPCThreadState.h> @@ -488,6 +489,7 @@ void Layer::prepareBasicGeometryCompositionState() { compositionState->alpha = alpha; compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; compositionState->blurRegions = drawingState.blurRegions; + compositionState->stretchEffect = getStretchEffect(); } void Layer::prepareGeometryCompositionState() { @@ -556,8 +558,8 @@ void Layer::preparePerFrameCompositionState() { isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf; // Force client composition for special cases known only to the front-end. - if (isHdrY410() || usesRoundedCorners || drawShadows() || - getDrawingState().blurRegions.size() > 0) { + if (isHdrY410() || usesRoundedCorners || drawShadows() || drawingState.blurRegions.size() > 0 || + compositionState->stretchEffect.hasEffect()) { compositionState->forceClientComposition = true; } } @@ -656,6 +658,7 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom layerSettings.backgroundBlurRadius = getBackgroundBlurRadius(); layerSettings.blurRegions = getBlurRegions(); } + layerSettings.stretchEffect = getDrawingState().stretchEffect; // Record the name of the layer for debugging further down the stack. layerSettings.name = getName(); return layerSettings; @@ -867,9 +870,7 @@ void Layer::mergeSurfaceFrames(State& source, State& target) { void Layer::popPendingState(State* stateToCommit) { ATRACE_CALL(); - if (stateToCommit != nullptr) { - mergeSurfaceFrames(*stateToCommit, mPendingStates[0]); - } + mergeSurfaceFrames(*stateToCommit, mPendingStates[0]); *stateToCommit = mPendingStates[0]; mPendingStates.pop_front(); ATRACE_INT(mTransactionName.c_str(), mPendingStates.size()); @@ -1423,6 +1424,35 @@ bool Layer::setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHin return true; } +bool Layer::setStretchEffect(const StretchEffect& effect) { + StretchEffect temp = effect; + temp.sanitize(); + if (mCurrentState.stretchEffect == temp) { + return false; + } + mCurrentState.sequence++; + mCurrentState.stretchEffect = temp; + mCurrentState.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::updateTreeHasFrameRateVote() { const auto traverseTree = [&](const LayerVector::Visitor& visitor) { auto parent = getParent(); @@ -1438,7 +1468,7 @@ void Layer::updateTreeHasFrameRateVote() { // First traverse the tree and count how many layers has votes. In addition // activate the layers in Scheduler's LayerHistory for it to check for changes int layersWithVote = 0; - traverseTree([&layersWithVote, this](Layer* layer) { + traverseTree([&layersWithVote](Layer* layer) { const auto layerVotedWithDefaultCompatibility = layer->mCurrentState.frameRate.rate.isValid() && layer->mCurrentState.frameRate.type == FrameRateCompatibility::Default; @@ -1454,20 +1484,21 @@ void Layer::updateTreeHasFrameRateVote() { layerVotedWithExactCompatibility) { layersWithVote++; } - - mFlinger->mScheduler->recordLayerHistory(layer, systemTime(), - LayerHistory::LayerUpdateType::SetFrameRate); }); // Now update the other layers bool transactionNeeded = false; - traverseTree([layersWithVote, &transactionNeeded](Layer* layer) { - if (layer->mCurrentState.treeHasFrameRateVote != layersWithVote > 0) { + traverseTree([layersWithVote, &transactionNeeded, this](Layer* layer) { + const bool treeHasFrameRateVote = layersWithVote > 0; + if (layer->mCurrentState.treeHasFrameRateVote != treeHasFrameRateVote) { layer->mCurrentState.sequence++; - layer->mCurrentState.treeHasFrameRateVote = layersWithVote > 0; + layer->mCurrentState.treeHasFrameRateVote = treeHasFrameRateVote; layer->mCurrentState.modified = true; layer->setTransactionFlags(eTransactionNeeded); transactionNeeded = true; + + mFlinger->mScheduler->recordLayerHistory(layer, systemTime(), + LayerHistory::LayerUpdateType::SetFrameRate); } }); @@ -1547,6 +1578,7 @@ void Layer::setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInf void Layer::addSurfaceFrameDroppedForBuffer( std::shared_ptr<frametimeline::SurfaceFrame>& surfaceFrame) { + surfaceFrame->setDropTime(systemTime()); surfaceFrame->setPresentState(PresentState::Dropped); mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame); } @@ -1568,6 +1600,10 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransac // For Transactions, the post time is considered to be both queue and acquire fence time. surfaceFrame->setActualQueueTime(postTime); surfaceFrame->setAcquireFenceTime(postTime); + const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); + if (fps) { + surfaceFrame->setRenderRate(*fps); + } onSurfaceFrameCreated(surfaceFrame); return surfaceFrame; } @@ -1579,6 +1615,10 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( debugName); // For buffers, acquire fence time will set during latch. surfaceFrame->setActualQueueTime(queueTime); + const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid()); + if (fps) { + surfaceFrame->setRenderRate(*fps); + } // TODO(b/178542907): Implement onSurfaceFrameCreated for BQLayer as well. onSurfaceFrameCreated(surfaceFrame); return surfaceFrame; @@ -1719,6 +1759,7 @@ LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const { info.mRefreshPending = isBufferLatched(); info.mIsOpaque = isOpaque(ds); info.mContentDirty = contentDirty; + info.mStretchEffect = getStretchEffect(); return info; } @@ -2459,7 +2500,7 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet if (traceFlags & SurfaceTracing::TRACE_INPUT) { InputWindowInfo info; if (useDrawing) { - info = fillInputInfo(); + info = fillInputInfo({nullptr}); } else { info = state.inputInfo; } @@ -2480,7 +2521,7 @@ bool Layer::isRemovedFromCurrentState() const { return mRemovedFromCurrentState; } -void Layer::fillInputFrameInfo(InputWindowInfo& info) { +void Layer::fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay) { // Transform layer size to screen space and inset it by surface insets. // If this is a portal window, set the touchableRegion to the layerBounds. Rect layerBounds = info.portalToDisplayId == ADISPLAY_ID_NONE @@ -2500,9 +2541,13 @@ void Layer::fillInputFrameInfo(InputWindowInfo& info) { return; } - ui::Transform t = getTransform(); + ui::Transform layerToDisplay = getTransform(); + // Transform that takes window coordinates to unrotated display coordinates + ui::Transform t = toPhysicalDisplay * layerToDisplay; int32_t xSurfaceInset = info.surfaceInset; int32_t ySurfaceInset = info.surfaceInset; + // Bring screenBounds into unrotated space + Rect screenBounds = toPhysicalDisplay.transform(Rect{mScreenBounds}); const float xScale = t.getScaleX(); const float yScale = t.getScaleY(); @@ -2560,7 +2605,6 @@ void Layer::fillInputFrameInfo(InputWindowInfo& info) { // We need to send the layer bounds cropped to the screenbounds since the layer can be cropped. // The frame should be the area the user sees on screen since it's used for occlusion // detection. - Rect screenBounds = Rect{mScreenBounds}; transformedLayerBounds.intersect(screenBounds, &transformedLayerBounds); info.frameLeft = transformedLayerBounds.left; info.frameTop = transformedLayerBounds.top; @@ -2572,7 +2616,7 @@ void Layer::fillInputFrameInfo(InputWindowInfo& info) { info.touchableRegion = inputTransform.transform(info.touchableRegion); } -InputWindowInfo Layer::fillInputInfo() { +InputWindowInfo Layer::fillInputInfo(const sp<DisplayDevice>& display) { if (!hasInputInfo()) { mDrawingState.inputInfo.name = getName(); mDrawingState.inputInfo.ownerUid = mOwnerUid; @@ -2589,7 +2633,13 @@ InputWindowInfo Layer::fillInputInfo() { info.displayId = getLayerStack(); } - fillInputFrameInfo(info); + // Transform that goes from "logical(rotated)" display to physical/unrotated display. + // This is for when inputflinger operates in physical display-space. + ui::Transform toPhysicalDisplay; + if (display) { + toPhysicalDisplay = display->getTransform(); + } + fillInputFrameInfo(info, toPhysicalDisplay); // For compatibility reasons we let layers which can receive input // receive input before they have actually submitted a buffer. Because @@ -2605,12 +2655,14 @@ InputWindowInfo Layer::fillInputInfo() { auto cropLayer = mDrawingState.touchableRegionCrop.promote(); if (info.replaceTouchableRegionWithCrop) { if (cropLayer == nullptr) { - info.touchableRegion = Region(Rect{mScreenBounds}); + info.touchableRegion = Region(toPhysicalDisplay.transform(Rect{mScreenBounds})); } else { - info.touchableRegion = Region(Rect{cropLayer->mScreenBounds}); + info.touchableRegion = + Region(toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds})); } } else if (cropLayer != nullptr) { - info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds}); + info.touchableRegion = info.touchableRegion.intersect( + toPhysicalDisplay.transform(Rect{cropLayer->mScreenBounds})); } // If the layer is a clone, we need to crop the input region to cloned root to prevent @@ -2618,7 +2670,7 @@ InputWindowInfo Layer::fillInputInfo() { if (isClone()) { sp<Layer> clonedRoot = getClonedRoot(); if (clonedRoot != nullptr) { - Rect rect(clonedRoot->mScreenBounds); + Rect rect = toPhysicalDisplay.transform(Rect{clonedRoot->mScreenBounds}); info.touchableRegion = info.touchableRegion.intersect(rect); } } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 36ae19cb46..14bb5b71ac 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -32,6 +32,7 @@ #include <ui/GraphicBuffer.h> #include <ui/PixelFormat.h> #include <ui/Region.h> +#include <ui/StretchEffect.h> #include <ui/Transform.h> #include <utils/RefBase.h> #include <utils/Timers.h> @@ -323,6 +324,9 @@ 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; }; /* @@ -909,7 +913,7 @@ public: bool getPremultipledAlpha() const; void setInputInfo(const InputWindowInfo& info); - InputWindowInfo fillInputInfo(); + InputWindowInfo fillInputInfo(const sp<DisplayDevice>& display); /** * Returns whether this layer has an explicitly set input-info. */ @@ -938,6 +942,9 @@ public: bool backpressureEnabled() { return mDrawingState.flags & layer_state_t::eEnableBackpressure; } + bool setStretchEffect(const StretchEffect& effect); + StretchEffect getStretchEffect() const; + protected: class SyncPoint { public: @@ -1004,6 +1011,7 @@ protected: friend class TestableSurfaceFlinger; friend class RefreshRateSelectionTest; friend class SetFrameRateTest; + friend class TransactionFrameTracerTest; friend class TransactionSurfaceFrameTest; virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom); @@ -1138,9 +1146,6 @@ protected: // Window types from WindowManager.LayoutParams const InputWindowInfo::Type mWindowType; - // Can only be accessed with the SF state lock held. - std::shared_ptr<frametimeline::SurfaceFrame> mSurfaceFrame; - // The owner of the layer. If created from a non system process, it will be the calling uid. // If created from a system process, the value can be passed in. uid_t mOwnerUid; @@ -1189,7 +1194,7 @@ private: sp<Layer> getRootLayer(); // Fills in the frame and transform info for the InputWindowInfo - void fillInputFrameInfo(InputWindowInfo& info); + void fillInputFrameInfo(InputWindowInfo& info, const ui::Transform& toPhysicalDisplay); // Cached properties computed from drawing state // Effective transform taking into account parent transforms and any parent scaling, which is diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 49ffc81359..b29c624d00 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -125,6 +125,9 @@ std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumbe GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE, "RefreshRateOverlayBuffer"); + const status_t bufferStatus = buffer->initCheck(); + LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d", + bufferStatus); uint8_t* pixels; buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); // Clear buffer content diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 9186538c0a..09615f920d 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -447,6 +447,9 @@ void RegionSamplingThread::captureSample() { GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; buffer = new GraphicBuffer(sampledBounds.getWidth(), sampledBounds.getHeight(), PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread"); + const status_t bufferStatus = buffer->initCheck(); + LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureSample: Buffer failed to allocate: %d", + bufferStatus); } const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index e7a44a70d5..de11c16f64 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -64,7 +64,7 @@ using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType; using RefreshRate = RefreshRateConfigs::RefreshRate; std::string RefreshRate::toString() const { - return base::StringPrintf("{id=%zu, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}", + return base::StringPrintf("{id=%d, hwcId=%d, fps=%.2f, width=%d, height=%d group=%d}", getModeId().value(), mode->getHwcId(), getFps().getValue(), mode->getWidth(), mode->getHeight(), getModeGroup()); } @@ -89,7 +89,7 @@ std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) { } std::string RefreshRateConfigs::Policy::toString() const { - return base::StringPrintf("default mode ID: %zu, allowGroupSwitching = %d" + return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d" ", primary range: %s, app request range: %s", defaultMode.value(), allowGroupSwitching, primaryRange.toString().c_str(), appRequestRange.toString().c_str()); @@ -627,6 +627,8 @@ void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes, std::vector<const RefreshRate*> sortedModes; getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedModes); + // Reset the policy because the old one may no longer be valid. + mDisplayManagerPolicy = {}; mDisplayManagerPolicy.defaultMode = currentModeId; mMinSupportedRefreshRate = sortedModes.front(); mMaxSupportedRefreshRate = sortedModes.back(); @@ -722,7 +724,7 @@ void RefreshRateConfigs::getSortedRefreshRateListLocked( outRefreshRates->reserve(mRefreshRates.size()); for (const auto& [type, refreshRate] : mRefreshRates) { if (shouldAddRefreshRate(*refreshRate)) { - ALOGV("getSortedRefreshRateListLocked: mode %zu added to list policy", + ALOGV("getSortedRefreshRateListLocked: mode %d added to list policy", refreshRate->modeId.value()); outRefreshRates->push_back(refreshRate.get()); } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 5237516f17..0e9eba731a 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -174,6 +174,8 @@ public: // Stores the preferred refresh rate that an app should run at. // FrameRateOverride.refreshRateHz == 0 means no preference. void setPreferredRefreshRateForUid(FrameRateOverride) EXCLUDES(mFrameRateOverridesMutex); + // Retrieves the overridden refresh rate for a given uid. + std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mFrameRateOverridesMutex); private: friend class TestableScheduler; @@ -236,7 +238,6 @@ private: Fps displayRefreshRate) REQUIRES(mFeatureStateLock) EXCLUDES(mFrameRateOverridesMutex); - std::optional<Fps> getFrameRateOverride(uid_t uid) const EXCLUDES(mFrameRateOverridesMutex); impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const; // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 04e9a6d3ff..472dee2d34 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -68,12 +68,13 @@ #include <ui/ColorSpace.h> #include <ui/DebugUtils.h> #include <ui/DisplayId.h> -#include <ui/DisplayInfo.h> #include <ui/DisplayMode.h> #include <ui/DisplayStatInfo.h> #include <ui/DisplayState.h> +#include <ui/DynamicDisplayInfo.h> #include <ui/GraphicBufferAllocator.h> #include <ui/PixelFormat.h> +#include <ui/StaticDisplayInfo.h> #include <utils/StopWatch.h> #include <utils/String16.h> #include <utils/String8.h> @@ -854,7 +855,8 @@ status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::Di return NO_ERROR; } -status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo* info) { +status_t SurfaceFlinger::getStaticDisplayInfo(const sp<IBinder>& displayToken, + ui::StaticDisplayInfo* info) { if (!displayToken || !info) { return BAD_VALUE; } @@ -875,7 +877,7 @@ status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, Display if (mEmulatedDisplayDensity) { info->density = mEmulatedDisplayDensity; } else { - info->density = info->connectionType == DisplayConnectionType::Internal + info->density = info->connectionType == ui::DisplayConnectionType::Internal ? mInternalDisplayDensity : FALLBACK_DENSITY; } @@ -887,9 +889,9 @@ status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, Display return NO_ERROR; } -status_t SurfaceFlinger::getDisplayModes(const sp<IBinder>& displayToken, - Vector<ui::DisplayMode>* modes) { - if (!displayToken || !modes) { +status_t SurfaceFlinger::getDynamicDisplayInfo(const sp<IBinder>& displayToken, + ui::DynamicDisplayInfo* info) { + if (!displayToken || !info) { return BAD_VALUE; } @@ -900,16 +902,25 @@ status_t SurfaceFlinger::getDisplayModes(const sp<IBinder>& displayToken, return NAME_NOT_FOUND; } - modes->clear(); + info->activeDisplayModeId = static_cast<int32_t>(display->getActiveMode()->getId().value()); + if (display->isPrimary()) { + if (const auto mode = getDesiredActiveMode()) { + info->activeDisplayModeId = static_cast<int32_t>(mode->modeId.value()); + } + } - for (const auto& supportedMode : display->getSupportedModes()) { - ui::DisplayMode mode; + const auto& supportedModes = display->getSupportedModes(); + info->supportedDisplayModes.clear(); + info->supportedDisplayModes.reserve(supportedModes.size()); + for (const auto& mode : supportedModes) { + ui::DisplayMode outMode; + outMode.id = static_cast<int32_t>(mode->getId().value()); - auto width = supportedMode->getWidth(); - auto height = supportedMode->getHeight(); + auto width = mode->getWidth(); + auto height = mode->getHeight(); - auto xDpi = supportedMode->getDpiX(); - auto yDpi = supportedMode->getDpiY(); + auto xDpi = mode->getDpiX(); + auto yDpi = mode->getDpiY(); if (display->isPrimary() && (internalDisplayOrientation == ui::ROTATION_90 || @@ -918,24 +929,24 @@ status_t SurfaceFlinger::getDisplayModes(const sp<IBinder>& displayToken, std::swap(xDpi, yDpi); } - mode.resolution = ui::Size(width, height); + outMode.resolution = ui::Size(width, height); if (mEmulatedDisplayDensity) { - mode.xDpi = mEmulatedDisplayDensity; - mode.yDpi = mEmulatedDisplayDensity; + outMode.xDpi = mEmulatedDisplayDensity; + outMode.yDpi = mEmulatedDisplayDensity; } else { - mode.xDpi = xDpi; - mode.yDpi = yDpi; + outMode.xDpi = xDpi; + outMode.yDpi = yDpi; } - const nsecs_t period = supportedMode->getVsyncPeriod(); - mode.refreshRate = Fps::fromPeriodNsecs(period).getValue(); + const nsecs_t period = mode->getVsyncPeriod(); + outMode.refreshRate = Fps::fromPeriodNsecs(period).getValue(); const auto vsyncConfigSet = - mVsyncConfiguration->getConfigsForRefreshRate(Fps(mode.refreshRate)); - mode.appVsyncOffset = vsyncConfigSet.late.appOffset; - mode.sfVsyncOffset = vsyncConfigSet.late.sfOffset; - mode.group = supportedMode->getGroup(); + mVsyncConfiguration->getConfigsForRefreshRate(Fps(outMode.refreshRate)); + outMode.appVsyncOffset = vsyncConfigSet.late.appOffset; + outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset; + outMode.group = mode->getGroup(); // This is how far in advance a buffer must be queued for // presentation at a given time. If you want a buffer to appear @@ -949,11 +960,24 @@ status_t SurfaceFlinger::getDisplayModes(const sp<IBinder>& displayToken, // // We add an additional 1ms to allow for processing time and // differences between the ideal and actual refresh rate. - mode.presentationDeadline = period - mode.sfVsyncOffset + 1000000; + outMode.presentationDeadline = period - outMode.sfVsyncOffset + 1000000; - modes->push_back(mode); + info->supportedDisplayModes.push_back(outMode); } + info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; + const auto displayId = display->getPhysicalId(); + info->supportedColorModes = getDisplayColorModes(displayId); + + info->hdrCapabilities = display->getHdrCapabilities(); + info->autoLowLatencyModeSupported = + getHwComposer().hasDisplayCapability(displayId, + hal::DisplayCapability::AUTO_LOW_LATENCY_MODE); + std::vector<hal::ContentType> types; + getHwComposer().getSupportedContentTypes(displayId, &types); + info->gameContentTypeSupported = std::any_of(types.begin(), types.end(), [](auto type) { + return type == hal::ContentType::GAME; + }); return NO_ERROR; } @@ -966,31 +990,6 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* st return NO_ERROR; } -int SurfaceFlinger::getActiveDisplayModeId(const sp<IBinder>& displayToken) { - int activeMode; - bool isPrimary; - - { - Mutex::Autolock lock(mStateLock); - - if (const auto display = getDisplayDeviceLocked(displayToken)) { - activeMode = display->getActiveMode()->getId().value(); - isPrimary = display->isPrimary(); - } else { - ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); - return NAME_NOT_FOUND; - } - } - - if (isPrimary) { - if (const auto mode = getDesiredActiveMode()) { - return mode->modeId.value(); - } - } - - return activeMode; -} - void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) { ATRACE_CALL(); auto refreshRate = mRefreshRateConfigs->getRefreshRateFromModeId(info.modeId); @@ -1082,7 +1081,7 @@ void SurfaceFlinger::setActiveModeInternal() { const auto upcomingMode = display->getMode(mUpcomingActiveMode.modeId); if (!upcomingMode) { - ALOGW("Upcoming active mode is no longer supported. Mode ID = %zu", + ALOGW("Upcoming active mode is no longer supported. Mode ID = %d", mUpcomingActiveMode.modeId.value()); // TODO(b/159590486) Handle the error better. Some parts of SurfaceFlinger may // have been already updated with the upcoming active mode. @@ -1142,13 +1141,13 @@ void SurfaceFlinger::performSetActiveMode() { const auto display = getDefaultDisplayDeviceLocked(); const auto desiredMode = display->getMode(desiredActiveMode->modeId); if (!desiredMode) { - ALOGW("Desired display mode is no longer supported. Mode ID = %zu", + ALOGW("Desired display mode is no longer supported. Mode ID = %d", desiredActiveMode->modeId.value()); clearDesiredActiveModeState(); return; } const auto refreshRate = desiredMode->getFps(); - ALOGV("%s changing active mode to %zu(%s)", __FUNCTION__, desiredMode->getId().value(), + ALOGV("%s changing active mode to %d(%s)", __FUNCTION__, desiredMode->getId().value(), to_string(refreshRate).c_str()); if (!display || display->getActiveMode()->getId() == desiredActiveMode->modeId) { @@ -1190,39 +1189,20 @@ void SurfaceFlinger::performSetActiveMode() { mSetActiveModePending = true; } -status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken, - Vector<ColorMode>* outColorModes) { - if (!displayToken || !outColorModes) { - return BAD_VALUE; - } - - std::vector<ColorMode> modes; - bool isInternalDisplay = false; - { - ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId); - - const auto displayId = getPhysicalDisplayIdLocked(displayToken); - if (!displayId) { - return NAME_NOT_FOUND; - } - - modes = getHwComposer().getColorModes(*displayId); - isInternalDisplay = displayId == getInternalDisplayIdLocked(); - } - outColorModes->clear(); +std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(PhysicalDisplayId displayId) { + auto modes = getHwComposer().getColorModes(displayId); + bool isInternalDisplay = displayId == getInternalDisplayIdLocked(); // If it's built-in display and the configuration claims it's not wide color capable, // filter out all wide color modes. The typical reason why this happens is that the // hardware is not good enough to support GPU composition of wide color, and thus the // OEMs choose to disable this capability. if (isInternalDisplay && !hasWideColorDisplay) { - std::remove_copy_if(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes), - isWideColorMode); - } else { - std::copy(modes.cbegin(), modes.cend(), std::back_inserter(*outColorModes)); + const auto newEnd = std::remove_if(modes.begin(), modes.end(), isWideColorMode); + modes.erase(newEnd, modes.end()); } - return NO_ERROR; + return modes; } status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayToken, @@ -1240,19 +1220,14 @@ status_t SurfaceFlinger::getDisplayNativePrimaries(const sp<IBinder>& displayTok return NO_ERROR; } -ColorMode SurfaceFlinger::getActiveColorMode(const sp<IBinder>& displayToken) { - Mutex::Autolock lock(mStateLock); - - if (const auto display = getDisplayDeviceLocked(displayToken)) { - return display->getCompositionDisplay()->getState().colorMode; - } - return static_cast<ColorMode>(BAD_VALUE); -} - status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, ColorMode mode) { schedule([=]() MAIN_THREAD { - Vector<ColorMode> modes; - getDisplayColorModes(displayToken, &modes); + const auto displayId = getPhysicalDisplayIdLocked(displayToken); + if (!displayId) { + ALOGE("Invalid display token %p", displayToken.get()); + return; + } + const auto modes = getDisplayColorModes(*displayId); bool exists = std::find(std::begin(modes), std::end(modes), mode) != std::end(modes); if (mode < ColorMode::NATIVE || !exists) { ALOGE("Attempt to set invalid active color mode %s (%d) for display token %p", @@ -1277,24 +1252,6 @@ status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, Col return NO_ERROR; } -status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken, - bool* outSupport) const { - if (!displayToken) { - return BAD_VALUE; - } - - Mutex::Autolock lock(mStateLock); - - const auto displayId = getPhysicalDisplayIdLocked(displayToken); - if (!displayId) { - return NAME_NOT_FOUND; - } - *outSupport = - getHwComposer().hasDisplayCapability(*displayId, - hal::DisplayCapability::AUTO_LOW_LATENCY_MODE); - return NO_ERROR; -} - void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) { static_cast<void>(schedule([=]() MAIN_THREAD { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { @@ -1305,27 +1262,6 @@ void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool })); } -status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken, - bool* outSupport) const { - if (!displayToken) { - return BAD_VALUE; - } - - Mutex::Autolock lock(mStateLock); - - const auto displayId = getPhysicalDisplayIdLocked(displayToken); - if (!displayId) { - return NAME_NOT_FOUND; - } - - std::vector<hal::ContentType> types; - getHwComposer().getSupportedContentTypes(*displayId, &types); - - *outSupport = std::any_of(types.begin(), types.end(), - [](auto type) { return type == hal::ContentType::GAME; }); - return NO_ERROR; -} - void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) { static_cast<void>(schedule([=]() MAIN_THREAD { if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) { @@ -1349,28 +1285,6 @@ status_t SurfaceFlinger::getAnimationFrameStats(FrameStats* outStats) const { return NO_ERROR; } -status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken, - HdrCapabilities* outCapabilities) const { - Mutex::Autolock lock(mStateLock); - - const auto display = getDisplayDeviceLocked(displayToken); - if (!display) { - ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get()); - return NAME_NOT_FOUND; - } - - // At this point the DisplayDevice should already be set up, - // meaning the luminance information is already queried from - // hardware composer and stored properly. - const HdrCapabilities& capabilities = display->getHdrCapabilities(); - *outCapabilities = HdrCapabilities(capabilities.getSupportedHdrTypes(), - capabilities.getDesiredMaxLuminance(), - capabilities.getDesiredMaxAverageLuminance(), - capabilities.getDesiredMinLuminance()); - - return NO_ERROR; -} - status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, @@ -1640,7 +1554,7 @@ void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate, // Don't do any updating if the current fps is the same as the new one. if (!isDisplayModeAllowed(refreshRate.getModeId())) { - ALOGV("Skipping mode %zu as it is not part of allowed modes", + ALOGV("Skipping mode %d as it is not part of allowed modes", refreshRate.getModeId().value()); return; } @@ -1899,7 +1813,7 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT const bool tracePreComposition = mTracingEnabled && !mTracePostComposition; ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition); - mFrameTimeline->setSfWakeUp(vsyncId, frameStart, stats.vsyncPeriod); + mFrameTimeline->setSfWakeUp(vsyncId, frameStart, Fps::fromPeriodNsecs(stats.vsyncPeriod)); refreshNeeded = handleMessageTransaction(); refreshNeeded |= handleMessageInvalidate(); @@ -2360,7 +2274,7 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { DisplayModes SurfaceFlinger::loadSupportedDisplayModes(PhysicalDisplayId displayId) const { const auto hwcModes = getHwComposer().getModes(displayId); DisplayModes modes; - size_t nextModeId = 0; + int32_t nextModeId = 0; for (const auto& hwcMode : hwcModes) { modes.push_back(DisplayMode::Builder(hwcMode.hwcId) .setId(DisplayModeId{nextModeId++}) @@ -2906,15 +2820,32 @@ void SurfaceFlinger::updateInputFlinger() { mInputWindowCommands.clear(); } +bool enablePerWindowInputRotation() { + static bool value = + android::base::GetBoolProperty("persist.debug.per_window_input_rotation", false); + return value; +} + void SurfaceFlinger::updateInputWindowInfo() { std::vector<InputWindowInfo> inputInfos; mDrawingState.traverseInReverseZOrder([&](Layer* layer) { - if (layer->needsInputInfo()) { - // When calculating the screen bounds we ignore the transparent region since it may - // result in an unwanted offset. - inputInfos.push_back(layer->fillInputInfo()); + if (!layer->needsInputInfo()) return; + sp<DisplayDevice> display; + if (enablePerWindowInputRotation()) { + for (const auto& pair : ON_MAIN_THREAD(mDisplays)) { + const auto& displayDevice = pair.second; + if (!displayDevice->getCompositionDisplay() + ->belongsInOutput(layer->getLayerStack(), + layer->getPrimaryDisplayOnly())) { + continue; + } + display = displayDevice; + } } + // When calculating the screen bounds we ignore the transparent region since it may + // result in an unwanted offset. + inputInfos.push_back(layer->fillInputInfo(display)); }); mInputFlinger->setInputWindows(inputInfos, @@ -3950,6 +3881,11 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (what & layer_state_t::eAutoRefreshChanged) { layer->setAutoRefresh(s.autoRefresh); } + if (what & layer_state_t::eStretchChanged) { + if (layer->setStretchEffect(s.stretchEffect)) { + flags |= eTraversalNeeded; + } + } // This has to happen after we reparent children because when we reparent to null we remove // child layers from current state and remove its relative z. If the children are reparented in // the same transaction, then we have to make sure we reparent the children first so we do not @@ -4846,6 +4782,8 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co getRenderEngine().dump(result); + result.append("ClientCache state:\n"); + ClientCache::getInstance().dump(result); DebugEGLImageTracker::getInstance()->dump(result); if (const auto display = getDefaultDisplayDeviceLocked()) { @@ -5008,7 +4946,8 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case GET_PHYSICAL_DISPLAY_TOKEN: case GET_DISPLAY_COLOR_MODES: case GET_DISPLAY_NATIVE_PRIMARIES: - case GET_DISPLAY_INFO: + case GET_STATIC_DISPLAY_INFO: + case GET_DYNAMIC_DISPLAY_INFO: case GET_DISPLAY_MODES: case GET_DISPLAY_STATE: case GET_DISPLAY_STATS: @@ -5846,6 +5785,10 @@ status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), static_cast<android_pixel_format>(reqPixelFormat), 1 /* layerCount */, usage, "screenshot"); + + const status_t bufferStatus = buffer->initCheck(); + LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d", + bufferStatus); return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, false /* regionSampling */, grayscale, captureListener); } @@ -6136,27 +6079,25 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( ? mRefreshRateConfigs->getRefreshRateFromModeId(*modeId) // NOTE: Choose the default mode ID, if Scheduler doesn't have one in mind. : mRefreshRateConfigs->getRefreshRateFromModeId(currentPolicy.defaultMode); - ALOGV("trying to switch to Scheduler preferred mode %zu (%s)", + ALOGV("trying to switch to Scheduler preferred mode %d (%s)", preferredRefreshRate.getModeId().value(), preferredRefreshRate.getName().c_str()); if (isDisplayModeAllowed(preferredRefreshRate.getModeId())) { - ALOGV("switching to Scheduler preferred display mode %zu", + ALOGV("switching to Scheduler preferred display mode %d", preferredRefreshRate.getModeId().value()); setDesiredActiveMode({preferredRefreshRate.getModeId(), Scheduler::ModeEvent::Changed}); } else { - LOG_ALWAYS_FATAL("Desired display mode not allowed: %zu", + LOG_ALWAYS_FATAL("Desired display mode not allowed: %d", preferredRefreshRate.getModeId().value()); } return NO_ERROR; } -status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, - size_t defaultMode, bool allowGroupSwitching, - float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { +status_t SurfaceFlinger::setDesiredDisplayModeSpecs( + const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode, bool allowGroupSwitching, + float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, + float appRequestRefreshRateMax) { ATRACE_CALL(); if (!displayToken) { @@ -6187,10 +6128,13 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecs(const sp<IBinder>& displayTo return future.get(); } -status_t SurfaceFlinger::getDesiredDisplayModeSpecs( - const sp<IBinder>& displayToken, size_t* outDefaultMode, bool* outAllowGroupSwitching, - float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) { +status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + ui::DisplayModeId* outDefaultMode, + bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, + float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, + float* outAppRequestRefreshRateMax) { ATRACE_CALL(); if (!displayToken || !outDefaultMode || !outPrimaryRefreshRateMin || diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index a62d0b9a95..d9af7509a1 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -552,26 +552,20 @@ private: const sp<IScreenCaptureListener>& captureListener) override; status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats) override; - status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) override; - status_t getDisplayInfo(const sp<IBinder>& displayToken, DisplayInfo*) override; - status_t getDisplayModes(const sp<IBinder>& displayToken, Vector<ui::DisplayMode>*) override; - int getActiveDisplayModeId(const sp<IBinder>& displayToken) override; - status_t getDisplayColorModes(const sp<IBinder>& displayToken, Vector<ui::ColorMode>*) override; + status_t getDisplayState(const sp<IBinder>& displayToken, ui::DisplayState*) + EXCLUDES(mStateLock) override; + status_t getStaticDisplayInfo(const sp<IBinder>& displayToken, ui::StaticDisplayInfo*) + EXCLUDES(mStateLock) override; + status_t getDynamicDisplayInfo(const sp<IBinder>& displayToken, ui::DynamicDisplayInfo*) + EXCLUDES(mStateLock) override; status_t getDisplayNativePrimaries(const sp<IBinder>& displayToken, ui::DisplayPrimaries&) override; - ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override; status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override; - status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken, - bool* outSupported) const override; void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override; - status_t getGameContentTypeSupport(const sp<IBinder>& displayToken, - bool* outSupported) const override; void setGameContentType(const sp<IBinder>& displayToken, bool on) override; void setPowerMode(const sp<IBinder>& displayToken, int mode) override; status_t clearAnimationFrameStats() override; status_t getAnimationFrameStats(FrameStats* outStats) const override; - status_t getHdrCapabilities(const sp<IBinder>& displayToken, - HdrCapabilities* outCapabilities) const override; status_t enableVSyncInjections(bool enable) override; status_t injectVSync(nsecs_t when) override; status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) override; @@ -594,11 +588,13 @@ private: status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle, const sp<IRegionSamplingListener>& listener) override; status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override; - status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t displayModeId, - bool allowGroupSwitching, float primaryRefreshRateMin, - float primaryRefreshRateMax, float appRequestRefreshRateMin, + status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + ui::DisplayModeId displayModeId, bool allowGroupSwitching, + float primaryRefreshRateMin, float primaryRefreshRateMax, + float appRequestRefreshRateMin, float appRequestRefreshRateMax) override; - status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, size_t* outDefaultMode, + status_t getDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, + ui::DisplayModeId* outDefaultMode, bool* outAllowGroupSwitching, float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, @@ -1054,6 +1050,9 @@ private: return std::nullopt; } + std::vector<ui::ColorMode> getDisplayColorModes(PhysicalDisplayId displayId) + REQUIRES(mStateLock); + static int calculateExtraBufferCount(Fps maxSupportedRefreshRate, std::chrono::nanoseconds presentLatency); diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 61005c92df..b0413f1c92 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -499,6 +499,9 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, if (state.what & layer_state_t::eShadowRadiusChanged) { addShadowRadiusLocked(transaction, layerId, state.shadowRadius); } + if (state.what & layer_state_t::eStretchChanged) { + ALOGW("SurfaceInterceptor not implemented for eStretchChanged"); + } } void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction, diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp index 1d1f0c5cf6..b4d8a9adce 100644 --- a/services/surfaceflinger/SurfaceTracing.cpp +++ b/services/surfaceflinger/SurfaceTracing.cpp @@ -71,12 +71,14 @@ status_t SurfaceTracing::writeToFile() { } void SurfaceTracing::notify(const char* where) { + std::scoped_lock lock(mTraceLock); if (mEnabled) { runner->notify(where); } } void SurfaceTracing::notifyLocked(const char* where) { + std::scoped_lock lock(mTraceLock); if (mEnabled) { runner->notifyLocked(where); } diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h index 497ebd3c50..15a503d834 100644 --- a/services/surfaceflinger/SurfaceTracing.h +++ b/services/surfaceflinger/SurfaceTracing.h @@ -81,8 +81,8 @@ private: SurfaceFlinger& mFlinger; mutable std::mutex mTraceLock; - bool mEnabled = false; - std::unique_ptr<Runner> runner; + bool mEnabled GUARDED_BY(mTraceLock) = false; + std::unique_ptr<Runner> runner GUARDED_BY(mTraceLock); struct Config { uint32_t flags = TRACE_CRITICAL | TRACE_INPUT | TRACE_SYNC; diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp index 0a23da2e9c..62fddb4f47 100644 --- a/services/surfaceflinger/TimeStats/Android.bp +++ b/services/surfaceflinger/TimeStats/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libtimestats", srcs: [ diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 5d387d68e8..100354adac 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -105,53 +105,64 @@ std::string frameRateVoteToProtoByteString(float refreshRate, int frameRateCompa AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventList* data) { std::lock_guard<std::mutex> lock(mMutex); - if (mTimeStats.statsStart == 0) { + if (mTimeStats.statsStartLegacy == 0) { return AStatsManager_PULL_SKIP; } flushPowerTimeLocked(); - AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data); - mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); - mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFrames); - mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFrames); - mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFrames); - mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTime); - mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresent.totalTime()); - mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCount); - std::string frameDurationBytes = - histogramToProtoByteString(mTimeStats.frameDuration.hist, mMaxPulledHistogramBuckets); - mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(), - frameDurationBytes.size()); - std::string renderEngineTimingBytes = - histogramToProtoByteString(mTimeStats.renderEngineTiming.hist, - mMaxPulledHistogramBuckets); - mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)renderEngineTimingBytes.c_str(), - renderEngineTimingBytes.size()); - - mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalFrames); - mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalJankyFrames); - mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongCpu); - mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFLongGpu); - mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalSFUnattributed); - mStatsDelegate->statsEventWriteInt32(event, mTimeStats.jankPayload.totalAppUnattributed); - - // TODO: populate these with real values - mStatsDelegate->statsEventWriteInt32(event, 0); // total_janky_frames_sf_scheduling - mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_sf_prediction_error - mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_app_buffer_stuffing - mStatsDelegate->statsEventWriteInt32(event, 0); // display_refresh_rate_bucket - std::string sfDeadlineMissedBytes = - histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(), - mMaxPulledHistogramBuckets); - mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)sfDeadlineMissedBytes.c_str(), - sfDeadlineMissedBytes.size()); // sf_deadline_misses - std::string sfPredictionErrorBytes = - histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(), - mMaxPulledHistogramBuckets); - mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)sfPredictionErrorBytes.c_str(), - sfPredictionErrorBytes.size()); // sf_prediction_errors - mStatsDelegate->statsEventWriteInt32(event, 0); // render_rate_bucket - mStatsDelegate->statsEventBuild(event); + for (const auto& globalSlice : mTimeStats.stats) { + AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data); + mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_GLOBAL_INFO); + mStatsDelegate->statsEventWriteInt64(event, mTimeStats.totalFramesLegacy); + mStatsDelegate->statsEventWriteInt64(event, mTimeStats.missedFramesLegacy); + mStatsDelegate->statsEventWriteInt64(event, mTimeStats.clientCompositionFramesLegacy); + mStatsDelegate->statsEventWriteInt64(event, mTimeStats.displayOnTimeLegacy); + mStatsDelegate->statsEventWriteInt64(event, mTimeStats.presentToPresentLegacy.totalTime()); + mStatsDelegate->statsEventWriteInt32(event, mTimeStats.displayEventConnectionsCountLegacy); + std::string frameDurationBytes = + histogramToProtoByteString(mTimeStats.frameDurationLegacy.hist, + mMaxPulledHistogramBuckets); + mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameDurationBytes.c_str(), + frameDurationBytes.size()); + std::string renderEngineTimingBytes = + histogramToProtoByteString(mTimeStats.renderEngineTimingLegacy.hist, + mMaxPulledHistogramBuckets); + mStatsDelegate->statsEventWriteByteArray(event, + (const uint8_t*)renderEngineTimingBytes.c_str(), + renderEngineTimingBytes.size()); + + mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalFrames); + mStatsDelegate->statsEventWriteInt32(event, + globalSlice.second.jankPayload.totalJankyFrames); + mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalSFLongCpu); + mStatsDelegate->statsEventWriteInt32(event, globalSlice.second.jankPayload.totalSFLongGpu); + mStatsDelegate->statsEventWriteInt32(event, + globalSlice.second.jankPayload.totalSFUnattributed); + mStatsDelegate->statsEventWriteInt32(event, + globalSlice.second.jankPayload.totalAppUnattributed); + mStatsDelegate->statsEventWriteInt32(event, + globalSlice.second.jankPayload.totalSFScheduling); + mStatsDelegate->statsEventWriteInt32(event, + globalSlice.second.jankPayload.totalSFPredictionError); + mStatsDelegate->statsEventWriteInt32(event, + globalSlice.second.jankPayload.totalAppBufferStuffing); + mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.displayRefreshRateBucket); + std::string sfDeadlineMissedBytes = + histogramToProtoByteString(globalSlice.second.displayDeadlineDeltas.hist, + mMaxPulledHistogramBuckets); + mStatsDelegate->statsEventWriteByteArray(event, + (const uint8_t*)sfDeadlineMissedBytes.c_str(), + sfDeadlineMissedBytes.size()); + std::string sfPredictionErrorBytes = + histogramToProtoByteString(globalSlice.second.displayPresentDeltas.hist, + mMaxPulledHistogramBuckets); + mStatsDelegate->statsEventWriteByteArray(event, + (const uint8_t*)sfPredictionErrorBytes.c_str(), + sfPredictionErrorBytes.size()); + mStatsDelegate->statsEventWriteInt32(event, globalSlice.first.renderRateBucket); + mStatsDelegate->statsEventBuild(event); + } + clearGlobalLocked(); return AStatsManager_PULL_SUCCESS; @@ -160,9 +171,18 @@ AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventLi AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventList* data) { std::lock_guard<std::mutex> lock(mMutex); - std::vector<TimeStatsHelper::TimeStatsLayer const*> dumpStats; - for (const auto& ele : mTimeStats.stats) { - dumpStats.push_back(&ele.second); + std::vector<TimeStatsHelper::TimeStatsLayer*> dumpStats; + uint32_t numLayers = 0; + for (const auto& globalSlice : mTimeStats.stats) { + numLayers += globalSlice.second.stats.size(); + } + + dumpStats.reserve(numLayers); + + for (auto& globalSlice : mTimeStats.stats) { + for (auto& layerSlice : globalSlice.second.stats) { + dumpStats.push_back(&layerSlice.second); + } } std::sort(dumpStats.begin(), dumpStats.end(), @@ -175,7 +195,7 @@ AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventLis dumpStats.resize(mMaxPulledLayers); } - for (const auto& layer : dumpStats) { + for (auto& layer : dumpStats) { AStatsEvent* event = mStatsDelegate->addStatsEventToPullData(data); mStatsDelegate->statsEventSetAtomId(event, android::util::SURFACEFLINGER_STATS_LAYER_INFO); mStatsDelegate->statsEventWriteString8(event, layer->layerName.c_str()); @@ -203,22 +223,21 @@ AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventLis mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu); mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed); mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed); - - // TODO: populate these with real values - mStatsDelegate->statsEventWriteInt32(event, 0); // total_janky_frames_sf_scheduling - mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_sf_prediction_error - mStatsDelegate->statsEventWriteInt32(event, 0); // total_jank_frames_app_buffer_stuffing - mStatsDelegate->statsEventWriteInt32(event, 0); // display_refresh_rate_bucket - mStatsDelegate->statsEventWriteInt32(event, 0); // render_rate_bucket + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFScheduling); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFPredictionError); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppBufferStuffing); + mStatsDelegate->statsEventWriteInt32( + event, layer->displayRefreshRateBucket); // display_refresh_rate_bucket + mStatsDelegate->statsEventWriteInt32(event, layer->renderRateBucket); // render_rate_bucket std::string frameRateVoteBytes = frameRateVoteToProtoByteString(0.0, 0, 0); mStatsDelegate->statsEventWriteByteArray(event, (const uint8_t*)frameRateVoteBytes.c_str(), frameRateVoteBytes.size()); // set_frame_rate_vote std::string appDeadlineMissedBytes = - histogramToProtoByteString(std::unordered_map<int32_t, int32_t>(), + histogramToProtoByteString(layer->deltas["appDeadlineDeltas"].hist, mMaxPulledHistogramBuckets); - mStatsDelegate - ->statsEventWriteByteArray(event, (const uint8_t*)appDeadlineMissedBytes.c_str(), - appDeadlineMissedBytes.size()); // app_deadline_misses + mStatsDelegate->statsEventWriteByteArray(event, + (const uint8_t*)appDeadlineMissedBytes.c_str(), + appDeadlineMissedBytes.size()); mStatsDelegate->statsEventBuild(event); } @@ -310,7 +329,7 @@ void TimeStats::incrementTotalFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - mTimeStats.totalFrames++; + mTimeStats.totalFramesLegacy++; } void TimeStats::incrementMissedFrames() { @@ -319,7 +338,7 @@ void TimeStats::incrementMissedFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - mTimeStats.missedFrames++; + mTimeStats.missedFramesLegacy++; } void TimeStats::incrementClientCompositionFrames() { @@ -328,7 +347,7 @@ void TimeStats::incrementClientCompositionFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - mTimeStats.clientCompositionFrames++; + mTimeStats.clientCompositionFramesLegacy++; } void TimeStats::incrementClientCompositionReusedFrames() { @@ -337,7 +356,7 @@ void TimeStats::incrementClientCompositionReusedFrames() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - mTimeStats.clientCompositionReusedFrames++; + mTimeStats.clientCompositionReusedFramesLegacy++; } void TimeStats::incrementRefreshRateSwitches() { @@ -346,7 +365,7 @@ void TimeStats::incrementRefreshRateSwitches() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - mTimeStats.refreshRateSwitches++; + mTimeStats.refreshRateSwitchesLegacy++; } void TimeStats::incrementCompositionStrategyChanges() { @@ -355,7 +374,7 @@ void TimeStats::incrementCompositionStrategyChanges() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - mTimeStats.compositionStrategyChanges++; + mTimeStats.compositionStrategyChangesLegacy++; } void TimeStats::recordDisplayEventConnectionCount(int32_t count) { @@ -364,8 +383,8 @@ void TimeStats::recordDisplayEventConnectionCount(int32_t count) { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - mTimeStats.displayEventConnectionsCount = - std::max(mTimeStats.displayEventConnectionsCount, count); + mTimeStats.displayEventConnectionsCountLegacy = + std::max(mTimeStats.displayEventConnectionsCountLegacy, count); } static int32_t msBetween(nsecs_t start, nsecs_t end) { @@ -381,7 +400,7 @@ void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) { std::lock_guard<std::mutex> lock(mMutex); if (mPowerTime.powerMode == PowerMode::ON) { - mTimeStats.frameDuration.insert(msBetween(startTime, endTime)); + mTimeStats.frameDurationLegacy.insert(msBetween(startTime, endTime)); } } @@ -444,12 +463,22 @@ bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) { return true; } -void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) { +static int32_t clampToSmallestBucket(Fps fps, size_t bucketWidth) { + return (fps.getIntValue() / bucketWidth) * bucketWidth; +} + +void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate, + std::optional<Fps> renderRate) { ATRACE_CALL(); LayerRecord& layerRecord = mTimeStatsTracker[layerId]; TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord; std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords; + const int32_t refreshRateBucket = + clampToSmallestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH); + const int32_t renderRateBucket = + clampToSmallestBucket(renderRate ? *renderRate : displayRefreshRate, + RENDER_RATE_BUCKET_WIDTH); while (!timeRecords.empty()) { if (!recordReadyLocked(layerId, &timeRecords[0])) break; ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerId, @@ -458,11 +487,21 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) { if (prevTimeRecord.ready) { uid_t uid = layerRecord.uid; const std::string& layerName = layerRecord.layerName; - if (!mTimeStats.stats.count({uid, layerName})) { - mTimeStats.stats[{uid, layerName}].uid = uid; - mTimeStats.stats[{uid, layerName}].layerName = layerName; + TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket}; + if (!mTimeStats.stats.count(timelineKey)) { + mTimeStats.stats[timelineKey].key = timelineKey; } - TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}]; + + TimeStatsHelper::TimelineStats& displayStats = mTimeStats.stats[timelineKey]; + + TimeStatsHelper::LayerStatsKey layerKey = {uid, layerName}; + if (!displayStats.stats.count(layerKey)) { + displayStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket; + displayStats.stats[layerKey].renderRateBucket = renderRateBucket; + displayStats.stats[layerKey].uid = uid; + displayStats.stats[layerKey].layerName = layerName; + } + TimeStatsHelper::TimeStatsLayer& timeStatsLayer = displayStats.stats[layerKey]; timeStatsLayer.totalFrames++; timeStatsLayer.droppedFrames += layerRecord.droppedFrames; timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames; @@ -524,8 +563,16 @@ static bool layerNameIsValid(const std::string& layerName) { } bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) { - return mTimeStats.stats.count({uid, layerName}) > 0 || - mTimeStats.stats.size() < MAX_NUM_LAYER_STATS; + uint32_t layerRecords = 0; + for (const auto& record : mTimeStats.stats) { + if (record.second.stats.count({uid, layerName}) > 0) { + return true; + } + + layerRecords += record.second.stats.size(); + } + + return mTimeStats.stats.size() < MAX_NUM_LAYER_STATS; } void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, @@ -676,7 +723,8 @@ void TimeStats::setAcquireFence(int32_t layerId, uint64_t frameNumber, } } -void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) { +void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, + Fps displayRefreshRate, std::optional<Fps> renderRate) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -695,11 +743,12 @@ void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t pr layerRecord.waitData++; } - flushAvailableRecordsToStatsLocked(layerId); + flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate); } void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, - const std::shared_ptr<FenceTime>& presentFence) { + const std::shared_ptr<FenceTime>& presentFence, + Fps displayRefreshRate, std::optional<Fps> renderRate) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -719,16 +768,18 @@ void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, layerRecord.waitData++; } - flushAvailableRecordsToStatsLocked(layerId); + flushAvailableRecordsToStatsLocked(layerId, displayRefreshRate, renderRate); } +static const constexpr int32_t kValidJankyReason = JankType::DisplayHAL | + JankType::SurfaceFlingerCpuDeadlineMissed | JankType::SurfaceFlingerGpuDeadlineMissed | + JankType::AppDeadlineMissed | JankType::PredictionError | + JankType::SurfaceFlingerScheduling | JankType::BufferStuffing; + template <class T> static void updateJankPayload(T& t, int32_t reasons) { t.jankPayload.totalFrames++; - static const constexpr int32_t kValidJankyReason = JankType::SurfaceFlingerCpuDeadlineMissed | - JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed | - JankType::DisplayHAL; if (reasons & kValidJankyReason) { t.jankPayload.totalJankyFrames++; if ((reasons & JankType::SurfaceFlingerCpuDeadlineMissed) != 0) { @@ -743,19 +794,19 @@ static void updateJankPayload(T& t, int32_t reasons) { if ((reasons & JankType::AppDeadlineMissed) != 0) { t.jankPayload.totalAppUnattributed++; } + if ((reasons & JankType::PredictionError) != 0) { + t.jankPayload.totalSFPredictionError++; + } + if ((reasons & JankType::SurfaceFlingerScheduling) != 0) { + t.jankPayload.totalSFScheduling++; + } + if ((reasons & JankType::BufferStuffing) != 0) { + t.jankPayload.totalAppBufferStuffing++; + } } } -void TimeStats::incrementJankyFrames(int32_t reasons) { - if (!mEnabled.load()) return; - - ATRACE_CALL(); - std::lock_guard<std::mutex> lock(mMutex); - - updateJankPayload<TimeStatsHelper::TimeStatsGlobal>(mTimeStats, reasons); -} - -void TimeStats::incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) { +void TimeStats::incrementJankyFrames(const JankyFramesInfo& info) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -772,17 +823,44 @@ void TimeStats::incrementJankyFrames(uid_t uid, const std::string& layerName, in // TimeStats will flush the first present fence for a layer *before* FrameTimeline does so that // the first jank record is not dropped. - bool useDefaultLayerKey = false; static const std::string kDefaultLayerName = "none"; - if (!mTimeStats.stats.count({uid, layerName})) { - mTimeStats.stats[{uid, kDefaultLayerName}].uid = uid; - mTimeStats.stats[{uid, kDefaultLayerName}].layerName = kDefaultLayerName; - useDefaultLayerKey = true; + + const int32_t refreshRateBucket = + clampToSmallestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH); + const int32_t renderRateBucket = + clampToSmallestBucket(info.renderRate ? *info.renderRate : info.refreshRate, + RENDER_RATE_BUCKET_WIDTH); + const TimeStatsHelper::TimelineStatsKey timelineKey = {refreshRateBucket, renderRateBucket}; + + if (!mTimeStats.stats.count(timelineKey)) { + mTimeStats.stats[timelineKey].key = timelineKey; } - TimeStatsHelper::TimeStatsLayer& timeStatsLayer = - mTimeStats.stats[{uid, useDefaultLayerKey ? kDefaultLayerName : layerName}]; - updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons); + TimeStatsHelper::TimelineStats& timelineStats = mTimeStats.stats[timelineKey]; + + updateJankPayload<TimeStatsHelper::TimelineStats>(timelineStats, info.reasons); + + TimeStatsHelper::LayerStatsKey layerKey = {info.uid, info.layerName}; + if (!timelineStats.stats.count(layerKey)) { + layerKey = {info.uid, kDefaultLayerName}; + timelineStats.stats[layerKey].displayRefreshRateBucket = refreshRateBucket; + timelineStats.stats[layerKey].renderRateBucket = renderRateBucket; + timelineStats.stats[layerKey].uid = info.uid; + timelineStats.stats[layerKey].layerName = kDefaultLayerName; + } + + TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timelineStats.stats[layerKey]; + updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, info.reasons); + + if (info.reasons & kValidJankyReason) { + // TimeStats Histograms only retain positive values, so we don't need to check if these + // deadlines were really missed if we know that the frame had jank, since deadlines + // that were met will be dropped. + timelineStats.displayDeadlineDeltas.insert(static_cast<int32_t>(info.displayDeadlineDelta)); + timelineStats.displayPresentDeltas.insert(static_cast<int32_t>(info.displayPresentJitter)); + timeStatsLayer.deltas["appDeadlineDeltas"].insert( + static_cast<int32_t>(info.appDeadlineDelta)); + } } void TimeStats::onDestroy(int32_t layerId) { @@ -823,7 +901,7 @@ void TimeStats::flushPowerTimeLocked() { switch (mPowerTime.powerMode) { case PowerMode::ON: - mTimeStats.displayOnTime += elapsedTime; + mTimeStats.displayOnTimeLegacy += elapsedTime; break; case PowerMode::OFF: case PowerMode::DOZE: @@ -852,10 +930,10 @@ void TimeStats::setPowerMode(PowerMode powerMode) { void TimeStats::recordRefreshRate(uint32_t fps, nsecs_t duration) { std::lock_guard<std::mutex> lock(mMutex); - if (mTimeStats.refreshRateStats.count(fps)) { - mTimeStats.refreshRateStats[fps] += duration; + if (mTimeStats.refreshRateStatsLegacy.count(fps)) { + mTimeStats.refreshRateStatsLegacy[fps] += duration; } else { - mTimeStats.refreshRateStats.insert({fps, duration}); + mTimeStats.refreshRateStatsLegacy.insert({fps, duration}); } } @@ -881,7 +959,7 @@ void TimeStats::flushAvailableGlobalRecordsToStatsLocked() { msBetween(mGlobalRecord.prevPresentTime, curPresentTime); ALOGV("Global present2present[%d] prev[%" PRId64 "] curr[%" PRId64 "]", presentToPresentMs, mGlobalRecord.prevPresentTime, curPresentTime); - mTimeStats.presentToPresent.insert(presentToPresentMs); + mTimeStats.presentToPresentLegacy.insert(presentToPresentMs); } mGlobalRecord.prevPresentTime = curPresentTime; @@ -908,7 +986,7 @@ void TimeStats::flushAvailableGlobalRecordsToStatsLocked() { } const int32_t renderEngineMs = msBetween(duration.startTime, endNs); - mTimeStats.renderEngineTiming.insert(renderEngineMs); + mTimeStats.renderEngineTimingLegacy.insert(renderEngineMs); mGlobalRecord.renderEngineDurations.pop_front(); } @@ -951,7 +1029,7 @@ void TimeStats::enable() { std::lock_guard<std::mutex> lock(mMutex); mEnabled.store(true); - mTimeStats.statsStart = static_cast<int64_t>(std::time(0)); + mTimeStats.statsStartLegacy = static_cast<int64_t>(std::time(0)); mPowerTime.prevTime = systemTime(); ALOGD("Enabled"); } @@ -964,7 +1042,7 @@ void TimeStats::disable() { std::lock_guard<std::mutex> lock(mMutex); flushPowerTimeLocked(); mEnabled.store(false); - mTimeStats.statsEnd = static_cast<int64_t>(std::time(0)); + mTimeStats.statsEndLegacy = static_cast<int64_t>(std::time(0)); ALOGD("Disabled"); } @@ -977,21 +1055,20 @@ void TimeStats::clearAll() { void TimeStats::clearGlobalLocked() { ATRACE_CALL(); - mTimeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0); - mTimeStats.statsEnd = 0; - mTimeStats.totalFrames = 0; - mTimeStats.missedFrames = 0; - mTimeStats.clientCompositionFrames = 0; - mTimeStats.clientCompositionReusedFrames = 0; - mTimeStats.refreshRateSwitches = 0; - mTimeStats.compositionStrategyChanges = 0; - mTimeStats.displayEventConnectionsCount = 0; - mTimeStats.displayOnTime = 0; - mTimeStats.presentToPresent.hist.clear(); - mTimeStats.frameDuration.hist.clear(); - mTimeStats.renderEngineTiming.hist.clear(); - mTimeStats.jankPayload = TimeStatsHelper::JankPayload(); - mTimeStats.refreshRateStats.clear(); + mTimeStats.statsStartLegacy = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0); + mTimeStats.statsEndLegacy = 0; + mTimeStats.totalFramesLegacy = 0; + mTimeStats.missedFramesLegacy = 0; + mTimeStats.clientCompositionFramesLegacy = 0; + mTimeStats.clientCompositionReusedFramesLegacy = 0; + mTimeStats.refreshRateSwitchesLegacy = 0; + mTimeStats.compositionStrategyChangesLegacy = 0; + mTimeStats.displayEventConnectionsCountLegacy = 0; + mTimeStats.displayOnTimeLegacy = 0; + mTimeStats.presentToPresentLegacy.hist.clear(); + mTimeStats.frameDurationLegacy.hist.clear(); + mTimeStats.renderEngineTimingLegacy.hist.clear(); + mTimeStats.refreshRateStatsLegacy.clear(); mPowerTime.prevTime = systemTime(); mGlobalRecord.prevPresentTime = 0; mGlobalRecord.presentFences.clear(); @@ -1014,11 +1091,11 @@ void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::strin ATRACE_CALL(); std::lock_guard<std::mutex> lock(mMutex); - if (mTimeStats.statsStart == 0) { + if (mTimeStats.statsStartLegacy == 0) { return; } - mTimeStats.statsEnd = static_cast<int64_t>(std::time(0)); + mTimeStats.statsEndLegacy = static_cast<int64_t>(std::time(0)); flushPowerTimeLocked(); diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index df40ef6933..fd112b95b0 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -27,12 +27,13 @@ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion -Wextra" +#include <../Fps.h> +#include <gui/JankInfo.h> #include <stats_event.h> #include <stats_pull_atom_callback.h> #include <statslog.h> #include <timestatsproto/TimeStatsHelper.h> #include <timestatsproto/TimeStatsProtoHeader.h> -#include <gui/JankInfo.h> #include <ui/FenceTime.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -108,23 +109,60 @@ public: const std::shared_ptr<FenceTime>& acquireFence) = 0; // SetPresent{Time, Fence} are not expected to be called in the critical // rendering path, as they flush prior fences if those fences have fired. - virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0; + virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, + Fps displayRefreshRate, std::optional<Fps> renderRate) = 0; virtual void setPresentFence(int32_t layerId, uint64_t frameNumber, - const std::shared_ptr<FenceTime>& presentFence) = 0; - - // Increments janky frames, tracked globally. Because FrameTimeline is the infrastructure - // responsible for computing jank in the system, this is expected to be called from - // FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there are no - // jank reasons, then total frames are incremented but jank is not, for accurate accounting of - // janky frames. - virtual void incrementJankyFrames(int32_t reasons) = 0; - // Increments janky frames, blamed to the provided {uid, layerName} key, with JankMetadata as - // supplementary reasons for the jank. Because FrameTimeline is the infrastructure responsible - // for computing jank in the system, this is expected to be called from FrameTimeline, rather - // than directly from SurfaceFlinger or individual layers. - // If there are no jank reasons, then total frames are incremented but jank is not, for accurate + const std::shared_ptr<FenceTime>& presentFence, + Fps displayRefreshRate, std::optional<Fps> renderRate) = 0; + + // Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName} + // key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the + // infrastructure responsible for computing jank in the system, this is expected to be called + // from FrameTimeline, rather than directly from SurfaceFlinger or individual layers. If there + // are no jank reasons, then total frames are incremented but jank is not, for accurate // accounting of janky frames. - virtual void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) = 0; + // displayDeadlineDelta, displayPresentJitter, and appDeadlineDelta are also provided in order + // to provide contextual information about a janky frame. These values may only be uploaded if + // there was an associated valid jank reason, and they must be positive. When these frame counts + // are incremented, these are also aggregated into a global reporting packet to help with data + // validation and assessing of overall device health. + struct JankyFramesInfo { + Fps refreshRate; + std::optional<Fps> renderRate; + uid_t uid = 0; + std::string layerName; + int32_t reasons = 0; + nsecs_t displayDeadlineDelta = 0; + nsecs_t displayPresentJitter = 0; + nsecs_t appDeadlineDelta = 0; + + bool operator==(const JankyFramesInfo& o) const { + return Fps::EqualsInBuckets{}(refreshRate, o.refreshRate) && + ((renderRate == std::nullopt && o.renderRate == std::nullopt) || + (renderRate != std::nullopt && o.renderRate != std::nullopt && + Fps::EqualsInBuckets{}(*renderRate, *o.renderRate))) && + uid == o.uid && layerName == o.layerName && reasons == o.reasons && + displayDeadlineDelta == o.displayDeadlineDelta && + displayPresentJitter == o.displayPresentJitter && + appDeadlineDelta == o.appDeadlineDelta; + } + + friend std::ostream& operator<<(std::ostream& os, const JankyFramesInfo& info) { + os << "JankyFramesInfo {"; + os << "\n .refreshRate = " << info.refreshRate; + os << "\n .renderRate = " + << (info.renderRate ? to_string(*info.renderRate) : "nullopt"); + os << "\n .uid = " << info.uid; + os << "\n .layerName = " << info.layerName; + os << "\n .reasons = " << info.reasons; + os << "\n .displayDeadlineDelta = " << info.displayDeadlineDelta; + os << "\n .displayPresentJitter = " << info.displayPresentJitter; + os << "\n .appDeadlineDelta = " << info.appDeadlineDelta; + return os << "\n}"; + } + }; + + virtual void incrementJankyFrames(const JankyFramesInfo& info) = 0; // Clean up the layer record virtual void onDestroy(int32_t layerId) = 0; // If SF skips or rejects a buffer, remove the corresponding TimeRecord. @@ -268,11 +306,13 @@ public: void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) override; void setAcquireFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& acquireFence) override; - void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override; + void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime, + Fps displayRefreshRate, std::optional<Fps> renderRate) override; void setPresentFence(int32_t layerId, uint64_t frameNumber, - const std::shared_ptr<FenceTime>& presentFence) override; - void incrementJankyFrames(int32_t reasons) override; - void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) override; + const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate, + std::optional<Fps> renderRate) override; + + void incrementJankyFrames(const JankyFramesInfo& info) override; // Clean up the layer record void onDestroy(int32_t layerId) override; // If SF skips or rejects a buffer, remove the corresponding TimeRecord. @@ -293,7 +333,8 @@ private: AStatsManager_PullAtomCallbackReturn populateGlobalAtom(AStatsEventList* data); AStatsManager_PullAtomCallbackReturn populateLayerAtom(AStatsEventList* data); bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord); - void flushAvailableRecordsToStatsLocked(int32_t layerId); + void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate, + std::optional<Fps> renderRate); void flushPowerTimeLocked(); void flushAvailableGlobalRecordsToStatsLocked(); bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName); @@ -314,6 +355,9 @@ private: GlobalRecord mGlobalRecord; static const size_t MAX_NUM_LAYER_RECORDS = 200; + + static const size_t REFRESH_RATE_BUCKET_WIDTH = 30; + static const size_t RENDER_RATE_BUCKET_WIDTH = REFRESH_RATE_BUCKET_WIDTH; static const size_t MAX_NUM_LAYER_STATS = 200; static const size_t MAX_NUM_PULLED_LAYERS = MAX_NUM_LAYER_STATS; std::unique_ptr<StatsEventDelegate> mStatsDelegate = std::make_unique<StatsEventDelegate>(); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp index 9481cac34d..972edaa30b 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/Android.bp +++ b/services/surfaceflinger/TimeStats/timestatsproto/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "libtimestats_proto", export_include_dirs: ["include"], diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index 0fb748f1a6..d116b02d11 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -41,7 +41,7 @@ void TimeStatsHelper::Histogram::insert(int32_t delta) { if (delta < 0) return; // std::lower_bound won't work on out of range values if (delta > histogramConfig[HISTOGRAM_SIZE - 1]) { - hist[histogramConfig[HISTOGRAM_SIZE - 1]] += delta / histogramConfig[HISTOGRAM_SIZE - 1]; + hist[histogramConfig[HISTOGRAM_SIZE - 1]]++; return; } auto iter = std::lower_bound(histogramConfig.begin(), histogramConfig.end(), delta); @@ -83,13 +83,18 @@ std::string TimeStatsHelper::JankPayload::toString() const { StringAppendF(&result, "jankyFrames = %d\n", totalJankyFrames); StringAppendF(&result, "sfLongCpuJankyFrames = %d\n", totalSFLongCpu); StringAppendF(&result, "sfLongGpuJankyFrames = %d\n", totalSFLongGpu); - StringAppendF(&result, "sfUnattributedJankyFrame = %d\n", totalSFUnattributed); - StringAppendF(&result, "appUnattributedJankyFrame = %d\n", totalAppUnattributed); + StringAppendF(&result, "sfUnattributedJankyFrames = %d\n", totalSFUnattributed); + StringAppendF(&result, "appUnattributedJankyFrames = %d\n", totalAppUnattributed); + StringAppendF(&result, "sfSchedulingJankyFrames = %d\n", totalSFScheduling); + StringAppendF(&result, "sfPredictionErrorJankyFrames = %d\n", totalSFPredictionError); + StringAppendF(&result, "appBufferStuffingJankyFrames = %d\n", totalAppBufferStuffing); return result; } std::string TimeStatsHelper::TimeStatsLayer::toString() const { std::string result = "\n"; + StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket); + StringAppendF(&result, "renderRate = %d fps\n", renderRateBucket); StringAppendF(&result, "uid = %d\n", uid); StringAppendF(&result, "layerName = %s\n", layerName.c_str()); StringAppendF(&result, "packageName = %s\n", packageName.c_str()); @@ -115,35 +120,49 @@ std::string TimeStatsHelper::TimeStatsLayer::toString() const { std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> maxLayers) const { std::string result = "SurfaceFlinger TimeStats:\n"; - StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStart); - StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEnd); - StringAppendF(&result, "totalFrames = %d\n", totalFrames); - StringAppendF(&result, "missedFrames = %d\n", missedFrames); - StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames); - StringAppendF(&result, "clientCompositionReusedFrames = %d\n", clientCompositionReusedFrames); - StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitches); - StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChanges); - StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime); - result.append("Global aggregated jank payload:\n"); - result.append(jankPayload.toString()); + result.append("Legacy stats are as follows:\n"); + StringAppendF(&result, "statsStart = %" PRId64 "\n", statsStartLegacy); + StringAppendF(&result, "statsEnd = %" PRId64 "\n", statsEndLegacy); + StringAppendF(&result, "totalFrames = %d\n", totalFramesLegacy); + StringAppendF(&result, "missedFrames = %d\n", missedFramesLegacy); + StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFramesLegacy); + StringAppendF(&result, "clientCompositionReusedFrames = %d\n", + clientCompositionReusedFramesLegacy); + StringAppendF(&result, "refreshRateSwitches = %d\n", refreshRateSwitchesLegacy); + StringAppendF(&result, "compositionStrategyChanges = %d\n", compositionStrategyChangesLegacy); + StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTimeLegacy); StringAppendF(&result, "displayConfigStats is as below:\n"); - for (const auto& [fps, duration] : refreshRateStats) { + for (const auto& [fps, duration] : refreshRateStatsLegacy) { StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration)); } result.back() = '\n'; - StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime()); + StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresentLegacy.totalTime()); StringAppendF(&result, "presentToPresent histogram is as below:\n"); - result.append(presentToPresent.toString()); - const float averageFrameDuration = frameDuration.averageTime(); + result.append(presentToPresentLegacy.toString()); + const float averageFrameDuration = frameDurationLegacy.averageTime(); StringAppendF(&result, "averageFrameDuration = %.3f ms\n", std::isnan(averageFrameDuration) ? 0.0f : averageFrameDuration); StringAppendF(&result, "frameDuration histogram is as below:\n"); - result.append(frameDuration.toString()); - const float averageRenderEngineTiming = renderEngineTiming.averageTime(); + result.append(frameDurationLegacy.toString()); + const float averageRenderEngineTiming = renderEngineTimingLegacy.averageTime(); StringAppendF(&result, "averageRenderEngineTiming = %.3f ms\n", std::isnan(averageRenderEngineTiming) ? 0.0f : averageRenderEngineTiming); StringAppendF(&result, "renderEngineTiming histogram is as below:\n"); - result.append(renderEngineTiming.toString()); + result.append(renderEngineTimingLegacy.toString()); + + result.append("\nGlobal aggregated jank payload (Timeline stats):"); + for (const auto& ele : stats) { + result.append("\n"); + StringAppendF(&result, "displayRefreshRate = %d fps\n", + ele.second.key.displayRefreshRateBucket); + StringAppendF(&result, "renderRate = %d fps\n", ele.second.key.renderRateBucket); + result.append(ele.second.jankPayload.toString()); + StringAppendF(&result, "sfDeadlineMisses histogram is as below:\n"); + result.append(ele.second.displayDeadlineDeltas.toString()); + StringAppendF(&result, "sfPredictionErrors histogram is as below:\n"); + result.append(ele.second.displayPresentDeltas.toString()); + } + const auto dumpStats = generateDumpStats(maxLayers); for (const auto& ele : dumpStats) { result.append(ele->toString()); @@ -173,30 +192,30 @@ SFTimeStatsLayerProto TimeStatsHelper::TimeStatsLayer::toProto() const { SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto( std::optional<uint32_t> maxLayers) const { SFTimeStatsGlobalProto globalProto; - globalProto.set_stats_start(statsStart); - globalProto.set_stats_end(statsEnd); - globalProto.set_total_frames(totalFrames); - globalProto.set_missed_frames(missedFrames); - globalProto.set_client_composition_frames(clientCompositionFrames); - globalProto.set_display_on_time(displayOnTime); - for (const auto& ele : refreshRateStats) { + globalProto.set_stats_start(statsStartLegacy); + globalProto.set_stats_end(statsEndLegacy); + globalProto.set_total_frames(totalFramesLegacy); + globalProto.set_missed_frames(missedFramesLegacy); + globalProto.set_client_composition_frames(clientCompositionFramesLegacy); + globalProto.set_display_on_time(displayOnTimeLegacy); + for (const auto& ele : refreshRateStatsLegacy) { SFTimeStatsDisplayConfigBucketProto* configBucketProto = globalProto.add_display_config_stats(); SFTimeStatsDisplayConfigProto* configProto = configBucketProto->mutable_config(); configProto->set_fps(ele.first); configBucketProto->set_duration_millis(ns2ms(ele.second)); } - for (const auto& histEle : presentToPresent.hist) { + for (const auto& histEle : presentToPresentLegacy.hist) { SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present(); histProto->set_time_millis(histEle.first); histProto->set_frame_count(histEle.second); } - for (const auto& histEle : frameDuration.hist) { + for (const auto& histEle : frameDurationLegacy.hist) { SFTimeStatsHistogramBucketProto* histProto = globalProto.add_frame_duration(); histProto->set_time_millis(histEle.first); histProto->set_frame_count(histEle.second); } - for (const auto& histEle : renderEngineTiming.hist) { + for (const auto& histEle : renderEngineTimingLegacy.hist) { SFTimeStatsHistogramBucketProto* histProto = globalProto.add_render_engine_timing(); histProto->set_time_millis(histEle.first); histProto->set_frame_count(histEle.second); @@ -212,8 +231,18 @@ SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto( std::vector<TimeStatsHelper::TimeStatsLayer const*> TimeStatsHelper::TimeStatsGlobal::generateDumpStats(std::optional<uint32_t> maxLayers) const { std::vector<TimeStatsLayer const*> dumpStats; + + int numLayers = 0; + for (const auto& ele : stats) { + numLayers += ele.second.stats.size(); + } + + dumpStats.reserve(numLayers); + for (const auto& ele : stats) { - dumpStats.push_back(&ele.second); + for (const auto& layerEle : ele.second.stats) { + dumpStats.push_back(&layerEle.second); + } } std::sort(dumpStats.begin(), dumpStats.end(), diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 033eb5dcd9..4556badf7b 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -48,6 +48,9 @@ public: int32_t totalSFLongGpu = 0; int32_t totalSFUnattributed = 0; int32_t totalAppUnattributed = 0; + int32_t totalSFScheduling = 0; + int32_t totalSFPredictionError = 0; + int32_t totalAppBufferStuffing = 0; std::string toString() const; }; @@ -57,6 +60,8 @@ public: uid_t uid; std::string layerName; std::string packageName; + int32_t displayRefreshRateBucket = 0; + int32_t renderRateBucket = 0; int32_t totalFrames = 0; int32_t droppedFrames = 0; int32_t lateAcquireFrames = 0; @@ -68,32 +73,84 @@ public: SFTimeStatsLayerProto toProto() const; }; - class TimeStatsGlobal { - public: - int64_t statsStart = 0; - int64_t statsEnd = 0; - int32_t totalFrames = 0; - int32_t missedFrames = 0; - int32_t clientCompositionFrames = 0; - int32_t clientCompositionReusedFrames = 0; - int32_t refreshRateSwitches = 0; - int32_t compositionStrategyChanges = 0; - int32_t displayEventConnectionsCount = 0; - int64_t displayOnTime = 0; - Histogram presentToPresent; - Histogram frameDuration; - Histogram renderEngineTiming; - - struct StatsHasher { - size_t operator()(const std::pair<uid_t, std::string>& p) const { - // Normally this isn't a very good hash function due to symmetry reasons, - // but these are distinct types so this should be good enough - return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second); + // Lifted from SkiaGLRenderEngine's LinearEffect class. + // Which in turn was inspired by art/runtime/class_linker.cc + // Also this is what boost:hash_combine does so this is a pretty good hash. + static size_t HashCombine(size_t seed, size_t val) { + return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + } + + struct TimelineStatsKey { + int32_t displayRefreshRateBucket = 0; + int32_t renderRateBucket = 0; + + struct Hasher { + size_t operator()(const TimelineStatsKey& key) const { + size_t result = std::hash<int32_t>{}(key.displayRefreshRateBucket); + return HashCombine(result, std::hash<int32_t>{}(key.renderRateBucket)); + } + }; + + bool operator==(const TimelineStatsKey& o) const { + return displayRefreshRateBucket == o.displayRefreshRateBucket && + renderRateBucket == o.renderRateBucket; + } + }; + + struct LayerStatsKey { + uid_t uid = 0; + std::string layerName; + + struct Hasher { + size_t operator()(const LayerStatsKey& key) const { + size_t result = std::hash<uid_t>{}(key.uid); + return HashCombine(result, std::hash<std::string>{}(key.layerName)); } }; - std::unordered_map<std::pair<uid_t, std::string>, TimeStatsLayer, StatsHasher> stats; - std::unordered_map<uint32_t, nsecs_t> refreshRateStats; + + bool operator==(const LayerStatsKey& o) const { + return uid == o.uid && layerName == o.layerName; + } + }; + + struct LayerStatsHasher { + size_t operator()(const std::pair<uid_t, std::string>& p) const { + // Normally this isn't a very good hash function due to symmetry reasons, + // but these are distinct types so this should be good enough + return std::hash<uid_t>{}(p.first) ^ std::hash<std::string>{}(p.second); + } + }; + + struct TimelineStats { + TimelineStatsKey key; JankPayload jankPayload; + Histogram displayDeadlineDeltas; + Histogram displayPresentDeltas; + std::unordered_map<LayerStatsKey, TimeStatsLayer, LayerStatsKey::Hasher> stats; + }; + + class TimeStatsGlobal { + public: + // Note: these are all legacy statistics, we're keeping these around because a variety of + // systems and form-factors find these useful when comparing with older releases. However, + // the current recommendation is that the new timeline-based metrics are used, and the old + // ones are deprecated. + int64_t statsStartLegacy = 0; + int64_t statsEndLegacy = 0; + int32_t totalFramesLegacy = 0; + int32_t missedFramesLegacy = 0; + int32_t clientCompositionFramesLegacy = 0; + int32_t clientCompositionReusedFramesLegacy = 0; + int32_t refreshRateSwitchesLegacy = 0; + int32_t compositionStrategyChangesLegacy = 0; + int32_t displayEventConnectionsCountLegacy = 0; + int64_t displayOnTimeLegacy = 0; + Histogram presentToPresentLegacy; + Histogram frameDurationLegacy; + Histogram renderEngineTimingLegacy; + std::unordered_map<uint32_t, nsecs_t> refreshRateStatsLegacy; + + std::unordered_map<TimelineStatsKey, TimelineStats, TimelineStatsKey::Hasher> stats; std::string toString(std::optional<uint32_t> maxLayers) const; SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const; diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp index e2a28a2ae5..c8a2b5eed6 100644 --- a/services/surfaceflinger/layerproto/Android.bp +++ b/services/surfaceflinger/layerproto/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library { name: "liblayers_proto", export_include_dirs: ["include"], diff --git a/services/surfaceflinger/sysprop/Android.bp b/services/surfaceflinger/sysprop/Android.bp index 7721d7d2b7..f5791195db 100644 --- a/services/surfaceflinger/sysprop/Android.bp +++ b/services/surfaceflinger/sysprop/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + sysprop_library { name: "SurfaceFlingerProperties", srcs: ["*.sysprop"], diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 8142aad382..78187f7e9d 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "SurfaceFlinger_test", defaults: ["surfaceflinger_defaults"], diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index 53e37d874e..6246321fab 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -26,6 +26,7 @@ #include <private/android_filesystem_config.h> #include <private/gui/ComposerService.h> #include <ui/DisplayMode.h> +#include <ui/DynamicDisplayInfo.h> #include <utils/String8.h> #include <functional> #include "utils/ScreenshotUtils.h" @@ -188,19 +189,15 @@ TEST_F(CredentialsTest, AllowedGetterMethodsTest) { ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode)); Vector<ui::DisplayMode> modes; - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes)); - - ASSERT_TRUE(SurfaceComposerClient::getActiveDisplayModeId(display) >= 0); - - ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE), - SurfaceComposerClient::getActiveColorMode(display)); + ui::DynamicDisplayInfo info; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); } -TEST_F(CredentialsTest, GetDisplayColorModesTest) { +TEST_F(CredentialsTest, GetDynamicDisplayInfoTest) { const auto display = SurfaceComposerClient::getInternalDisplayToken(); std::function<status_t()> condition = [=]() { - Vector<ui::ColorMode> outColorModes; - return SurfaceComposerClient::getDisplayColorModes(display, &outColorModes); + ui::DynamicDisplayInfo info; + return SurfaceComposerClient::getDynamicDisplayInfo(display, &info); }; ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR)); } @@ -216,7 +213,7 @@ TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) { TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) { const auto display = SurfaceComposerClient::getInternalDisplayToken(); - size_t defaultMode; + ui::DisplayModeId defaultMode; bool allowGroupSwitching; float primaryFpsMin; float primaryFpsMax; @@ -355,8 +352,9 @@ TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) { status_t error = SurfaceComposerClient::isWideColorDisplay(display, &result); ASSERT_EQ(NO_ERROR, error); bool hasWideColorMode = false; - Vector<ColorMode> colorModes; - SurfaceComposerClient::getDisplayColorModes(display, &colorModes); + ui::DynamicDisplayInfo info; + SurfaceComposerClient::getDynamicDisplayInfo(display, &info); + const auto& colorModes = info.supportedColorModes; for (ColorMode colorMode : colorModes) { switch (colorMode) { case ColorMode::DISPLAY_P3: @@ -384,7 +382,9 @@ TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) { TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) { const auto display = SurfaceComposerClient::getInternalDisplayToken(); ASSERT_FALSE(display == nullptr); - ColorMode colorMode = SurfaceComposerClient::getActiveColorMode(display); + ui::DynamicDisplayInfo info; + SurfaceComposerClient::getDynamicDisplayInfo(display, &info); + ColorMode colorMode = info.activeColorMode; ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode); } diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp index 9f025a6aaa..2dc96b8511 100644 --- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp +++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp @@ -23,6 +23,7 @@ #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> #include <ui/DisplayMode.h> +#include <ui/DynamicDisplayInfo.h> #include <utils/Errors.h> #include <utils/Vector.h> @@ -38,7 +39,7 @@ namespace android { */ class RefreshRateRangeTest : public ::testing::Test { private: - size_t initialDefaultMode; + ui::DisplayModeId initialDefaultMode; bool initialAllowGroupSwitching; float initialPrimaryMin; float initialPrimaryMax; @@ -76,20 +77,21 @@ protected: }; TEST_F(RefreshRateRangeTest, setAllConfigs) { - Vector<ui::DisplayMode> modes; - status_t res = SurfaceComposerClient::getDisplayModes(mDisplayToken, &modes); + ui::DynamicDisplayInfo info; + status_t res = SurfaceComposerClient::getDynamicDisplayInfo(mDisplayToken, &info); + const auto& modes = info.supportedDisplayModes; ASSERT_EQ(res, NO_ERROR); ASSERT_GT(modes.size(), 0); for (size_t i = 0; i < modes.size(); i++) { - res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, i, false, + res = SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, modes[i].id, false, modes[i].refreshRate, modes[i].refreshRate, modes[i].refreshRate, modes[i].refreshRate); ASSERT_EQ(res, NO_ERROR); - size_t defaultConfig; + ui::DisplayModeId defaultConfig; bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; @@ -116,7 +118,7 @@ void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) SurfaceComposerClient::setDesiredDisplayModeSpecs(mDisplayToken, 0, allowGroupSwitching, 0.f, 90.f, 0.f, 90.f); ASSERT_EQ(res, NO_ERROR); - size_t defaultConfig; + ui::DisplayModeId defaultConfig; bool newAllowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp index 3535fbb1b4..2551a19b71 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "sffakehwc_test", defaults: ["surfaceflinger_defaults"], diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp index 56e1ae90c3..11bd9ebbb8 100644 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -44,6 +44,7 @@ #include <log/log.h> #include <private/gui/ComposerService.h> #include <ui/DisplayMode.h> +#include <ui/DynamicDisplayInfo.h> #include <utils/Looper.h> #include <gmock/gmock.h> @@ -432,8 +433,9 @@ protected: } } - Vector<ui::DisplayMode> modes; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes)); + ui::DynamicDisplayInfo info; + EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); + const auto& modes = info.supportedDisplayModes; EXPECT_EQ(modes.size(), 2); // change active mode @@ -539,8 +541,9 @@ protected: } } - Vector<ui::DisplayMode> modes; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes)); + ui::DynamicDisplayInfo info; + EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); + const auto& modes = info.supportedDisplayModes; EXPECT_EQ(modes.size(), 2); // change active mode @@ -655,8 +658,9 @@ protected: } } - Vector<ui::DisplayMode> modes; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes)); + ui::DynamicDisplayInfo info; + EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); + const auto& modes = info.supportedDisplayModes; EXPECT_EQ(modes.size(), 4); // change active mode to 800x1600@90Hz @@ -884,8 +888,9 @@ protected: EXPECT_EQ(ui::Size(800, 1600), mode.resolution); EXPECT_EQ(1e9f / 11'111'111, mode.refreshRate); - Vector<ui::DisplayMode> modes; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes)); + ui::DynamicDisplayInfo info; + EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); + const auto& modes = info.supportedDisplayModes; EXPECT_EQ(modes.size(), 1); } @@ -923,8 +928,9 @@ protected: EXPECT_EQ(1e9f / 16'666'666, mode.refreshRate); } - Vector<ui::DisplayMode> modes; - EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayModes(display, &modes)); + ui::DynamicDisplayInfo info; + EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDynamicDisplayInfo(display, &info)); + const auto& modes = info.supportedDisplayModes; EXPECT_EQ(modes.size(), 3); EXPECT_EQ(ui::Size(800, 1600), modes[0].resolution); diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 17928a0d61..2ac6b092b5 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "libsurfaceflinger_unittest", defaults: ["surfaceflinger_defaults"], @@ -73,6 +82,7 @@ cc_test { "FrameTracerTest.cpp", "TimerTest.cpp", "TransactionApplicationTest.cpp", + "TransactionFrameTracerTest.cpp", "TransactionSurfaceFrameTest.cpp", "StrongTypingTest.cpp", "VSyncDispatchTimerQueueTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index b696a6d51b..256be27b2d 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -286,7 +286,7 @@ struct BaseDisplayVariant { auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() - .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal}) + .setPhysical({DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal}) .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) .setIsSecure(Derived::IS_SECURE) .setLayerStackId(DEFAULT_LAYER_STACK) @@ -300,7 +300,7 @@ struct BaseDisplayVariant { ceDisplayArgs); test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay, - DisplayConnectionType::Internal, HWC_DISPLAY, + ui::DisplayConnectionType::Internal, HWC_DISPLAY, true /* isPrimary */) .setDisplaySurface(test->mDisplaySurface) .setNativeWindow(test->mNativeWindow) diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 90692003ef..a3e810871c 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -139,14 +139,14 @@ sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay( createDisplay(mFlinger.getCompositionEngine(), compositionengine::DisplayCreationArgsBuilder() .setPhysical( - {DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal}) + {DEFAULT_DISPLAY_ID, ui::DisplayConnectionType::Internal}) .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) .setPowerAdvisor(&mPowerAdvisor) .build()); - auto injector = - FakeDisplayDeviceInjector(mFlinger, compositionDisplay, DisplayConnectionType::Internal, - DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */); + auto injector = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, + ui::DisplayConnectionType::Internal, + DEFAULT_DISPLAY_HWC_DISPLAY_ID, true /* isPrimary */); injector.setNativeWindow(mNativeWindow); if (injectExtra) { diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 166430193d..d68fff6345 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -202,12 +202,13 @@ struct DisplayIdGetter<GpuVirtualDisplayIdType> { template <typename> struct DisplayConnectionTypeGetter { - static constexpr std::optional<DisplayConnectionType> value; + static constexpr std::optional<ui::DisplayConnectionType> value; }; template <typename PhysicalDisplay> struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> { - static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE; + static constexpr std::optional<ui::DisplayConnectionType> value = + PhysicalDisplay::CONNECTION_TYPE; }; template <typename> @@ -263,7 +264,7 @@ struct DisplayVariant { static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) { auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder(); if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) { - ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal}); + ceDisplayArgs.setPhysical({*displayId, ui::DisplayConnectionType::Internal}); } else { // We turn off the use of HwcVirtualDisplays, to prevent Composition Engine // from calling into HWComposer. This way all virtual displays will get @@ -457,7 +458,7 @@ struct HwcDisplayVariant { static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) { constexpr auto CONNECTION_TYPE = - PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal + PhysicalDisplay::CONNECTION_TYPE == ui::DisplayConnectionType::Internal ? IComposerClient::DisplayConnectionType::INTERNAL : IComposerClient::DisplayConnectionType::EXTERNAL; @@ -504,7 +505,7 @@ struct PhysicalDisplayVariant template <bool hasIdentificationData> struct PrimaryDisplay { - static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal; + static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::Internal; static constexpr Primary PRIMARY = Primary::TRUE; static constexpr uint8_t PORT = 255; static constexpr HWDisplayId HWC_DISPLAY_ID = 1001; @@ -514,7 +515,7 @@ struct PrimaryDisplay { template <bool hasIdentificationData> struct ExternalDisplay { - static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External; + static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External; static constexpr Primary PRIMARY = Primary::FALSE; static constexpr uint8_t PORT = 254; static constexpr HWDisplayId HWC_DISPLAY_ID = 1002; diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 6e9f09bdaa..b8c1607562 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -27,6 +27,7 @@ #include <cinttypes> using namespace std::chrono_literals; +using testing::_; using testing::AtLeast; using testing::Contains; using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent; @@ -40,10 +41,6 @@ using ProtoFrameEnd = perfetto::protos::FrameTimelineEvent_FrameEnd; using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType; using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType; -MATCHER_P(HasBit, bit, "") { - return (arg & bit) != 0; -} - namespace android::frametimeline { class FrameTimelineTest : public testing::Test { @@ -109,6 +106,7 @@ public: void addEmptyDisplayFrame() { auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + // Trigger a flushPresentFence by calling setSfPresent for the next frame mFrameTimeline->setSfPresent(2500, presentFence1); } @@ -158,12 +156,9 @@ public: uint32_t* maxDisplayFrames; nsecs_t maxTokenRetentionTime; static constexpr pid_t kSurfaceFlingerPid = 666; - static constexpr nsecs_t kPresentThreshold = - std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count(); - static constexpr nsecs_t kDeadlineThreshold = - std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count(); - static constexpr nsecs_t kStartThreshold = - std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count(); + static constexpr nsecs_t kPresentThreshold = std::chrono::nanoseconds(2ns).count(); + static constexpr nsecs_t kDeadlineThreshold = std::chrono::nanoseconds(2ns).count(); + static constexpr nsecs_t kStartThreshold = std::chrono::nanoseconds(2ns).count(); static constexpr JankClassificationThresholds kTestThresholds{kPresentThreshold, kDeadlineThreshold, kStartThreshold}; @@ -237,50 +232,42 @@ TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validInputEventId) { } TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); - int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); // Set up the display frame - mFrameTimeline->setSfWakeUp(token1, 20, 11); + mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11)); + surfaceFrame1->setDropTime(12); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(25, presentFence1); presentFence1->signalForTest(30); - // Trigger a flush by calling setSfPresent for the next frame - mFrameTimeline->setSfWakeUp(token2, 50, 11); - mFrameTimeline->setSfPresent(55, presentFence2); + addEmptyDisplayFrame(); auto& droppedSurfaceFrame = getSurfaceFrame(0, 0); EXPECT_EQ(droppedSurfaceFrame.getPresentState(), SurfaceFrame::PresentState::Dropped); + EXPECT_EQ(0u, droppedSurfaceFrame.getActuals().endTime); + EXPECT_EQ(12u, droppedSurfaceFrame.getDropTime()); EXPECT_EQ(droppedSurfaceFrame.getActuals().presentTime, 0); } TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); - int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30}); - int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 60}); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameTwo, sLayerNameTwo); - mFrameTimeline->setSfWakeUp(sfToken1, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented); @@ -296,16 +283,7 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 0); EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0); - // Trigger a flush by finalizing the next DisplayFrame - auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - auto surfaceFrame3 = - mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, - sUidOne, sLayerNameOne, sLayerNameOne); - mFrameTimeline->setSfWakeUp(sfToken2, 52, 11); - surfaceFrame3->setPresentState(SurfaceFrame::PresentState::Dropped); - mFrameTimeline->addSurfaceFrame(surfaceFrame3); - mFrameTimeline->setSfPresent(56, presentFence2); - displayFrame = getDisplayFrame(0); + addEmptyDisplayFrame(); // Fences have flushed, so the present timestamps should be updated EXPECT_EQ(displayFrame->getActuals().presentTime, 42); @@ -318,11 +296,8 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque int frameTimeFactor = 0; - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)) - .Times(static_cast<int32_t>(*maxDisplayFrames)); // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)) + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)) .Times(static_cast<int32_t>(*maxDisplayFrames)); for (size_t i = 0; i < *maxDisplayFrames; i++) { auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); @@ -334,7 +309,7 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); - mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11); + mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11)); surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame); mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence); @@ -355,7 +330,7 @@ TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); - mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, 11); + mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor, Fps::fromPeriodNsecs(11)); surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame); mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence); @@ -386,9 +361,6 @@ TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) { } TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)) - .Times(static_cast<int32_t>(*maxDisplayFrames + 10)); auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); presentFence->signalForTest(2); @@ -398,7 +370,7 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); - mFrameTimeline->setSfWakeUp(sfToken, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11)); surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame); mFrameTimeline->setSfPresent(27, presentFence); @@ -408,16 +380,13 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { // Increase the size to 256 mFrameTimeline->setMaxDisplayFrames(256); EXPECT_EQ(*maxDisplayFrames, 256u); - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)) - .Times(static_cast<int32_t>(*maxDisplayFrames + 10)); for (size_t i = 0; i < *maxDisplayFrames + 10; i++) { auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); - mFrameTimeline->setSfWakeUp(sfToken, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11)); surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame); mFrameTimeline->setSfPresent(27, presentFence); @@ -427,16 +396,13 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { // Shrink the size to 128 mFrameTimeline->setMaxDisplayFrames(128); EXPECT_EQ(*maxDisplayFrames, 128u); - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)) - .Times(static_cast<int32_t>(*maxDisplayFrames + 10)); for (size_t i = 0; i < *maxDisplayFrames + 10; i++) { auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); - mFrameTimeline->setSfWakeUp(sfToken, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken, 22, Fps::fromPeriodNsecs(11)); surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame); mFrameTimeline->setSfPresent(27, presentFence); @@ -446,93 +412,217 @@ TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { // Tests related to TimeStats TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) { + Fps refreshRate = Fps(11); EXPECT_CALL(*mTimeStats, - incrementJankyFrames(sUidOne, sLayerNameOne, - HasBit(JankType::SurfaceFlingerCpuDeadlineMissed))); - EXPECT_CALL(*mTimeStats, - incrementJankyFrames(HasBit(JankType::SurfaceFlingerCpuDeadlineMissed))); + incrementJankyFrames( + TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne, + sLayerNameOne, + JankType::SurfaceFlingerCpuDeadlineMissed, + std::chrono::duration_cast< + std::chrono::nanoseconds>(3ms) + .count(), + std::chrono::duration_cast< + std::chrono::nanoseconds>(10ms) + .count(), + 0})); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( - {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(), + std::chrono::nanoseconds(60ms).count()}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions( - {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(), + std::chrono::nanoseconds(60ms).count()}); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); - mFrameTimeline->setSfWakeUp(sfToken1, - std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(), - 11); + mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate); + surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count()); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); - presentFence1->signalForTest( - std::chrono::duration_cast<std::chrono::nanoseconds>(70ms).count()); + presentFence1->signalForTest(std::chrono::nanoseconds(70ms).count()); - mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(), - presentFence1); + mFrameTimeline->setSfPresent(std::chrono::nanoseconds(59ms).count(), presentFence1); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { + Fps refreshRate = Fps::fromPeriodNsecs(30); EXPECT_CALL(*mTimeStats, - incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(JankType::DisplayHAL))); - EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::DisplayHAL))); + incrementJankyFrames(TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne, + sLayerNameOne, JankType::DisplayHAL, + 0, 0, 0})); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( - {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(), + std::chrono::nanoseconds(60ms).count()}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions( - {std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(), + std::chrono::nanoseconds(60ms).count()}); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); - mFrameTimeline->setSfWakeUp(sfToken1, - std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(), - 30); + mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); + surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(20ms).count()); mFrameTimeline->addSurfaceFrame(surfaceFrame1); - presentFence1->signalForTest( - std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()); - mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(), - presentFence1); + presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count()); + mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::DisplayHAL); } TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { + Fps refreshRate = Fps(11.0); EXPECT_CALL(*mTimeStats, - incrementJankyFrames(sUidOne, sLayerNameOne, - HasBit(JankType::AppDeadlineMissed))); - EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(JankType::AppDeadlineMissed))); + incrementJankyFrames( + TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne, + sLayerNameOne, JankType::AppDeadlineMissed, 0, 0, + std::chrono::duration_cast< + std::chrono::nanoseconds>(25ms) + .count()})); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( - {std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(60ms).count()}); + {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(), + std::chrono::nanoseconds(60ms).count()}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions( - {std::chrono::duration_cast<std::chrono::nanoseconds>(82ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()}); + {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(), + std::chrono::nanoseconds(90ms).count()}); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); - surfaceFrame1->setAcquireFenceTime( - std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count()); - mFrameTimeline->setSfWakeUp(sfToken1, - std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count(), - 11); + surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count()); + mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); - presentFence1->signalForTest( - std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()); - mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(86ms).count(), - presentFence1); + presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count()); + mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1); + + EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfScheduling) { + Fps refreshRate = Fps::fromPeriodNsecs(std::chrono::nanoseconds(32ms).count()); + EXPECT_CALL(*mTimeStats, + incrementJankyFrames( + TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne, + sLayerNameOne, + JankType::SurfaceFlingerScheduling, 0, 0, + std::chrono::duration_cast< + std::chrono::nanoseconds>(-10ms) + .count()})); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::nanoseconds(40ms).count(), std::chrono::nanoseconds(60ms).count(), + std::chrono::nanoseconds(92ms).count()}); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(), + std::chrono::nanoseconds(60ms).count()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, + sUidOne, sLayerNameOne, sLayerNameOne); + surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(50ms).count()); + mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate); + + surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); + mFrameTimeline->addSurfaceFrame(surfaceFrame1); + presentFence1->signalForTest(std::chrono::nanoseconds(60ms).count()); + mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1); + + EXPECT_EQ(surfaceFrame1->getJankType(), JankType::SurfaceFlingerScheduling); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsSfPredictionError) { + Fps refreshRate = Fps(16.66f); + EXPECT_CALL(*mTimeStats, + incrementJankyFrames( + TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne, + sLayerNameOne, JankType::PredictionError, 0, + std::chrono::duration_cast< + std::chrono::nanoseconds>(5ms) + .count(), + 0})); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::nanoseconds(30ms).count(), std::chrono::nanoseconds(40ms).count(), + std::chrono::nanoseconds(60ms).count()}); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::nanoseconds(52ms).count(), std::chrono::nanoseconds(56ms).count(), + std::chrono::nanoseconds(60ms).count()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, + sUidOne, sLayerNameOne, sLayerNameOne); + surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count()); + mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate); + + surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); + mFrameTimeline->addSurfaceFrame(surfaceFrame1); + presentFence1->signalForTest(std::chrono::nanoseconds(65ms).count()); + mFrameTimeline->setSfPresent(std::chrono::nanoseconds(56ms).count(), presentFence1); + + EXPECT_EQ(surfaceFrame1->getJankType(), JankType::PredictionError); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppBufferStuffing) { + Fps refreshRate = Fps::fromPeriodNsecs(std::chrono::nanoseconds(32ms).count()); + EXPECT_CALL(*mTimeStats, + incrementJankyFrames( + TimeStats::JankyFramesInfo{refreshRate, std::nullopt, sUidOne, + sLayerNameOne, + JankType::BufferStuffing | + JankType::SurfaceFlingerScheduling, + 0, 0, 0})); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::nanoseconds(30ms).count(), std::chrono::nanoseconds(40ms).count(), + std::chrono::nanoseconds(58ms).count()}); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(), + std::chrono::nanoseconds(90ms).count()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, + sUidOne, sLayerNameOne, sLayerNameOne); + surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(40ms).count()); + mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(82ms).count(), refreshRate); + + surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented, + /*previousLatchTime*/ + std::chrono::nanoseconds(56ms).count()); + mFrameTimeline->addSurfaceFrame(surfaceFrame1); + presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count()); + mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1); + + EXPECT_EQ(surfaceFrame1->getJankType(), + JankType::BufferStuffing | JankType::SurfaceFlingerScheduling); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMissWithRenderRate) { + Fps refreshRate = Fps(11.0); + Fps renderRate = Fps(30.0); + EXPECT_CALL(*mTimeStats, + incrementJankyFrames( + TimeStats::JankyFramesInfo{refreshRate, renderRate, sUidOne, sLayerNameOne, + JankType::AppDeadlineMissed, 0, 0, + std::chrono::duration_cast< + std::chrono::nanoseconds>(25ms) + .count()})); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::nanoseconds(10ms).count(), std::chrono::nanoseconds(20ms).count(), + std::chrono::nanoseconds(60ms).count()}); + int64_t sfToken1 = mTokenManager->generateTokenForPredictions( + {std::chrono::nanoseconds(82ms).count(), std::chrono::nanoseconds(86ms).count(), + std::chrono::nanoseconds(90ms).count()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, + sUidOne, sLayerNameOne, sLayerNameOne); + surfaceFrame1->setAcquireFenceTime(std::chrono::nanoseconds(45ms).count()); + mFrameTimeline->setSfWakeUp(sfToken1, std::chrono::nanoseconds(52ms).count(), refreshRate); + + surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); + surfaceFrame1->setRenderRate(renderRate); + mFrameTimeline->addSurfaceFrame(surfaceFrame1); + presentFence1->signalForTest(std::chrono::nanoseconds(90ms).count()); + mFrameTimeline->setSfPresent(std::chrono::nanoseconds(86ms).count(), presentFence1); EXPECT_EQ(surfaceFrame1->getJankType(), JankType::AppDeadlineMissed); } @@ -547,28 +637,20 @@ TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { */ TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) { auto tracingSession = getTracingSessionForTest(); - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); - int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({token1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); // Set up the display frame - mFrameTimeline->setSfWakeUp(token1, 20, 11); + mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(25, presentFence1); presentFence1->signalForTest(30); - // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the - // next frame - mFrameTimeline->setSfWakeUp(token2, 50, 11); - mFrameTimeline->setSfPresent(55, presentFence2); + addEmptyDisplayFrame(); auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); EXPECT_EQ(packets.size(), 0u); @@ -576,12 +658,9 @@ TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) { TEST_F(FrameTimelineTest, tracing_sanityTest) { auto tracingSession = getTracingSessionForTest(); - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); tracingSession->StartBlocking(); int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); @@ -591,18 +670,13 @@ TEST_F(FrameTimelineTest, tracing_sanityTest) { sLayerNameOne, sLayerNameOne); // Set up the display frame - mFrameTimeline->setSfWakeUp(token2, 20, 11); + mFrameTimeline->setSfWakeUp(token2, 20, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(25, presentFence1); presentFence1->signalForTest(30); - // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the - // next frame - mFrameTimeline->setSfWakeUp(token2, 50, 11); - mFrameTimeline->setSfPresent(55, presentFence2); - presentFence2->signalForTest(55); - + addEmptyDisplayFrame(); flushTrace(); tracingSession->StopBlocking(); @@ -613,25 +687,16 @@ TEST_F(FrameTimelineTest, tracing_sanityTest) { TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) { auto tracingSession = getTracingSessionForTest(); - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); tracingSession->StartBlocking(); - int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); // Set up the display frame - mFrameTimeline->setSfWakeUp(-1, 20, 11); + mFrameTimeline->setSfWakeUp(-1, 20, Fps::fromPeriodNsecs(11)); mFrameTimeline->setSfPresent(25, presentFence1); presentFence1->signalForTest(30); - // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the - // next frame - mFrameTimeline->setSfWakeUp(token1, 50, 11); - mFrameTimeline->setSfPresent(55, presentFence2); - presentFence2->signalForTest(60); - + addEmptyDisplayFrame(); flushTrace(); tracingSession->StopBlocking(); @@ -641,30 +706,21 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) { auto tracingSession = getTracingSessionForTest(); - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); tracingSession->StartBlocking(); int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); - int64_t token2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); // Set up the display frame - mFrameTimeline->setSfWakeUp(token1, 20, 11); + mFrameTimeline->setSfWakeUp(token1, 20, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(25, presentFence1); presentFence1->signalForTest(30); - // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the - // next frame - mFrameTimeline->setSfWakeUp(token2, 50, 11); - mFrameTimeline->setSfPresent(55, presentFence2); - presentFence2->signalForTest(60); - + addEmptyDisplayFrame(); flushTrace(); tracingSession->StopBlocking(); @@ -674,6 +730,65 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) EXPECT_EQ(packets.size(), 4u); } +ProtoExpectedDisplayFrameStart createProtoExpectedDisplayFrameStart(int64_t cookie, int64_t token, + pid_t pid) { + ProtoExpectedDisplayFrameStart proto; + proto.set_cookie(cookie); + proto.set_token(token); + proto.set_pid(pid); + return proto; +} + +ProtoActualDisplayFrameStart createProtoActualDisplayFrameStart( + int64_t cookie, int64_t token, pid_t pid, ProtoPresentType presentType, bool onTimeFinish, + bool gpuComposition, ProtoJankType jankType) { + ProtoActualDisplayFrameStart proto; + proto.set_cookie(cookie); + proto.set_token(token); + proto.set_pid(pid); + proto.set_present_type(presentType); + proto.set_on_time_finish(onTimeFinish); + proto.set_gpu_composition(gpuComposition); + proto.set_jank_type(jankType); + return proto; +} + +ProtoExpectedSurfaceFrameStart createProtoExpectedSurfaceFrameStart(int64_t cookie, int64_t token, + int64_t displayFrameToken, + pid_t pid, + std::string layerName) { + ProtoExpectedSurfaceFrameStart proto; + proto.set_cookie(cookie); + proto.set_token(token); + proto.set_display_frame_token(displayFrameToken); + proto.set_pid(pid); + proto.set_layer_name(layerName); + return proto; +} + +ProtoActualSurfaceFrameStart createProtoActualSurfaceFrameStart( + int64_t cookie, int64_t token, int64_t displayFrameToken, pid_t pid, std::string layerName, + ProtoPresentType presentType, bool onTimeFinish, bool gpuComposition, + ProtoJankType jankType) { + ProtoActualSurfaceFrameStart proto; + proto.set_cookie(cookie); + proto.set_token(token); + proto.set_display_frame_token(displayFrameToken); + proto.set_pid(pid); + proto.set_layer_name(layerName); + proto.set_present_type(presentType); + proto.set_on_time_finish(onTimeFinish); + proto.set_gpu_composition(gpuComposition); + proto.set_jank_type(jankType); + return proto; +} + +ProtoFrameEnd createProtoFrameEnd(int64_t cookie) { + ProtoFrameEnd proto; + proto.set_cookie(cookie); + return proto; +} + void validateTraceEvent(const ProtoExpectedDisplayFrameStart& received, const ProtoExpectedDisplayFrameStart& source) { ASSERT_TRUE(received.has_cookie()); @@ -759,48 +874,29 @@ void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& sour TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) { auto tracingSession = getTracingSessionForTest(); - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); tracingSession->StartBlocking(); int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30}); - int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); // Set up the display frame - mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11); + mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11)); mFrameTimeline->setSfPresent(26, presentFence1); presentFence1->signalForTest(31); int64_t traceCookie = snoopCurrentTraceCookie(); - ProtoExpectedDisplayFrameStart protoExpectedDisplayFrameStart; - protoExpectedDisplayFrameStart.set_cookie(traceCookie + 1); - protoExpectedDisplayFrameStart.set_token(displayFrameToken1); - protoExpectedDisplayFrameStart.set_pid(kSurfaceFlingerPid); - - ProtoFrameEnd protoExpectedDisplayFrameEnd; - protoExpectedDisplayFrameEnd.set_cookie(traceCookie + 1); - - ProtoActualDisplayFrameStart protoActualDisplayFrameStart; - protoActualDisplayFrameStart.set_cookie(traceCookie + 2); - protoActualDisplayFrameStart.set_token(displayFrameToken1); - protoActualDisplayFrameStart.set_pid(kSurfaceFlingerPid); - protoActualDisplayFrameStart.set_present_type( - ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME)); - protoActualDisplayFrameStart.set_on_time_finish(true); - protoActualDisplayFrameStart.set_gpu_composition(false); - protoActualDisplayFrameStart.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE)); - - ProtoFrameEnd protoActualDisplayFrameEnd; - protoActualDisplayFrameEnd.set_cookie(traceCookie + 2); - - // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the - // next frame - mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11); - mFrameTimeline->setSfPresent(55, presentFence2); - presentFence2->signalForTest(55); + auto protoExpectedDisplayFrameStart = + createProtoExpectedDisplayFrameStart(traceCookie + 1, displayFrameToken1, + kSurfaceFlingerPid); + auto protoExpectedDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1); + auto protoActualDisplayFrameStart = + createProtoActualDisplayFrameStart(traceCookie + 2, displayFrameToken1, + kSurfaceFlingerPid, + FrameTimelineEvent::PRESENT_ON_TIME, true, false, + FrameTimelineEvent::JANK_NONE); + auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 2); + addEmptyDisplayFrame(); flushTrace(); tracingSession->StopBlocking(); @@ -852,75 +948,126 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) { validateTraceEvent(actualDisplayFrameEnd, protoActualDisplayFrameEnd); } +TEST_F(FrameTimelineTest, traceDisplayFrame_predictionExpiredDoesNotTraceExpectedTimeline) { + auto tracingSession = getTracingSessionForTest(); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + + tracingSession->StartBlocking(); + int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({10, 25, 30}); + // Flush the token so that it would expire + flushTokens(systemTime() + maxTokenRetentionTime); + + // Set up the display frame + mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11)); + mFrameTimeline->setSfPresent(26, presentFence1); + presentFence1->signalForTest(31); + + int64_t traceCookie = snoopCurrentTraceCookie(); + + auto protoActualDisplayFrameStart = + createProtoActualDisplayFrameStart(traceCookie + 1, displayFrameToken1, + kSurfaceFlingerPid, + FrameTimelineEvent::PRESENT_UNSPECIFIED, false, + false, FrameTimelineEvent::JANK_UNKNOWN); + auto protoActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 1); + + addEmptyDisplayFrame(); + flushTrace(); + tracingSession->StopBlocking(); + + auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); + // Only actual timeline packets should be in the trace + EXPECT_EQ(packets.size(), 2u); + + // Packet - 0 : ActualDisplayFrameStart + const auto& packet0 = packets[0]; + ASSERT_TRUE(packet0.has_timestamp()); + EXPECT_EQ(packet0.timestamp(), 20u); + ASSERT_TRUE(packet0.has_frame_timeline_event()); + + const auto& event0 = packet0.frame_timeline_event(); + ASSERT_TRUE(event0.has_actual_display_frame_start()); + const auto& actualDisplayFrameStart = event0.actual_display_frame_start(); + validateTraceEvent(actualDisplayFrameStart, protoActualDisplayFrameStart); + + // Packet - 1 : FrameEnd (ActualDisplayFrame) + const auto& packet1 = packets[1]; + ASSERT_TRUE(packet1.has_timestamp()); + EXPECT_EQ(packet1.timestamp(), 26u); + ASSERT_TRUE(packet1.has_frame_timeline_event()); + + const auto& event1 = packet1.frame_timeline_event(); + ASSERT_TRUE(event1.has_frame_end()); + const auto& actualDisplayFrameEnd = event1.frame_end(); + validateTraceEvent(actualDisplayFrameEnd, protoActualDisplayFrameEnd); +} + TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { auto tracingSession = getTracingSessionForTest(); - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); tracingSession->StartBlocking(); int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 25, 40}); int64_t displayFrameToken1 = mTokenManager->generateTokenForPredictions({30, 35, 40}); - int64_t displayFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); - surfaceFrame1->setActualStartTime(0); - surfaceFrame1->setActualQueueTime(15); - surfaceFrame1->setAcquireFenceTime(20); + auto surfaceFrame2 = + mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, + sUidOne, sLayerNameOne, sLayerNameOne); + surfaceFrame1->setActualQueueTime(10); + surfaceFrame1->setDropTime(15); + + surfaceFrame2->setActualQueueTime(15); + surfaceFrame2->setAcquireFenceTime(20); // First 2 cookies will be used by the DisplayFrame int64_t traceCookie = snoopCurrentTraceCookie() + 2; - ProtoExpectedSurfaceFrameStart protoExpectedSurfaceFrameStart; - protoExpectedSurfaceFrameStart.set_cookie(traceCookie + 1); - protoExpectedSurfaceFrameStart.set_token(surfaceFrameToken); - protoExpectedSurfaceFrameStart.set_display_frame_token(displayFrameToken1); - protoExpectedSurfaceFrameStart.set_pid(sPidOne); - protoExpectedSurfaceFrameStart.set_layer_name(sLayerNameOne); - - ProtoFrameEnd protoExpectedSurfaceFrameEnd; - protoExpectedSurfaceFrameEnd.set_cookie(traceCookie + 1); - - ProtoActualSurfaceFrameStart protoActualSurfaceFrameStart; - protoActualSurfaceFrameStart.set_cookie(traceCookie + 2); - protoActualSurfaceFrameStart.set_token(surfaceFrameToken); - protoActualSurfaceFrameStart.set_display_frame_token(displayFrameToken1); - protoActualSurfaceFrameStart.set_pid(sPidOne); - protoActualSurfaceFrameStart.set_layer_name(sLayerNameOne); - protoActualSurfaceFrameStart.set_present_type( - ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME)); - protoActualSurfaceFrameStart.set_on_time_finish(true); - protoActualSurfaceFrameStart.set_gpu_composition(false); - protoActualSurfaceFrameStart.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE)); - - ProtoFrameEnd protoActualSurfaceFrameEnd; - protoActualSurfaceFrameEnd.set_cookie(traceCookie + 2); + auto protoDroppedSurfaceFrameExpectedStart = + createProtoExpectedSurfaceFrameStart(traceCookie + 1, surfaceFrameToken, + displayFrameToken1, sPidOne, sLayerNameOne); + auto protoDroppedSurfaceFrameExpectedEnd = createProtoFrameEnd(traceCookie + 1); + auto protoDroppedSurfaceFrameActualStart = + createProtoActualSurfaceFrameStart(traceCookie + 2, surfaceFrameToken, + displayFrameToken1, sPidOne, sLayerNameOne, + FrameTimelineEvent::PRESENT_DROPPED, false, false, + FrameTimelineEvent::JANK_NONE); + auto protoDroppedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 2); + + auto protoPresentedSurfaceFrameExpectedStart = + createProtoExpectedSurfaceFrameStart(traceCookie + 3, surfaceFrameToken, + displayFrameToken1, sPidOne, sLayerNameOne); + auto protoPresentedSurfaceFrameExpectedEnd = createProtoFrameEnd(traceCookie + 3); + auto protoPresentedSurfaceFrameActualStart = + createProtoActualSurfaceFrameStart(traceCookie + 4, surfaceFrameToken, + displayFrameToken1, sPidOne, sLayerNameOne, + FrameTimelineEvent::PRESENT_ON_TIME, true, false, + FrameTimelineEvent::JANK_NONE); + auto protoPresentedSurfaceFrameActualEnd = createProtoFrameEnd(traceCookie + 4); // Set up the display frame - mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11); - surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); + mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, Fps::fromPeriodNsecs(11)); + surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Dropped); + surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); + mFrameTimeline->addSurfaceFrame(surfaceFrame2); mFrameTimeline->setSfPresent(26, presentFence1); presentFence1->signalForTest(40); - // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the - // next frame - mFrameTimeline->setSfWakeUp(displayFrameToken2, 50, 11); - mFrameTimeline->setSfPresent(55, presentFence2); - presentFence2->signalForTest(55); - + addEmptyDisplayFrame(); flushTrace(); tracingSession->StopBlocking(); auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); - EXPECT_EQ(packets.size(), 8u); + // 4 DisplayFrame + 4 DroppedSurfaceFrame + 4 PresentedSurfaceFrame + EXPECT_EQ(packets.size(), 12u); - // Packet - 4 : ExpectedSurfaceFrameStart + // Packet - 4 : ExpectedSurfaceFrameStart1 const auto& packet4 = packets[4]; ASSERT_TRUE(packet4.has_timestamp()); EXPECT_EQ(packet4.timestamp(), 10u); @@ -928,10 +1075,10 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { const auto& event4 = packet4.frame_timeline_event(); ASSERT_TRUE(event4.has_expected_surface_frame_start()); - const auto& expectedSurfaceFrameStart = event4.expected_surface_frame_start(); - validateTraceEvent(expectedSurfaceFrameStart, protoExpectedSurfaceFrameStart); + const auto& expectedSurfaceFrameStart1 = event4.expected_surface_frame_start(); + validateTraceEvent(expectedSurfaceFrameStart1, protoDroppedSurfaceFrameExpectedStart); - // Packet - 5 : FrameEnd (ExpectedSurfaceFrame) + // Packet - 5 : FrameEnd (ExpectedSurfaceFrame1) const auto& packet5 = packets[5]; ASSERT_TRUE(packet5.has_timestamp()); EXPECT_EQ(packet5.timestamp(), 25u); @@ -939,10 +1086,10 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { const auto& event5 = packet5.frame_timeline_event(); ASSERT_TRUE(event5.has_frame_end()); - const auto& expectedSurfaceFrameEnd = event5.frame_end(); - validateTraceEvent(expectedSurfaceFrameEnd, protoExpectedSurfaceFrameEnd); + const auto& expectedSurfaceFrameEnd1 = event5.frame_end(); + validateTraceEvent(expectedSurfaceFrameEnd1, protoDroppedSurfaceFrameExpectedEnd); - // Packet - 6 : ActualSurfaceFrameStart + // Packet - 6 : ActualSurfaceFrameStart1 const auto& packet6 = packets[6]; ASSERT_TRUE(packet6.has_timestamp()); EXPECT_EQ(packet6.timestamp(), 10u); @@ -950,34 +1097,157 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { const auto& event6 = packet6.frame_timeline_event(); ASSERT_TRUE(event6.has_actual_surface_frame_start()); - const auto& actualSurfaceFrameStart = event6.actual_surface_frame_start(); - validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart); + const auto& actualSurfaceFrameStart1 = event6.actual_surface_frame_start(); + validateTraceEvent(actualSurfaceFrameStart1, protoDroppedSurfaceFrameActualStart); - // Packet - 7 : FrameEnd (ActualSurfaceFrame) + // Packet - 7 : FrameEnd (ActualSurfaceFrame1) const auto& packet7 = packets[7]; ASSERT_TRUE(packet7.has_timestamp()); - EXPECT_EQ(packet7.timestamp(), 20u); + EXPECT_EQ(packet7.timestamp(), 15u); ASSERT_TRUE(packet7.has_frame_timeline_event()); const auto& event7 = packet7.frame_timeline_event(); ASSERT_TRUE(event7.has_frame_end()); - const auto& actualSurfaceFrameEnd = event7.frame_end(); + const auto& actualSurfaceFrameEnd1 = event7.frame_end(); + validateTraceEvent(actualSurfaceFrameEnd1, protoDroppedSurfaceFrameActualEnd); + + // Packet - 8 : ExpectedSurfaceFrameStart2 + const auto& packet8 = packets[8]; + ASSERT_TRUE(packet8.has_timestamp()); + EXPECT_EQ(packet8.timestamp(), 10u); + ASSERT_TRUE(packet8.has_frame_timeline_event()); + + const auto& event8 = packet8.frame_timeline_event(); + ASSERT_TRUE(event8.has_expected_surface_frame_start()); + const auto& expectedSurfaceFrameStart2 = event8.expected_surface_frame_start(); + validateTraceEvent(expectedSurfaceFrameStart2, protoPresentedSurfaceFrameExpectedStart); + + // Packet - 9 : FrameEnd (ExpectedSurfaceFrame2) + const auto& packet9 = packets[9]; + ASSERT_TRUE(packet9.has_timestamp()); + EXPECT_EQ(packet9.timestamp(), 25u); + ASSERT_TRUE(packet9.has_frame_timeline_event()); + + const auto& event9 = packet9.frame_timeline_event(); + ASSERT_TRUE(event9.has_frame_end()); + const auto& expectedSurfaceFrameEnd2 = event9.frame_end(); + validateTraceEvent(expectedSurfaceFrameEnd2, protoPresentedSurfaceFrameExpectedEnd); + + // Packet - 10 : ActualSurfaceFrameStart2 + const auto& packet10 = packets[10]; + ASSERT_TRUE(packet10.has_timestamp()); + EXPECT_EQ(packet10.timestamp(), 10u); + ASSERT_TRUE(packet10.has_frame_timeline_event()); + + const auto& event10 = packet10.frame_timeline_event(); + ASSERT_TRUE(event10.has_actual_surface_frame_start()); + const auto& actualSurfaceFrameStart2 = event10.actual_surface_frame_start(); + validateTraceEvent(actualSurfaceFrameStart2, protoPresentedSurfaceFrameActualStart); + + // Packet - 11 : FrameEnd (ActualSurfaceFrame2) + const auto& packet11 = packets[11]; + ASSERT_TRUE(packet11.has_timestamp()); + EXPECT_EQ(packet11.timestamp(), 20u); + ASSERT_TRUE(packet11.has_frame_timeline_event()); + + const auto& event11 = packet11.frame_timeline_event(); + ASSERT_TRUE(event11.has_frame_end()); + const auto& actualSurfaceFrameEnd2 = event11.frame_end(); + validateTraceEvent(actualSurfaceFrameEnd2, protoPresentedSurfaceFrameActualEnd); +} + +TEST_F(FrameTimelineTest, traceSurfaceFrame_predictionExpiredDoesNotTraceExpectedTimeline) { + auto tracingSession = getTracingSessionForTest(); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + + tracingSession->StartBlocking(); + constexpr nsecs_t appStartTime = + std::chrono::duration_cast<std::chrono::nanoseconds>(10ms).count(); + constexpr nsecs_t appEndTime = + std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(); + constexpr nsecs_t appPresentTime = + std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count(); + int64_t surfaceFrameToken = + mTokenManager->generateTokenForPredictions({appStartTime, appEndTime, appPresentTime}); + + // Flush the token so that it would expire + flushTokens(systemTime() + maxTokenRetentionTime); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, /*inputEventId*/ 0}, + sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne); + surfaceFrame1->setActualQueueTime(appEndTime); + surfaceFrame1->setAcquireFenceTime(appEndTime); + + constexpr nsecs_t sfStartTime = + std::chrono::duration_cast<std::chrono::nanoseconds>(20ms).count(); + constexpr nsecs_t sfEndTime = + std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count(); + constexpr nsecs_t sfPresentTime = + std::chrono::duration_cast<std::chrono::nanoseconds>(30ms).count(); + int64_t displayFrameToken = + mTokenManager->generateTokenForPredictions({sfStartTime, sfEndTime, sfPresentTime}); + + // First 2 cookies will be used by the DisplayFrame + int64_t traceCookie = snoopCurrentTraceCookie() + 2; + + auto protoActualSurfaceFrameStart = + createProtoActualSurfaceFrameStart(traceCookie + 1, surfaceFrameToken, + displayFrameToken, sPidOne, sLayerNameOne, + FrameTimelineEvent::PRESENT_UNSPECIFIED, false, + false, FrameTimelineEvent::JANK_UNKNOWN); + auto protoActualSurfaceFrameEnd = createProtoFrameEnd(traceCookie + 1); + + // Set up the display frame + mFrameTimeline->setSfWakeUp(displayFrameToken, sfStartTime, Fps::fromPeriodNsecs(11)); + surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); + mFrameTimeline->addSurfaceFrame(surfaceFrame1); + mFrameTimeline->setSfPresent(sfEndTime, presentFence1); + presentFence1->signalForTest(sfPresentTime); + + addEmptyDisplayFrame(); + flushTrace(); + tracingSession->StopBlocking(); + + auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); + // Display Frame 4 packets + SurfaceFrame 2 packets + ASSERT_EQ(packets.size(), 6u); + + // Packet - 4 : ActualSurfaceFrameStart + const auto& packet4 = packets[4]; + ASSERT_TRUE(packet4.has_timestamp()); + EXPECT_EQ(packet4.timestamp(), + static_cast<uint64_t>(appEndTime - SurfaceFrame::kPredictionExpiredStartTimeDelta)); + ASSERT_TRUE(packet4.has_frame_timeline_event()); + + const auto& event4 = packet4.frame_timeline_event(); + ASSERT_TRUE(event4.has_actual_surface_frame_start()); + const auto& actualSurfaceFrameStart = event4.actual_surface_frame_start(); + validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart); + + // Packet - 5 : FrameEnd (ActualSurfaceFrame) + const auto& packet5 = packets[5]; + ASSERT_TRUE(packet5.has_timestamp()); + EXPECT_EQ(packet5.timestamp(), static_cast<uint64_t>(appEndTime)); + ASSERT_TRUE(packet5.has_frame_timeline_event()); + + const auto& event5 = packet5.frame_timeline_event(); + ASSERT_TRUE(event5.has_frame_end()); + const auto& actualSurfaceFrameEnd = event5.frame_end(); validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd); } // Tests for Jank classification TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions({10, 20, 30}); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30}); auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); - mFrameTimeline->setSfWakeUp(sfToken1, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); surfaceFrame->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame); mFrameTimeline->setSfPresent(26, presentFence1); @@ -1001,12 +1271,11 @@ TEST_F(FrameTimelineTest, jankClassification_presentOnTimeDoesNotClassify) { } TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresent) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); + Fps vsyncRate = Fps::fromPeriodNsecs(11); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70}); - mFrameTimeline->setSfWakeUp(sfToken1, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate); mFrameTimeline->setSfPresent(26, presentFence1); auto displayFrame = getDisplayFrame(0); presentFence1->signalForTest(30); @@ -1016,7 +1285,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresen // Trigger a flush by finalizing the next DisplayFrame auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - mFrameTimeline->setSfWakeUp(sfToken2, 52, 11); + mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate); mFrameTimeline->setSfPresent(56, presentFence2); displayFrame = getDisplayFrame(0); @@ -1030,7 +1299,6 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresen auto displayFrame2 = getDisplayFrame(1); presentFence2->signalForTest(65); EXPECT_EQ(displayFrame2->getActuals().presentTime, 0); - addEmptyDisplayFrame(); displayFrame2 = getDisplayFrame(1); @@ -1042,12 +1310,11 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishEarlyPresen } TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); + Fps vsyncRate = Fps::fromPeriodNsecs(11); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70}); - mFrameTimeline->setSfWakeUp(sfToken1, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 22, vsyncRate); mFrameTimeline->setSfPresent(26, presentFence1); auto displayFrame = getDisplayFrame(0); presentFence1->signalForTest(50); @@ -1057,7 +1324,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent // Trigger a flush by finalizing the next DisplayFrame auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); - mFrameTimeline->setSfWakeUp(sfToken2, 52, 11); + mFrameTimeline->setSfWakeUp(sfToken2, 52, vsyncRate); mFrameTimeline->setSfPresent(56, presentFence2); displayFrame = getDisplayFrame(0); @@ -1083,11 +1350,9 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameOnTimeFinishLatePresent } TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({12, 18, 40}); - mFrameTimeline->setSfWakeUp(sfToken1, 12, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11)); mFrameTimeline->setSfPresent(22, presentFence1); auto displayFrame = getDisplayFrame(0); @@ -1107,11 +1372,9 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishEarlyPresent) } TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); - mFrameTimeline->setSfWakeUp(sfToken1, 12, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 12, Fps::fromPeriodNsecs(11)); mFrameTimeline->setSfPresent(36, presentFence1); auto displayFrame = getDisplayFrame(0); presentFence1->signalForTest(52); @@ -1130,10 +1393,7 @@ TEST_F(FrameTimelineTest, jankClassification_displayFrameLateFinishLatePresent) } TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresent) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); - // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70}); @@ -1143,7 +1403,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame1->setAcquireFenceTime(16); - mFrameTimeline->setSfWakeUp(sfToken1, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(26, presentFence1); @@ -1162,7 +1422,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame2->setAcquireFenceTime(36); - mFrameTimeline->setSfWakeUp(sfToken2, 52, 11); + mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11)); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame2); mFrameTimeline->setSfPresent(56, presentFence2); @@ -1187,6 +1447,14 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen auto actuals2 = presentedSurfaceFrame2.getActuals(); EXPECT_EQ(actuals2.presentTime, 0); + ::testing::Mock::VerifyAndClearExpectations(mTimeStats.get()); + + EXPECT_CALL(*mTimeStats, + incrementJankyFrames( + TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne, + sLayerNameOne, JankType::PredictionError, 0, 5, + 0})); + addEmptyDisplayFrame(); // Fences for the second frame have flushed, so the present timestamps should be updated @@ -1203,10 +1471,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishEarlyPresen } TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); - // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 40}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({52, 56, 70}); @@ -1216,7 +1481,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame1->setAcquireFenceTime(16); - mFrameTimeline->setSfWakeUp(sfToken1, 22, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 22, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(26, presentFence1); @@ -1235,7 +1500,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame2->setAcquireFenceTime(36); - mFrameTimeline->setSfWakeUp(sfToken2, 52, 11); + mFrameTimeline->setSfWakeUp(sfToken2, 52, Fps::fromPeriodNsecs(11)); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame2); mFrameTimeline->setSfPresent(56, presentFence2); @@ -1260,6 +1525,14 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent auto actuals2 = presentedSurfaceFrame2.getActuals(); EXPECT_EQ(actuals2.presentTime, 0); + ::testing::Mock::VerifyAndClearExpectations(mTimeStats.get()); + + EXPECT_CALL(*mTimeStats, + incrementJankyFrames( + TimeStats::JankyFramesInfo{Fps::fromPeriodNsecs(11), std::nullopt, sUidOne, + sLayerNameOne, JankType::PredictionError, 0, 5, + 0})); + addEmptyDisplayFrame(); // Fences for the second frame have flushed, so the present timestamps should be updated @@ -1276,10 +1549,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameOnTimeFinishLatePresent } TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); - // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)); + auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({42, 46, 50}); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({5, 26, 60}); @@ -1287,7 +1558,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishEarlyPresent) mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame1->setAcquireFenceTime(40); - mFrameTimeline->setSfWakeUp(sfToken1, 42, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 42, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(46, presentFence1); @@ -1320,10 +1591,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) // AppDeadlineMissed. Second frame - DisplayFrame is janky. This should propagate DisplayFrame's // jank to the SurfaceFrame. - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); - // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t sfToken1 = mTokenManager->generateTokenForPredictions({32, 36, 40}); int64_t sfToken2 = mTokenManager->generateTokenForPredictions({42, 46, 50}); @@ -1333,7 +1601,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame1->setAcquireFenceTime(26); - mFrameTimeline->setSfWakeUp(sfToken1, 32, 11); + mFrameTimeline->setSfWakeUp(sfToken1, 32, Fps::fromPeriodNsecs(11)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(36, presentFence1); @@ -1352,7 +1620,7 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame2->setAcquireFenceTime(40); - mFrameTimeline->setSfWakeUp(sfToken2, 43, 11); + mFrameTimeline->setSfWakeUp(sfToken2, 43, Fps::fromPeriodNsecs(11)); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame2); mFrameTimeline->setSfPresent(56, presentFence2); @@ -1393,10 +1661,8 @@ TEST_F(FrameTimelineTest, jankClassification_surfaceFrameLateFinishLatePresent) } TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadlineMissed) { - // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); // Layer specific increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(2); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({40, 50, 60}); @@ -1407,7 +1673,7 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken1, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame1->setAcquireFenceTime(50); - mFrameTimeline->setSfWakeUp(sfToken1, 52, 30); + mFrameTimeline->setSfWakeUp(sfToken1, 52, Fps::fromPeriodNsecs(30)); surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented); mFrameTimeline->addSurfaceFrame(surfaceFrame1); mFrameTimeline->setSfPresent(56, presentFence1); @@ -1426,7 +1692,7 @@ TEST_F(FrameTimelineTest, jankClassification_multiJankBufferStuffingAndAppDeadli mFrameTimeline->createSurfaceFrameForToken({surfaceFrameToken2, sInputEventId}, sPidOne, sUidOne, sLayerNameOne, sLayerNameOne); surfaceFrame2->setAcquireFenceTime(84); - mFrameTimeline->setSfWakeUp(sfToken2, 112, 30); + mFrameTimeline->setSfWakeUp(sfToken2, 112, Fps::fromPeriodNsecs(30)); surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented, 54); mFrameTimeline->addSurfaceFrame(surfaceFrame2); mFrameTimeline->setSfPresent(116, presentFence2); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 376995f79e..06f2036615 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -1792,6 +1792,26 @@ TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_touch) { ASSERT_TRUE(frameRateOverrides.empty()); } +TEST_F(RefreshRateConfigsTest, updateDisplayModes) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, + /*currentConfigId=*/HWC_CONFIG_ID_30); + refreshRateConfigs->setDisplayManagerPolicy({DisplayModeId(HWC_CONFIG_ID_30), + /* allowGroupSwitching */ false, + /* range */ {Fps(30.0f), Fps(30.0f)}}); + + refreshRateConfigs->updateDisplayModes(m60_90Device, HWC_CONFIG_ID_60); + + const auto currentRefreshRate = refreshRateConfigs->getCurrentRefreshRate(); + EXPECT_TRUE(currentRefreshRate.getFps().equalsWithMargin(Fps(60.0))); + EXPECT_EQ(currentRefreshRate.getModeId(), HWC_CONFIG_ID_60); + + EXPECT_TRUE( + getMaxSupportedRefreshRate(*refreshRateConfigs).getFps().equalsWithMargin(Fps(90.0))); + EXPECT_TRUE( + getMinSupportedRefreshRate(*refreshRateConfigs).getFps().equalsWithMargin(Fps(60.0))); +} + } // namespace } // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index a6d07d00ee..5c8c2d888c 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -81,7 +81,7 @@ public: std::string name() override { return "BufferStateLayer"; } sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override { sp<Client> client; - LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT, + LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT, LAYER_FLAGS, LayerMetadata()); return new BufferStateLayer(args); } @@ -188,7 +188,8 @@ void SetFrameRateTest::setupScheduler() { .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), - std::move(eventThread), std::move(sfEventThread)); + std::move(eventThread), std::move(sfEventThread), /*callback*/ nullptr, + /*hasMultipleModes*/ true); } void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) { @@ -488,5 +489,28 @@ TEST_F(SetFrameRateTest, ValidateFrameRate) { EXPECT_FALSE(ValidateFrameRate(60.0f, ANATIVEWINDOW_FRAME_RATE_EXACT, "")); } +TEST_P(SetFrameRateTest, SetOnParentActivatesTree) { + const auto& layerFactory = GetParam(); + + auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); + if (!parent->isVisible()) { + // This is a hack as all the test layers except EffectLayer are not visible, + // but since the logic is unified in Layer, it should be fine. + return; + } + + auto child = mLayers.emplace_back(layerFactory->createLayer(mFlinger)); + addChild(parent, child); + + parent->setFrameRate(FRAME_RATE_VOTE1); + commitTransaction(); + + const auto layerHistorySummary = + mFlinger.mutableScheduler().mutableLayerHistory()->summarize(0); + ASSERT_EQ(2u, layerHistorySummary.size()); + EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[0].desiredRefreshRate)); + EXPECT_TRUE(FRAME_RATE_VOTE1.rate.equalsWithMargin(layerHistorySummary[1].desiredRefreshRate)); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index ce2bfefc44..63baf7dee2 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -246,6 +246,8 @@ public: void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } + TestableScheduler& mutableScheduler() const { return *mScheduler; } + using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction; void setCreateBufferQueueFunction(CreateBufferQueueFunction f) { mFactory.mCreateBufferQueue = f; @@ -410,6 +412,10 @@ public: const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; } + mock::FrameTracer* getFrameTracer() const { + return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get()); + } + /* ------------------------------------------------------------------------ * Read-write access to private data to set up preconditions and assert * post-conditions. @@ -630,7 +636,7 @@ public: public: FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger, std::shared_ptr<compositionengine::Display> compositionDisplay, - std::optional<DisplayConnectionType> connectionType, + std::optional<ui::DisplayConnectionType> connectionType, std::optional<hal::HWDisplayId> hwcDisplayId, bool isPrimary) : mFlinger(flinger), mCreationArgs(flinger.mFlinger.get(), flinger.mFlinger->getHwComposer(), diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index df4464ef52..e90366448c 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -55,14 +55,21 @@ using testing::UnorderedElementsAre; using PowerMode = hardware::graphics::composer::V2_4::IComposerClient::PowerMode; // clang-format off -#define FMT_PROTO true -#define FMT_STRING false -#define LAYER_ID_0 0 -#define LAYER_ID_1 1 -#define UID_0 123 -#define LAYER_ID_INVALID -1 -#define NUM_LAYERS 1 -#define NUM_LAYERS_INVALID "INVALID" +#define FMT_PROTO true +#define FMT_STRING false +#define LAYER_ID_0 0 +#define LAYER_ID_1 1 +#define UID_0 123 +#define REFRESH_RATE_0 61 +#define RENDER_RATE_0 31 +#define REFRESH_RATE_BUCKET_0 60 +#define RENDER_RATE_BUCKET_0 30 +#define LAYER_ID_INVALID -1 +#define NUM_LAYERS 1 +#define NUM_LAYERS_INVALID "INVALID" + +const constexpr Fps kRefreshRate0 = Fps(static_cast<float>(REFRESH_RATE_0)); +const constexpr Fps kRenderRate0 = Fps(static_cast<float>(RENDER_RATE_0)); enum InputCommand : int32_t { ENABLE = 0, @@ -246,11 +253,13 @@ void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumbe ASSERT_NO_FATAL_FAILURE(mTimeStats->setDesiredTime(id, frameNumber, ts)); break; case TimeStamp::PRESENT: - ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentTime(id, frameNumber, ts)); + ASSERT_NO_FATAL_FAILURE( + mTimeStats->setPresentTime(id, frameNumber, ts, kRefreshRate0, kRenderRate0)); break; case TimeStamp::PRESENT_FENCE: - ASSERT_NO_FATAL_FAILURE( - mTimeStats->setPresentFence(id, frameNumber, std::make_shared<FenceTime>(ts))); + ASSERT_NO_FATAL_FAILURE(mTimeStats->setPresentFence(id, frameNumber, + std::make_shared<FenceTime>(ts), + kRefreshRate0, kRenderRate0)); break; default: ALOGD("Invalid timestamp type"); @@ -352,58 +361,52 @@ TEST_F(TimeStatsTest, canIncreaseBadDesiredPresent) { EXPECT_THAT(result, HasSubstr(expectedResult)); } -TEST_F(TimeStatsTest, canIncreaseJankyFrames) { +TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) { // this stat is not in the proto so verify by checking the string dump EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); - mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed); - mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed); - mTimeStats->incrementJankyFrames(JankType::DisplayHAL); - mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed); - mTimeStats->incrementJankyFrames(JankType::None); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::DisplayHAL, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::AppDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerScheduling, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::PredictionError, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, + 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::None, 1, 2, 3}); const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); - std::string expectedResult = "totalTimelineFrames = " + std::to_string(5); + std::string expectedResult = + "displayRefreshRate = " + std::to_string(REFRESH_RATE_BUCKET_0) + " fps"; EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "jankyFrames = " + std::to_string(4); + expectedResult = "renderRate = " + std::to_string(RENDER_RATE_BUCKET_0) + " fps"; EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1); + expectedResult = "totalTimelineFrames = " + std::to_string(8); EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1); - EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1); + expectedResult = "jankyFrames = " + std::to_string(7); EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "appUnattributedJankyFrame = " + std::to_string(1); + expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1); EXPECT_THAT(result, HasSubstr(expectedResult)); -} - -TEST_F(TimeStatsTest, canIncreaseJankyFramesForLayer) { - // this stat is not in the proto so verify by checking the string dump - EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); - - insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerCpuDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerGpuDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None); - - const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); - std::string expectedResult = "totalTimelineFrames = " + std::to_string(5); + expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1); EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "jankyFrames = " + std::to_string(4); + expectedResult = "sfUnattributedJankyFrames = " + std::to_string(1); EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1); + expectedResult = "appUnattributedJankyFrames = " + std::to_string(2); EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1); + expectedResult = "sfSchedulingJankyFrames = " + std::to_string(1); EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1); + expectedResult = "sfPredictionErrorJankyFrames = " + std::to_string(1); EXPECT_THAT(result, HasSubstr(expectedResult)); - expectedResult = "appUnattributedJankyFrame = " + std::to_string(1); + expectedResult = "appBufferStuffingJankyFrames = " + std::to_string(1); EXPECT_THAT(result, HasSubstr(expectedResult)); } @@ -455,14 +458,10 @@ TEST_F(TimeStatsTest, canIncreaseCompositionStrategyChanges) { TEST_F(TimeStatsTest, canAverageFrameDuration) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); mTimeStats->setPowerMode(PowerMode::ON); - mTimeStats - ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(6ms) - .count()); - mTimeStats - ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(16ms) - .count()); + mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(), + std::chrono::nanoseconds(6ms).count()); + mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(), + std::chrono::nanoseconds(16ms).count()); const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); EXPECT_THAT(result, HasSubstr("averageFrameDuration = 10.000 ms")); @@ -470,22 +469,19 @@ TEST_F(TimeStatsTest, canAverageFrameDuration) { TEST_F(TimeStatsTest, canAverageRenderEngineTimings) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); - mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms) - .count(), + mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(1ms).count(), std::make_shared<FenceTime>( std::chrono::duration_cast< std::chrono::nanoseconds>(3ms) .count())); - mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms) - .count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(8ms) - .count()); + mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(4ms).count(), + std::chrono::nanoseconds(8ms).count()); // Push a fake present fence to trigger flushing the RenderEngine timings. mTimeStats->setPowerMode(PowerMode::ON); - mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>( - std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count())); + mTimeStats->setPresentFenceGlobal( + std::make_shared<FenceTime>(std::chrono::nanoseconds(1ms).count())); const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 3.000 ms")); @@ -524,15 +520,11 @@ TEST_F(TimeStatsTest, canInsertGlobalFrameDuration) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); mTimeStats->setPowerMode(PowerMode::OFF); - mTimeStats - ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(5ms) - .count()); + mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(), + std::chrono::nanoseconds(5ms).count()); mTimeStats->setPowerMode(PowerMode::ON); - mTimeStats - ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(6ms) - .count()); + mTimeStats->recordFrameDuration(std::chrono::nanoseconds(3ms).count(), + std::chrono::nanoseconds(6ms).count()); SFTimeStatsGlobalProto globalProto; ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); @@ -546,17 +538,14 @@ TEST_F(TimeStatsTest, canInsertGlobalFrameDuration) { TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); - mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms) - .count(), + mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(1ms).count(), std::make_shared<FenceTime>( std::chrono::duration_cast< std::chrono::nanoseconds>(3ms) .count())); - mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms) - .count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(6ms) - .count()); + mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(4ms).count(), + std::chrono::nanoseconds(6ms).count()); // First verify that flushing RenderEngine durations did not occur yet. SFTimeStatsGlobalProto preFlushProto; @@ -565,8 +554,8 @@ TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) { // Push a fake present fence to trigger flushing the RenderEngine timings. mTimeStats->setPowerMode(PowerMode::ON); - mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>( - std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count())); + mTimeStats->setPresentFenceGlobal( + std::make_shared<FenceTime>(std::chrono::nanoseconds(1ms).count())); // Now we can verify that RenderEngine durations were flushed now. SFTimeStatsGlobalProto postFlushProto; @@ -801,14 +790,10 @@ TEST_F(TimeStatsTest, canClearTimeStats) { ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementClientCompositionFrames()); ASSERT_NO_FATAL_FAILURE(mTimeStats->setPowerMode(PowerMode::ON)); - mTimeStats - ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(6ms) - .count()); - mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms) - .count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(6ms) - .count()); + mTimeStats->recordFrameDuration(std::chrono::nanoseconds(3ms).count(), + std::chrono::nanoseconds(6ms).count()); + mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(4ms).count(), + std::chrono::nanoseconds(6ms).count()); ASSERT_NO_FATAL_FAILURE( mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(1000000))); ASSERT_NO_FATAL_FAILURE( @@ -837,25 +822,30 @@ TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) { ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementRefreshRateSwitches()); ASSERT_NO_FATAL_FAILURE(mTimeStats->incrementCompositionStrategyChanges()); mTimeStats->setPowerMode(PowerMode::ON); - mTimeStats - ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(5ms) - .count()); - mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms) - .count(), - std::chrono::duration_cast<std::chrono::nanoseconds>(6ms) - .count()); - mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>( - std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count())); - - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerCpuDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerGpuDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None); + mTimeStats->recordFrameDuration(std::chrono::nanoseconds(1ms).count(), + std::chrono::nanoseconds(5ms).count()); + mTimeStats->recordRenderEngineDuration(std::chrono::nanoseconds(4ms).count(), + std::chrono::nanoseconds(6ms).count()); + mTimeStats->setPresentFenceGlobal( + std::make_shared<FenceTime>(std::chrono::nanoseconds(1ms).count())); + + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::DisplayHAL, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::AppDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerScheduling, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::PredictionError, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, + 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::None, 1, 2, 3}); EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty()); @@ -865,11 +855,6 @@ TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) { EXPECT_THAT(result, HasSubstr("compositionStrategyChanges = 0")); EXPECT_THAT(result, HasSubstr("averageFrameDuration = 0.000 ms")); EXPECT_THAT(result, HasSubstr("averageRenderEngineTiming = 0.000 ms")); - EXPECT_THAT(result, HasSubstr("jankyFrames = 0")); - EXPECT_THAT(result, HasSubstr("sfLongCpuJankyFrames = 0")); - EXPECT_THAT(result, HasSubstr("sfLongGpuJankyFrames = 0")); - EXPECT_THAT(result, HasSubstr("sfUnattributedJankyFrame = 0")); - EXPECT_THAT(result, HasSubstr("appUnattributedJankyFrame = 0")); } TEST_F(TimeStatsTest, canDumpWithMaxLayers) { @@ -1000,11 +985,23 @@ TEST_F(TimeStatsTest, globalStatsCallback) { mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)); mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)); - mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerCpuDeadlineMissed); - mTimeStats->incrementJankyFrames(JankType::SurfaceFlingerGpuDeadlineMissed); - mTimeStats->incrementJankyFrames(JankType::DisplayHAL); - mTimeStats->incrementJankyFrames(JankType::AppDeadlineMissed); - mTimeStats->incrementJankyFrames(JankType::None); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::DisplayHAL, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::AppDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerScheduling, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::PredictionError, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, + 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::None, 1, 2, 3}); EXPECT_THAT(mDelegate->mAtomTags, UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, @@ -1015,6 +1012,8 @@ TEST_F(TimeStatsTest, globalStatsCallback) { std::string expectedFrameDuration = buildExpectedHistogramBytestring({2}, {1}); std::string expectedRenderEngineTiming = buildExpectedHistogramBytestring({1, 2}, {1, 1}); std::string expectedEmptyHistogram = buildExpectedHistogramBytestring({}, {}); + std::string expectedSfDeadlineMissed = buildExpectedHistogramBytestring({1}, {7}); + std::string expectedSfPredictionErrors = buildExpectedHistogramBytestring({2}, {7}); { InSequence seq; @@ -1038,27 +1037,29 @@ TEST_F(TimeStatsTest, globalStatsCallback) { expectedRenderEngineTiming.c_str(), expectedRenderEngineTiming.size()), expectedRenderEngineTiming.size())); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 8)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 7)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 2)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, REFRESH_RATE_BUCKET_0)); EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, - BytesEq((const uint8_t*)expectedEmptyHistogram.c_str(), - expectedEmptyHistogram.size()), - expectedEmptyHistogram.size())); + BytesEq((const uint8_t*) + expectedSfDeadlineMissed.c_str(), + expectedSfDeadlineMissed.size()), + expectedSfDeadlineMissed.size())); EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, - BytesEq((const uint8_t*)expectedEmptyHistogram.c_str(), - expectedEmptyHistogram.size()), - expectedEmptyHistogram.size())); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); + BytesEq((const uint8_t*) + expectedSfPredictionErrors.c_str(), + expectedSfPredictionErrors.size()), + expectedSfPredictionErrors.size())); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, RENDER_RATE_BUCKET_0)); EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent)); } @@ -1091,14 +1092,23 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { } insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerCpuDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::SurfaceFlingerGpuDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::DisplayHAL); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), - JankType::AppDeadlineMissed); - mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), JankType::None); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerCpuDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerGpuDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::DisplayHAL, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::AppDeadlineMissed, 1, 2, 3}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::SurfaceFlingerScheduling, 1, 2, 2}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::PredictionError, 1, 2, 2}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::AppDeadlineMissed | JankType::BufferStuffing, 1, 2, + 2}); + mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), + JankType::None, 1, 2, 3}); EXPECT_THAT(mDelegate->mAtomTags, UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, @@ -1113,7 +1123,7 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { std::string expectedDesiredToPresent = buildExpectedHistogramBytestring({1}, {1}); std::string expectedPostToAcquire = buildExpectedHistogramBytestring({1}, {1}); std::string expectedFrameRateOverride = frameRateVoteToProtoByteString(0.0, 0, 0); - std::string expectedEmptyHistogram = buildExpectedHistogramBytestring({}, {}); + std::string expectedAppDeadlineMissed = buildExpectedHistogramBytestring({3, 2}, {4, 3}); { InSequence seq; EXPECT_CALL(*mDelegate, @@ -1161,17 +1171,17 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, BAD_DESIRED_PRESENT_FRAMES)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, UID_0)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 5)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 4)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 8)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 7)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 2)); EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); - EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 0)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, REFRESH_RATE_BUCKET_0)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, RENDER_RATE_BUCKET_0)); EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, BytesEq((const uint8_t*) @@ -1180,9 +1190,10 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { expectedFrameRateOverride.size())); EXPECT_CALL(*mDelegate, statsEventWriteByteArray(mDelegate->mEvent, - BytesEq((const uint8_t*)expectedEmptyHistogram.c_str(), - expectedEmptyHistogram.size()), - expectedEmptyHistogram.size())); + BytesEq((const uint8_t*) + expectedAppDeadlineMissed.c_str(), + expectedAppDeadlineMissed.size()), + expectedAppDeadlineMissed.size())); EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent)); } diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp new file mode 100644 index 0000000000..dbadf75d44 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <gui/SurfaceComposerClient.h> +#include <log/log.h> +#include <utils/String8.h> + +#include "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockComposer.h" +#include "mock/MockEventThread.h" +#include "mock/MockVsyncController.h" + +namespace android { + +using testing::_; +using testing::Mock; +using testing::Return; +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using PresentState = frametimeline::SurfaceFrame::PresentState; + +class TransactionFrameTracerTest : public testing::Test { +public: + TransactionFrameTracerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + setupScheduler(); + setupComposer(0); + } + + ~TransactionFrameTracerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + sp<BufferStateLayer> createBufferStateLayer() { + sp<Client> client; + LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0, + LayerMetadata()); + return new BufferStateLayer(args); + } + + void commitTransaction(Layer* layer) { + layer->pushPendingState(); + auto c = layer->getCurrentState(); + if (layer->applyPendingStates(&c)) { + layer->commitTransaction(c); + } + } + + void setupScheduler() { + auto eventThread = std::make_unique<mock::EventThread>(); + auto sfEventThread = std::make_unique<mock::EventThread>(); + + EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*eventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback()))); + + auto vsyncController = std::make_unique<mock::VsyncController>(); + auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); + + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), + std::move(eventThread), std::move(sfEventThread)); + } + + void setupComposer(uint32_t virtualDisplayCount) { + mComposer = new Hwc2::mock::Composer(); + EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount)); + mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + + Mock::VerifyAndClear(mComposer); + } + + TestableSurfaceFlinger mFlinger; + Hwc2::mock::Composer* mComposer = nullptr; + FenceToFenceTimeMap fenceFactory; + client_cache_t mClientCache; + + void BLASTTransactionSendsFrameTracerEvents() { + sp<BufferStateLayer> layer = createBufferStateLayer(); + + sp<Fence> fence(new Fence()); + sp<GraphicBuffer> buffer{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)}; + int32_t layerId = layer->getSequence(); + uint64_t bufferId = buffer->getId(); + uint64_t frameNumber = 5; + nsecs_t dequeueTime = 10; + nsecs_t postTime = 20; + EXPECT_CALL(*mFlinger.getFrameTracer(), traceNewLayer(layerId, "buffer-state-layer")); + EXPECT_CALL(*mFlinger.getFrameTracer(), + traceTimestamp(layerId, bufferId, frameNumber, dequeueTime, + FrameTracer::FrameEvent::DEQUEUE, /*duration*/ 0)); + EXPECT_CALL(*mFlinger.getFrameTracer(), + traceTimestamp(layerId, bufferId, frameNumber, postTime, + FrameTracer::FrameEvent::QUEUE, /*duration*/ 0)); + layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache, + frameNumber, dequeueTime, FrameTimelineInfo{}); + + commitTransaction(layer.get()); + bool computeVisisbleRegions; + nsecs_t latchTime = 25; + EXPECT_CALL(*mFlinger.getFrameTracer(), + traceFence(layerId, bufferId, frameNumber, _, + FrameTracer::FrameEvent::ACQUIRE_FENCE, /*startTime*/ 0)); + EXPECT_CALL(*mFlinger.getFrameTracer(), + traceTimestamp(layerId, bufferId, frameNumber, latchTime, + FrameTracer::FrameEvent::LATCH, /*duration*/ 0)); + layer->updateTexImage(computeVisisbleRegions, latchTime, /*expectedPresentTime*/ 0); + + auto glDoneFence = fenceFactory.createFenceTimeForTest(fence); + auto presentFence = fenceFactory.createFenceTimeForTest(fence); + CompositorTiming compositorTiming; + EXPECT_CALL(*mFlinger.getFrameTracer(), + traceFence(layerId, bufferId, frameNumber, presentFence, + FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0)); + layer->onPostComposition(nullptr, glDoneFence, presentFence, compositorTiming); + } +}; + +TEST_F(TransactionFrameTracerTest, BLASTTransactionSendsFrameTracerEvents) { + BLASTTransactionSendsFrameTracerEvents(); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index aa6798d84c..bf9ec39c35 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -157,8 +157,10 @@ public: sp<Fence> fence2(new Fence()); auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)}; + nsecs_t start = systemTime(); layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, {/*vsyncId*/ 1, /*inputEventId*/ 0}); + nsecs_t end = systemTime(); acquireFence2->signalForTest(12); EXPECT_EQ(0u, layer->mCurrentState.bufferlessSurfaceFramesTX.size()); @@ -171,6 +173,9 @@ public: EXPECT_EQ(1, droppedSurfaceFrame->getToken()); EXPECT_EQ(PresentState::Dropped, droppedSurfaceFrame->getPresentState()); + EXPECT_EQ(0u, droppedSurfaceFrame->getActuals().endTime); + auto dropTime = droppedSurfaceFrame->getDropTime(); + EXPECT_TRUE(dropTime > start && dropTime < end); EXPECT_EQ(1, presentedSurfaceFrame->getToken()); EXPECT_EQ(PresentState::Presented, presentedSurfaceFrame->getPresentState()); @@ -328,6 +333,41 @@ public: EXPECT_EQ(PresentState::Presented, bufferlessSurfaceFrame2->getPresentState()); EXPECT_EQ(12, bufferlessSurfaceFrame2->getActuals().endTime); } + + void PendingSurfaceFramesRemovedAfterClassification() { + sp<BufferStateLayer> layer = createBufferStateLayer(); + + sp<Fence> fence1(new Fence()); + auto acquireFence1 = fenceFactory.createFenceTimeForTest(fence1); + sp<GraphicBuffer> buffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)}; + layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); + ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX); + const auto droppedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX; + + sp<Fence> fence2(new Fence()); + auto acquireFence2 = fenceFactory.createFenceTimeForTest(fence2); + sp<GraphicBuffer> buffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)}; + layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, + {/*vsyncId*/ 1, /*inputEventId*/ 0}); + acquireFence2->signalForTest(12); + + ASSERT_NE(nullptr, layer->mCurrentState.bufferSurfaceFrameTX); + auto& presentedSurfaceFrame = layer->mCurrentState.bufferSurfaceFrameTX; + + commitTransaction(layer.get()); + bool computeVisisbleRegions; + layer->updateTexImage(computeVisisbleRegions, 15, 0); + + // Both the droppedSurfaceFrame and presentedSurfaceFrame should be in + // pendingJankClassifications. + EXPECT_EQ(2u, layer->mPendingJankClassifications.size()); + presentedSurfaceFrame->onPresent(20, JankType::None, Fps::fromPeriodNsecs(11), + /*displayDeadlineDelta*/ 0, /*displayPresentDelta*/ 0); + layer->releasePendingBuffer(25); + + EXPECT_EQ(0u, layer->mPendingJankClassifications.size()); + } }; TEST_F(TransactionSurfaceFrameTest, PresentedBufferlessSurfaceFrame) { @@ -364,4 +404,7 @@ TEST_F(TransactionSurfaceFrameTest, MergePendingStates_BufferlessSurfaceFramesWithOverlappingToken(); } +TEST_F(TransactionSurfaceFrameTest, PendingSurfaceFramesRemovedAfterClassification) { + PendingSurfaceFramesRemovedAfterClassification(); +} } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h index 0a6a9f4550..44b9b73fd4 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTimeline.h @@ -31,7 +31,7 @@ public: MOCK_METHOD0(onBootFinished, void()); MOCK_METHOD1(addSurfaceFrame, void(std::shared_ptr<frametimeline::SurfaceFrame>)); - MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, nsecs_t)); + MOCK_METHOD3(setSfWakeUp, void(int64_t, nsecs_t, Fps)); MOCK_METHOD2(setSfPresent, void(nsecs_t, const std::shared_ptr<FenceTime>&)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h index 99ec353095..3e4a0b8c09 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -48,10 +48,11 @@ public: MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t)); MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t)); MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&)); - MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t)); - MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&)); - MOCK_METHOD1(incrementJankyFrames, void(int32_t)); - MOCK_METHOD3(incrementJankyFrames, void(uid_t, const std::string&, int32_t)); + MOCK_METHOD5(setPresentTime, void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>)); + MOCK_METHOD5(setPresentFence, + void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, + std::optional<Fps>)); + MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&)); MOCK_METHOD1(onDestroy, void(int32_t)); MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t)); MOCK_METHOD1(setPowerMode, diff --git a/services/surfaceflinger/tests/vsync/Android.bp b/services/surfaceflinger/tests/vsync/Android.bp index 6a89945a92..bae9796598 100644 --- a/services/surfaceflinger/tests/vsync/Android.bp +++ b/services/surfaceflinger/tests/vsync/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "test-vsync-events", defaults: ["surfaceflinger_defaults"], diff --git a/services/surfaceflinger/tests/waitforvsync/Android.bp b/services/surfaceflinger/tests/waitforvsync/Android.bp index cb6d0fde0d..ffed4d77c7 100644 --- a/services/surfaceflinger/tests/waitforvsync/Android.bp +++ b/services/surfaceflinger/tests/waitforvsync/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_binary { name: "test-waitforvsync", cflags: [ diff --git a/services/utils/Android.bp b/services/utils/Android.bp index f3d2bc9766..81e1232e2c 100644 --- a/services/utils/Android.bp +++ b/services/utils/Android.bp @@ -15,6 +15,15 @@ // // Static library used in testing and executables // +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libserviceutils", diff --git a/services/utils/tests/Android.bp b/services/utils/tests/Android.bp index f21254cd7d..54cf5b7404 100644 --- a/services/utils/tests/Android.bp +++ b/services/utils/tests/Android.bp @@ -14,6 +14,15 @@ // Build unit tests. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_test { name: "prioritydumper_test", test_suites: ["device-tests"], diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp index 96b76ba2fe..f85fa10e0d 100644 --- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp @@ -77,6 +77,8 @@ public: MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override)); MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override)); MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override)); + MOCK_METHOD(Status, getQFactor, (float * ret), (override)); + MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override)); MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); MOCK_METHOD(std::string, getInterfaceHash, (), (override)); MOCK_METHOD(IBinder*, onAsBinder, (), (override)); diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp index dd71a6a525..bcfd15d4b2 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp @@ -71,6 +71,8 @@ public: MOCK_METHOD(Status, getSupportedAlwaysOnEffects, (std::vector<Effect> * ret), (override)); MOCK_METHOD(Status, alwaysOnEnable, (int32_t id, Effect e, EffectStrength s), (override)); MOCK_METHOD(Status, alwaysOnDisable, (int32_t id), (override)); + MOCK_METHOD(Status, getQFactor, (float * ret), (override)); + MOCK_METHOD(Status, getResonantFrequency, (float * ret), (override)); MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); MOCK_METHOD(std::string, getInterfaceHash, (), (override)); MOCK_METHOD(IBinder*, onAsBinder, (), (override)); diff --git a/services/vr/Android.bp b/services/vr/Android.bp index 80df479e8d..980dcf4616 100644 --- a/services/vr/Android.bp +++ b/services/vr/Android.bp @@ -1,3 +1,13 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + default_applicable_licenses: ["frameworks_native_license"], +} + subdirs = [ "*", ] diff --git a/services/vr/bufferhubd/Android.bp b/services/vr/bufferhubd/Android.bp index 7097e7a8cc..8523bb205f 100644 --- a/services/vr/bufferhubd/Android.bp +++ b/services/vr/bufferhubd/Android.bp @@ -12,6 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + sharedLibraries = [ "libbase", "libcutils", diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp index 372873149d..866007e188 100644 --- a/services/vr/hardware_composer/Android.bp +++ b/services/vr/hardware_composer/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libvr_hwc-hal", diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp index a1d5392071..98afdec517 100644 --- a/services/vr/hardware_composer/aidl/Android.bp +++ b/services/vr/hardware_composer/aidl/Android.bp @@ -1,3 +1,14 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + // SPDX-license-identifier-Unicode-DFS + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_static { name: "libvr_hwc-binder", srcs: [ diff --git a/services/vr/performanced/Android.bp b/services/vr/performanced/Android.bp index 0ef8cc4762..5eca88b1f9 100644 --- a/services/vr/performanced/Android.bp +++ b/services/vr/performanced/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-MIT + default_applicable_licenses: ["frameworks_native_license"], +} + cc_defaults { name: "performanced_defaults", static_libs: [ diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp index 9cf4905b68..f2ec5a42b1 100644 --- a/services/vr/virtual_touchpad/Android.bp +++ b/services/vr/virtual_touchpad/Android.bp @@ -2,6 +2,15 @@ // Touchpad implementation. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + src = [ "EvdevInjector.cpp", "VirtualTouchpadEvdev.cpp", diff --git a/vulkan/Android.bp b/vulkan/Android.bp index 4934970aaa..33599ea35b 100644 --- a/vulkan/Android.bp +++ b/vulkan/Android.bp @@ -14,6 +14,16 @@ // This module makes the Vulkan libhardware HAL headers available, for // the loader and for HAL/driver implementations. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + // SPDX-license-identifier-MIT + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_headers { name: "hwvulkan_headers", vendor_available: true, diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 1d29bab355..67cd8754a3 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -13,6 +13,15 @@ // limitations under the License. // Headers module is in external/vulkan-headers/Android.bp. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + ndk_library { name: "libvulkan", symbol_file: "libvulkan.map.txt", diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp index ba025046fa..0daad9c634 100644 --- a/vulkan/nulldrv/Android.bp +++ b/vulkan/nulldrv/Android.bp @@ -12,6 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { // Real drivers would set this to vulkan.$(TARGET_BOARD_PLATFORM) name: "vulkan.default", diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp index 8528898f3d..fa0258bc06 100644 --- a/vulkan/vkjson/Android.bp +++ b/vulkan/vkjson/Android.bp @@ -1,3 +1,12 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + cc_library_shared { name: "libvkjson", srcs: [ |