diff options
597 files changed, 41475 insertions, 20916 deletions
diff --git a/.gitignore b/.gitignore index 0d20b6487c..685e379850 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ +*.iml *.pyc +.idea/ diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 9cab9b4c41..560459be13 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -6,6 +6,7 @@ clang_format = true clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp cmds/idlcli/ include/input/ + include/powermanager/ libs/binder/fuzzer/ libs/binder/ndk/ libs/binder/tests/fuzzers/ @@ -19,7 +20,9 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp opengl/libs/ services/bufferhub/ services/inputflinger/ + services/powermanager/ services/surfaceflinger/ + services/vibratorservice/ services/vr/ vulkan/ diff --git a/TEST_MAPPING b/TEST_MAPPING index 8173c8927e..307e21cc44 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -53,11 +53,22 @@ }, { "include-filter": "*RelativeZTest.*" + }, + { + "include-filter": "*RefreshRateOverlayTest.*" } ] }, { "name": "libsurfaceflinger_unittest" + }, + { + "name": "CtsGraphicsTestCases", + "options": [ + { + "include-filter": "android.graphics.cts.VulkanPreTransformTest" + } + ] } ] } diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl index 7026ca8fad..491c629278 100644 --- a/aidl/gui/android/view/LayerMetadataKey.aidl +++ b/aidl/gui/android/view/LayerMetadataKey.aidl @@ -23,4 +23,6 @@ enum LayerMetadataKey { METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3, METADATA_MOUSE_CURSOR = 4, + METADATA_ACCESSIBILITY_ID = 5, + METADATA_OWNER_PID = 6, } diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 2519ffa45d..31848431e4 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -99,7 +99,9 @@ struct TracingCategory { /* Tracing categories */ static const TracingCategory k_categories[] = { - { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { } }, + { "gfx", "Graphics", ATRACE_TAG_GRAPHICS, { + { OPT, "events/gpu_mem/gpu_mem_total/enable" }, + } }, { "input", "Input", ATRACE_TAG_INPUT, { } }, { "view", "View System", ATRACE_TAG_VIEW, { } }, { "webview", "WebView", ATRACE_TAG_WEBVIEW, { } }, @@ -241,6 +243,7 @@ static const TracingCategory k_categories[] = { { OPT, "events/kmem/ion_heap_grow/enable" }, { OPT, "events/kmem/ion_heap_shrink/enable" }, { OPT, "events/ion/ion_stat/enable" }, + { OPT, "events/gpu_mem/gpu_mem_total/enable" }, } }, { "thermal", "Thermal event", 0, { { REQ, "events/thermal/thermal_temperature/enable" }, diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 994375b30e..d95d04a8d9 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -131,6 +131,34 @@ on late-init chmod 0666 /sys/kernel/tracing/events/task/task_newtask/enable chmod 0666 /sys/kernel/debug/tracing/events/gpu_mem/gpu_mem_total/enable chmod 0666 /sys/kernel/tracing/events/gpu_mem/gpu_mem_total/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/enable + chmod 0666 /sys/kernel/tracing/events/irq/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/irq_handler_entry/enable + chmod 0666 /sys/kernel/tracing/events/irq/irq_handler_entry/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/irq_handler_exit/enable + chmod 0666 /sys/kernel/tracing/events/irq/irq_handler_exit/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_entry/enable + chmod 0666 /sys/kernel/tracing/events/irq/softirq_entry/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_exit/enable + chmod 0666 /sys/kernel/tracing/events/irq/softirq_exit/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/softirq_raise/enable + chmod 0666 /sys/kernel/tracing/events/irq/softirq_raise/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_entry/enable + chmod 0666 /sys/kernel/tracing/events/irq/tasklet_entry/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_exit/enable + chmod 0666 /sys/kernel/tracing/events/irq/tasklet_exit/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_hi_entry/enable + chmod 0666 /sys/kernel/tracing/events/irq/tasklet_hi_entry/enable + chmod 0666 /sys/kernel/debug/tracing/events/irq/tasklet_hi_exit/enable + chmod 0666 /sys/kernel/tracing/events/irq/tasklet_hi_exit/enable + chmod 0666 /sys/kernel/debug/tracing/events/ipi/enable + chmod 0666 /sys/kernel/tracing/events/ipi/enable + chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_entry/enable + chmod 0666 /sys/kernel/tracing/events/ipi/ipi_entry/enable + chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_exit/enable + chmod 0666 /sys/kernel/tracing/events/ipi/ipi_exit/enable + chmod 0666 /sys/kernel/debug/tracing/events/ipi/ipi_raise/enable + chmod 0666 /sys/kernel/tracing/events/ipi/ipi_raise/enable # disk chmod 0666 /sys/kernel/tracing/events/f2fs/f2fs_get_data_block/enable diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index eefc84f0f1..a81cdafc61 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1350,7 +1350,8 @@ static Dumpstate::RunStatus RunDumpsysNormal() { static void DumpHals(int out_fd = STDOUT_FILENO) { if (!ds.IsZipping()) { RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"}, - CommandOptions::WithTimeout(60).AsRootIfAvailable().Build()); + CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(), + false, out_fd); return; } RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"}, diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp index 402767a426..64bfdf9289 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -37,10 +37,16 @@ cc_library { defaults: ["idlcli-defaults"], srcs: [ "CommandVibrator.cpp", + "vibrator/CommandAlwaysOnDisable.cpp", + "vibrator/CommandAlwaysOnEnable.cpp", "vibrator/CommandCompose.cpp", "vibrator/CommandGetCapabilities.cpp", "vibrator/CommandGetCompositionDelayMax.cpp", "vibrator/CommandGetCompositionSizeMax.cpp", + "vibrator/CommandGetPrimitiveDuration.cpp", + "vibrator/CommandGetSupportedAlwaysOnEffects.cpp", + "vibrator/CommandGetSupportedEffects.cpp", + "vibrator/CommandGetSupportedPrimitives.cpp", "vibrator/CommandOff.cpp", "vibrator/CommandOn.cpp", "vibrator/CommandPerform.cpp", diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h index a8e595470d..b8744555e2 100644 --- a/cmds/idlcli/utils.h +++ b/cmds/idlcli/utils.h @@ -17,6 +17,7 @@ #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ #define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ +#include <android/binder_enums.h> #include <hidl/HidlSupport.h> #include <iomanip> @@ -66,7 +67,7 @@ inline std::istream &operator>>(std::istream &stream, uint8_t &out) { } // namespace overrides -template <typename T, typename R = hardware::hidl_enum_range<T>> +template <typename T, typename R = ndk::enum_range<T>> inline std::istream &operator>>(std::istream &stream, T &out) { using overrides::operator>>; auto validRange = R(); diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h index ca5142dee9..6c30a9e2ca 100644 --- a/cmds/idlcli/vibrator.h +++ b/cmds/idlcli/vibrator.h @@ -16,8 +16,12 @@ #ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_ #define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_ +#include <future> + +#include <aidl/android/hardware/vibrator/BnVibratorCallback.h> #include <aidl/android/hardware/vibrator/IVibrator.h> #include <android/binder_manager.h> +#include <android/binder_process.h> #include <android/hardware/vibrator/1.3/IVibrator.h> #include "utils.h" @@ -101,6 +105,18 @@ namespace V1_2 = ::android::hardware::vibrator::V1_2; namespace V1_3 = ::android::hardware::vibrator::V1_3; namespace aidl = ::aidl::android::hardware::vibrator; +class VibratorCallback : public aidl::BnVibratorCallback { +public: + ndk::ScopedAStatus onComplete() override { + mPromise.set_value(); + return ndk::ScopedAStatus::ok(); + } + void waitForComplete() { mPromise.get_future().wait(); } + +private: + std::promise<void> mPromise; +}; + } // namespace vibrator } // namespace idlcli diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp new file mode 100644 index 0000000000..9afa300c2b --- /dev/null +++ b/cmds/idlcli/vibrator/CommandAlwaysOnDisable.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 CommandAlwaysOnDisable : public Command { + std::string getDescription() const override { return "Disarm always-on haptic source."; } + + std::string getUsageSummary() const override { return "<id>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<id>", {"Source ID (device-specific)."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto id = args.pop<decltype(mId)>()) { + mId = *id; + std::cout << "Source ID: " << mId << std::endl; + } else { + std::cerr << "Missing or Invalid Source ID!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::alwaysOnDisable, mId); + + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } + + int32_t mId; +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnDisable>("alwaysOnDisable"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp new file mode 100644 index 0000000000..bb7f9f284a --- /dev/null +++ b/cmds/idlcli/vibrator/CommandAlwaysOnEnable.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + +using aidl::Effect; +using aidl::EffectStrength; + +class CommandAlwaysOnEnable : public Command { + std::string getDescription() const override { + return "Arm always-on haptic source with an effect."; + } + + std::string getUsageSummary() const override { return "<id> <effect> <strength>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<id>", {"Source ID (device-specific)."}}, + {"<effect>", {"Effect ID."}}, + {"<strength>", {"0-2."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto id = args.pop<decltype(mId)>()) { + mId = *id; + std::cout << "Source ID: " << mId << std::endl; + } else { + std::cerr << "Missing or Invalid Source ID!" << std::endl; + return USAGE; + } + if (auto effect = args.pop<decltype(mEffect)>()) { + mEffect = *effect; + std::cout << "Effect: " << toString(mEffect) << std::endl; + } else { + std::cerr << "Missing or Invalid Effect!" << std::endl; + return USAGE; + } + if (auto strength = args.pop<decltype(mStrength)>()) { + mStrength = *strength; + std::cout << "Strength: " << toString(mStrength) << std::endl; + } else { + std::cerr << "Missing or Invalid Strength!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::alwaysOnEnable, mId, mEffect, mStrength); + + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } + + int32_t mId; + Effect mEffect; + EffectStrength mStrength; +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandAlwaysOnEnable>("alwaysOnEnable"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp index 4721a5f9ae..eb9008b68e 100644 --- a/cmds/idlcli/vibrator/CommandCompose.cpp +++ b/cmds/idlcli/vibrator/CommandCompose.cpp @@ -28,19 +28,33 @@ using aidl::CompositeEffect; class CommandCompose : public Command { std::string getDescription() const override { return "Compose vibration."; } - std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; } + std::string getUsageSummary() const override { + return "[options] <delay> <primitive> <scale> ..."; + } UsageDetails getUsageDetails() const override { UsageDetails details{ + {"-b", {"Block for duration of vibration."}}, {"<delay>", {"In milliseconds"}}, {"<primitive>", {"Primitive ID."}}, - {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}}, + {"<scale>", {"0.0 (inclusive) - 1.0 (inclusive)."}}, {"...", {"May repeat multiple times."}}, }; return details; } Status doArgs(Args &args) override { + while (args.get<std::string>().value_or("").find("-") == 0) { + auto opt = *args.pop<std::string>(); + if (opt == "--") { + break; + } else if (opt == "-b") { + mBlocking = true; + } else { + std::cerr << "Invalid Option '" << opt << "'!" << std::endl; + return USAGE; + } + } while (!args.empty()) { CompositeEffect effect; if (auto delay = args.pop<decltype(effect.delayMs)>()) { @@ -50,16 +64,15 @@ class CommandCompose : public Command { std::cerr << "Missing or Invalid Delay!" << std::endl; return USAGE; } - // TODO: Use range validation when supported by AIDL - if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) { - effect.primitive = static_cast<decltype(effect.primitive)>(*primitive); + if (auto primitive = args.pop<decltype(effect.primitive)>()) { + effect.primitive = *primitive; std::cout << "Primitive: " << toString(effect.primitive) << std::endl; } else { std::cerr << "Missing or Invalid Primitive!" << std::endl; return USAGE; } if (auto scale = args.pop<decltype(effect.scale)>(); - scale && *scale > 0.0 && scale <= 1.0) { + scale && *scale >= 0.0 && scale <= 1.0) { effect.scale = *scale; std::cout << "Scale: " << effect.scale << std::endl; } else { @@ -76,21 +89,33 @@ class CommandCompose : public Command { } Status doMain(Args && /*args*/) override { - std::string statusStr; - Status ret; - if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr); - statusStr = status.getDescription(); - ret = status.isOk() ? OK : ERROR; - } else { + auto hal = getHal<aidl::IVibrator>(); + + if (!hal) { return UNAVAILABLE; } - std::cout << "Status: " << statusStr << std::endl; + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + std::shared_ptr<VibratorCallback> callback; + + if (mBlocking) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); + } + + auto status = hal->call(&aidl::IVibrator::compose, mComposite, callback); + + if (status.isOk() && callback) { + callback->waitForComplete(); + } + + std::cout << "Status: " << status.getDescription() << std::endl; - return ret; + return status.isOk() ? OK : ERROR; } + bool mBlocking; std::vector<CompositeEffect> mComposite; }; diff --git a/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp new file mode 100644 index 0000000000..460d39e64f --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetPrimitiveDuration.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <future> + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +using aidl::CompositePrimitive; + +class CommandGetPrimitiveDuration : public Command { + std::string getDescription() const override { + return "Retrieve effect primitive's duration in milliseconds."; + } + + std::string getUsageSummary() const override { return "<primitive>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<primitive>", {"Primitive ID."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto primitive = args.pop<decltype(mPrimitive)>()) { + mPrimitive = *primitive; + std::cout << "Primitive: " << toString(mPrimitive) << std::endl; + } else { + std::cerr << "Missing or Invalid Primitive!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + int32_t duration; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getPrimitiveDuration, mPrimitive, &duration); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Duration: " << duration << std::endl; + + return ret; + } + + CompositePrimitive mPrimitive; +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetPrimitiveDuration>( + "getPrimitiveDuration"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp new file mode 100644 index 0000000000..edfcd9195a --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetSupportedAlwaysOnEffects.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + +using aidl::Effect; + +class CommandGetSupportedAlwaysOnEffects : public Command { + std::string getDescription() const override { return "List of supported always-on effects."; } + + 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; + std::vector<Effect> effects; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getSupportedAlwaysOnEffects, &effects); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Effects:" << std::endl; + for (auto &e : effects) { + std::cout << " " << toString(e) << std::endl; + } + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetSupportedAlwaysOnEffects>( + "getSupportedAlwaysOnEffects"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp new file mode 100644 index 0000000000..7658f22def --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetSupportedEffects.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + +using aidl::Effect; + +class CommandGetSupportedEffects : public Command { + std::string getDescription() const override { return "List supported effects."; } + + 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; + std::vector<Effect> effects; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getSupportedEffects, &effects); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Effects:" << std::endl; + for (auto &e : effects) { + std::cout << " " << toString(e) << std::endl; + } + + return ret; + } +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandGetSupportedEffects>( + "getSupportedEffects"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp new file mode 100644 index 0000000000..d101681914 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetSupportedPrimitives.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 { + +using aidl::CompositePrimitive; + +class CommandGetSupportedPrimitives : public Command { + std::string getDescription() const override { return "List of supported effect primitive."; } + + 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; + std::vector<CompositePrimitive> primitives; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getSupportedPrimitives, &primitives); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Primitives:" << std::endl; + for (auto &e : primitives) { + std::cout << " " << toString(e) << std::endl; + } + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetSupportedPrimitives>( + "getSupportedPrimitives"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp index 4e7e493d6d..8212fc14a7 100644 --- a/cmds/idlcli/vibrator/CommandOn.cpp +++ b/cmds/idlcli/vibrator/CommandOn.cpp @@ -13,9 +13,14 @@ * limitations under the License. */ +#include <thread> + #include "utils.h" #include "vibrator.h" +using std::chrono::milliseconds; +using std::this_thread::sleep_for; + namespace android { namespace idlcli { @@ -26,16 +31,28 @@ namespace vibrator { class CommandOn : public Command { std::string getDescription() const override { return "Turn on vibrator."; } - std::string getUsageSummary() const override { return "<duration>"; } + std::string getUsageSummary() const override { return "[options] <duration>"; } UsageDetails getUsageDetails() const override { UsageDetails details{ + {"-b", {"Block for duration of vibration."}}, {"<duration>", {"In milliseconds."}}, }; return details; } Status doArgs(Args &args) override { + while (args.get<std::string>().value_or("").find("-") == 0) { + auto opt = *args.pop<std::string>(); + if (opt == "--") { + break; + } else if (opt == "-b") { + mBlocking = true; + } else { + std::cerr << "Invalid Option '" << opt << "'!" << std::endl; + return USAGE; + } + } if (auto duration = args.pop<decltype(mDuration)>()) { mDuration = *duration; } else { @@ -52,9 +69,21 @@ class CommandOn : public Command { Status doMain(Args && /*args*/) override { std::string statusStr; Status ret; + std::shared_ptr<VibratorCallback> callback; if (auto hal = getHal<aidl::IVibrator>()) { - auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + int32_t cap; + hal->call(&aidl::IVibrator::getCapabilities, &cap); + + if (mBlocking && (cap & aidl::IVibrator::CAP_ON_CALLBACK)) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); + } + + auto status = hal->call(&aidl::IVibrator::on, mDuration, callback); + statusStr = status.getDescription(); ret = status.isOk() ? OK : ERROR; } else if (auto hal = getHal<V1_0::IVibrator>()) { @@ -65,11 +94,20 @@ class CommandOn : public Command { return UNAVAILABLE; } + if (ret == OK && mBlocking) { + if (callback) { + callback->waitForComplete(); + } else { + sleep_for(milliseconds(mDuration)); + } + } + std::cout << "Status: " << statusStr << std::endl; return ret; } + bool mBlocking; uint32_t mDuration; }; diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp index 69c7e37744..c897686cbe 100644 --- a/cmds/idlcli/vibrator/CommandPerform.cpp +++ b/cmds/idlcli/vibrator/CommandPerform.cpp @@ -13,9 +13,14 @@ * limitations under the License. */ +#include <thread> + #include "utils.h" #include "vibrator.h" +using std::chrono::milliseconds; +using std::this_thread::sleep_for; + namespace android { namespace idlcli { @@ -51,16 +56,17 @@ static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); -using V1_0::EffectStrength; -using V1_3::Effect; +using aidl::Effect; +using aidl::EffectStrength; class CommandPerform : public Command { std::string getDescription() const override { return "Perform vibration effect."; } - std::string getUsageSummary() const override { return "<effect> <strength>"; } + std::string getUsageSummary() const override { return "[options] <effect> <strength>"; } UsageDetails getUsageDetails() const override { UsageDetails details{ + {"-b", {"Block for duration of vibration."}}, {"<effect>", {"Effect ID."}}, {"<strength>", {"0-2."}}, }; @@ -68,6 +74,17 @@ class CommandPerform : public Command { } Status doArgs(Args &args) override { + while (args.get<std::string>().value_or("").find("-") == 0) { + auto opt = *args.pop<std::string>(); + if (opt == "--") { + break; + } else if (opt == "-b") { + mBlocking = true; + } else { + std::cerr << "Invalid Option '" << opt << "'!" << std::endl; + return USAGE; + } + } if (auto effect = args.pop<decltype(mEffect)>()) { mEffect = *effect; std::cout << "Effect: " << toString(mEffect) << std::endl; @@ -93,12 +110,23 @@ class CommandPerform : public Command { std::string statusStr; uint32_t lengthMs; Status ret; + std::shared_ptr<VibratorCallback> callback; if (auto hal = getHal<aidl::IVibrator>()) { + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + int32_t cap; + hal->call(&aidl::IVibrator::getCapabilities, &cap); + + if (mBlocking && (cap & aidl::IVibrator::CAP_PERFORM_CALLBACK)) { + callback = ndk::SharedRefBase::make<VibratorCallback>(); + } + int32_t aidlLengthMs; - auto status = - hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect), - static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs); + auto status = hal->call(&aidl::IVibrator::perform, mEffect, mStrength, callback, + &aidlLengthMs); + statusStr = status.getDescription(); lengthMs = static_cast<uint32_t>(aidlLengthMs); ret = status.isOk() ? OK : ERROR; @@ -111,17 +139,20 @@ class CommandPerform : public Command { }; if (auto hal = getHal<V1_3::IVibrator>()) { - hidlRet = hal->call(&V1_3::IVibrator::perform_1_3, - static_cast<V1_3::Effect>(mEffect), mStrength, callback); + hidlRet = + hal->call(&V1_3::IVibrator::perform_1_3, static_cast<V1_3::Effect>(mEffect), + static_cast<V1_0::EffectStrength>(mStrength), callback); } else if (auto hal = getHal<V1_2::IVibrator>()) { - hidlRet = hal->call(&V1_2::IVibrator::perform_1_2, - static_cast<V1_2::Effect>(mEffect), mStrength, callback); + hidlRet = + hal->call(&V1_2::IVibrator::perform_1_2, static_cast<V1_2::Effect>(mEffect), + static_cast<V1_0::EffectStrength>(mStrength), callback); } else if (auto hal = getHal<V1_1::IVibrator>()) { hidlRet = hal->call(&V1_1::IVibrator::perform_1_1, - static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback); + static_cast<V1_1::Effect_1_1>(mEffect), + static_cast<V1_0::EffectStrength>(mStrength), callback); } else if (auto hal = getHal<V1_0::IVibrator>()) { hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect), - mStrength, callback); + static_cast<V1_0::EffectStrength>(mStrength), callback); } else { return UNAVAILABLE; } @@ -130,12 +161,21 @@ class CommandPerform : public Command { ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR; } + if (ret == OK && mBlocking) { + if (callback) { + callback->waitForComplete(); + } else { + sleep_for(milliseconds(lengthMs)); + } + } + std::cout << "Status: " << statusStr << std::endl; std::cout << "Length: " << lengthMs << std::endl; return ret; } + bool mBlocking; Effect mEffect; EffectStrength mStrength; }; diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 523115f476..96875d5345 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -185,8 +185,7 @@ cc_binary { filegroup { name: "installd_aidl", srcs: [ - "binder/android/os/IInstalld.aidl", - "binder/android/os/storage/CrateMetadata.aidl", + "binder/**/*.aidl", ], path: "binder", } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 6001a587a5..b821578dd3 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -420,33 +420,6 @@ static bool prepare_app_profile_dir(const std::string& packageName, int32_t appI return true; } -binder::Status InstalldNativeService::createAppDataBatched( - const std::optional<std::vector<std::optional<std::string>>>& uuids, - const std::optional<std::vector<std::optional<std::string>>>& packageNames, - int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, - const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions, - int64_t* _aidl_return) { - ENFORCE_UID(AID_SYSTEM); - std::lock_guard<std::recursive_mutex> lock(mLock); - - ATRACE_BEGIN("createAppDataBatched"); - binder::Status ret; - for (size_t i = 0; i < uuids->size(); i++) { - std::optional<std::string> packageName = packageNames->at(i); - if (!packageName) { - continue; - } - ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i], - seInfos[i], targetSdkVersions[i], _aidl_return); - if (!ret.isOk()) { - ATRACE_END(); - return ret; - } - } - ATRACE_END(); - return ok(); -} - binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { @@ -528,6 +501,38 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str return ok(); } + +binder::Status InstalldNativeService::createAppData( + const android::os::CreateAppDataArgs& args, + android::os::CreateAppDataResult* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + std::lock_guard<std::recursive_mutex> lock(mLock); + + int64_t ceDataInode = -1; + auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId, + args.seInfo, args.targetSdkVersion, &ceDataInode); + _aidl_return->ceDataInode = ceDataInode; + _aidl_return->exceptionCode = status.exceptionCode(); + _aidl_return->exceptionMessage = status.exceptionMessage(); + return ok(); +} + +binder::Status InstalldNativeService::createAppDataBatched( + const std::vector<android::os::CreateAppDataArgs>& args, + std::vector<android::os::CreateAppDataResult>* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + std::lock_guard<std::recursive_mutex> lock(mLock); + + std::vector<android::os::CreateAppDataResult> results; + for (auto arg : args) { + android::os::CreateAppDataResult result; + createAppData(arg, &result); + results.push_back(result); + } + *_aidl_return = results; + return ok(); +} + binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); @@ -2198,9 +2203,6 @@ binder::Status InstalldNativeService::getExternalSize(const std::optional<std::s auto obbPath = StringPrintf("%s/Android/obb", create_data_media_path(uuid_, userId).c_str()); calculate_tree_size(obbPath, &obbSize); - if (!(flags & FLAG_USE_QUOTA)) { - totalSize -= obbSize; - } ATRACE_END(); } @@ -2252,7 +2254,7 @@ binder::Status InstalldNativeService::getAppCrates( #if CRATE_DEBUG LOG(WARNING) << "retVector.size() =" << retVector.size(); for (auto& item : retVector) { - CrateManager::dump(item); + CrateManager::dump(*item); } #endif @@ -2284,7 +2286,7 @@ binder::Status InstalldNativeService::getUserCrates( if (cratedFolder == nullptr) { return; } - retVector->push_back(std::move(crateMetadata)); + retVector.push_back(std::move(crateMetadata)); }; std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void { @@ -2296,7 +2298,7 @@ binder::Status InstalldNativeService::getUserCrates( #if CRATE_DEBUG LOG(DEBUG) << "retVector.size() =" << retVector.size(); for (auto& item : retVector) { - CrateManager::dump(item); + CrateManager::dump(*item); } #endif diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 9819327840..4966b96809 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -44,15 +44,18 @@ public: int32_t userSerial, int32_t flags); binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId, int32_t flags); - binder::Status createAppDataBatched( - const std::optional<std::vector<std::optional<std::string>>>& uuids, - const std::optional<std::vector<std::optional<std::string>>>& packageNames, - int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, - const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions, - int64_t* _aidl_return); + binder::Status createAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); + + binder::Status createAppData( + const android::os::CreateAppDataArgs& args, + android::os::CreateAppDataResult* _aidl_return); + binder::Status createAppDataBatched( + const std::vector<android::os::CreateAppDataArgs>& args, + std::vector<android::os::CreateAppDataResult>* _aidl_return); + binder::Status restoreconAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo); diff --git a/libs/ui/include/ui/UiConfig.h b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl index d1d6014a7b..96d7faaaa2 100644 --- a/libs/ui/include/ui/UiConfig.h +++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,15 @@ * limitations under the License. */ -#ifndef ANDROID_UI_CONFIG_H -#define ANDROID_UI_CONFIG_H +package android.os; -#include <string> - -namespace android { - -// Append the libui configuration details to configStr. -void appendUiConfigString(std::string& configStr); - -}; // namespace android - -#endif /*ANDROID_UI_CONFIG_H*/ +/** {@hide} */ +parcelable CreateAppDataArgs { + @nullable @utf8InCpp String uuid; + @utf8InCpp String packageName; + int userId; + int flags; + int appId; + @utf8InCpp String seInfo; + int targetSdkVersion; +} diff --git a/libs/gui/include/gui/GuiConfig.h b/cmds/installd/binder/android/os/CreateAppDataResult.aidl index 7aa54321fd..3b8fa6b9f2 100644 --- a/libs/gui/include/gui/GuiConfig.h +++ b/cmds/installd/binder/android/os/CreateAppDataResult.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,11 @@ * limitations under the License. */ -#ifndef ANDROID_GUI_CONFIG_H -#define ANDROID_GUI_CONFIG_H +package android.os; -#include <string> - -namespace android { - -// Append the libgui configuration details to configStr. -void appendGuiConfigString(std::string& configStr); - -}; // namespace android - -#endif /*ANDROID_GUI_CONFIG_H*/ +/** {@hide} */ +parcelable CreateAppDataResult { + long ceDataInode; + int exceptionCode; + @utf8InCpp String exceptionMessage; +} diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 4ac70a4857..2538e22b3d 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -21,11 +21,9 @@ interface IInstalld { void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags); void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags); - long createAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, - int userId, int flags, int appId, in @utf8InCpp String seInfo, int targetSdkVersion); - long createAppDataBatched(in @nullable @utf8InCpp String[] uuids, - in @nullable @utf8InCpp String[] packageNames, in int userId, int flags, in int[] appIds, - in @utf8InCpp String[] seInfos, in int[] targetSdkVersions); + android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args); + android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args); + void restoreconAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags, int appId, @utf8InCpp String seInfo); void migrateAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, @@ -117,10 +115,11 @@ interface IInstalld { int userId, int snapshotId, int storageFlags); void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags); - void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId, - in int[] retainSnapshotIds); void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, long ceSnapshotInode, int snapshotId, int storageFlags); + void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId, + in int[] retainSnapshotIds); + void tryMountDataMirror(@nullable @utf8InCpp String volumeUuid); void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid); diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto index b57409867f..c6f656ba0c 100644 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ b/cmds/surfacereplayer/proto/src/trace.proto @@ -25,8 +25,10 @@ message Transaction { repeated SurfaceChange surface_change = 1; repeated DisplayChange display_change = 2; - required bool synchronous = 3; - required bool animation = 4; + required bool synchronous = 3; + required bool animation = 4; + optional Origin origin = 5; + optional uint64 id = 6; } message SurfaceChange { @@ -39,7 +41,6 @@ message SurfaceChange { LayerChange layer = 5; CropChange crop = 6; MatrixChange matrix = 8; - OverrideScalingModeChange override_scaling_mode = 9; TransparentRegionHintChange transparent_region_hint = 10; LayerStackChange layer_stack = 11; HiddenFlagChange hidden_flag = 12; @@ -53,6 +54,7 @@ message SurfaceChange { ReparentChildrenChange reparent_children = 20; BackgroundBlurRadiusChange background_blur_radius = 21; ShadowRadiusChange shadow_radius = 22; + BlurRegionsChange blur_regions = 23; } } @@ -93,10 +95,6 @@ message MatrixChange { required float dtdy = 4; } -message OverrideScalingModeChange { - required int32 override_scaling_mode = 1; -} - message TransparentRegionHintChange { repeated Rectangle region = 1; } @@ -208,4 +206,26 @@ message DetachChildrenChange { message ShadowRadiusChange { required float radius = 1; +} + +message BlurRegionsChange { + repeated BlurRegionChange blur_regions = 1; +} + +message BlurRegionChange { + required uint32 blur_radius = 1; + required float corner_radius_tl = 2; + required float corner_radius_tr = 3; + required float corner_radius_bl = 4; + required float corner_radius_br = 5; + required float alpha = 6; + required int32 left = 7; + required int32 top = 8; + required int32 right = 9; + required int32 bottom = 10; +} + +message Origin { + required int32 pid = 1; + required int32 uid = 2; }
\ No newline at end of file diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 2b5667d8fe..5849212265 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -387,10 +387,6 @@ status_t Replayer::doSurfaceTransaction( case SurfaceChange::SurfaceChangeCase::kMatrix: setMatrix(transaction, change.id(), change.matrix()); break; - case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode: - setOverrideScalingMode(transaction, change.id(), - change.override_scaling_mode()); - break; case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint: setTransparentRegionHint(transaction, change.id(), change.transparent_region_hint()); @@ -427,6 +423,9 @@ status_t Replayer::doSurfaceTransaction( case SurfaceChange::SurfaceChangeCase::kShadowRadius: setShadowRadiusChange(transaction, change.id(), change.shadow_radius()); break; + case SurfaceChange::SurfaceChangeCase::kBlurRegions: + setBlurRegionsChange(transaction, change.id(), change.blur_regions()); + break; default: status = 1; break; @@ -525,12 +524,6 @@ void Replayer::setMatrix(SurfaceComposerClient::Transaction& t, t.setMatrix(mLayers[id], mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy()); } -void Replayer::setOverrideScalingMode(SurfaceComposerClient::Transaction& t, - layer_id id, const OverrideScalingModeChange& osmc) { - ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode()); - t.setOverrideScalingMode(mLayers[id], osmc.override_scaling_mode()); -} - void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t, layer_id id, const TransparentRegionHintChange& trhc) { ALOGV("Setting Transparent Region Hint"); @@ -584,9 +577,7 @@ void Replayer::setDeferredTransaction(SurfaceComposerClient::Transaction& t, return; } - auto handle = mLayers[dtc.layer_id()]->getHandle(); - - t.deferTransactionUntil_legacy(mLayers[id], handle, dtc.frame_number()); + t.deferTransactionUntil_legacy(mLayers[id], mLayers[dtc.layer_id()], dtc.frame_number()); } void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t, @@ -706,11 +697,11 @@ status_t Replayer::loadSurfaceComposerClient() { void Replayer::setReparentChange(SurfaceComposerClient::Transaction& t, layer_id id, const ReparentChange& c) { - sp<IBinder> newParentHandle = nullptr; + sp<SurfaceControl> newSurfaceControl = nullptr; if (mLayers.count(c.parent_id()) != 0 && mLayers[c.parent_id()] != nullptr) { - newParentHandle = mLayers[c.parent_id()]->getHandle(); + newSurfaceControl = mLayers[c.parent_id()]; } - t.reparent(mLayers[id], newParentHandle); + t.reparent(mLayers[id], newSurfaceControl); } void Replayer::setRelativeParentChange(SurfaceComposerClient::Transaction& t, @@ -719,7 +710,7 @@ void Replayer::setRelativeParentChange(SurfaceComposerClient::Transaction& t, ALOGE("Layer %d not found in set relative parent transaction", c.relative_parent_id()); return; } - t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()]->getHandle(), c.z()); + t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()], c.z()); } void Replayer::setDetachChildrenChange(SurfaceComposerClient::Transaction& t, @@ -733,10 +724,31 @@ void Replayer::setReparentChildrenChange(SurfaceComposerClient::Transaction& t, ALOGE("Layer %d not found in reparent children transaction", c.parent_id()); return; } - t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle()); + t.reparentChildren(mLayers[id], mLayers[c.parent_id()]); } void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t, layer_id id, const ShadowRadiusChange& c) { t.setShadowRadius(mLayers[id], c.radius()); } + +void Replayer::setBlurRegionsChange(SurfaceComposerClient::Transaction& t, + layer_id id, const BlurRegionsChange& c) { + std::vector<BlurRegion> regions; + for(size_t i=0; i < c.blur_regions_size(); i++) { + auto protoRegion = c.blur_regions(i); + regions.push_back(BlurRegion{ + .blurRadius = protoRegion.blur_radius(), + .alpha = protoRegion.alpha(), + .cornerRadiusTL = protoRegion.corner_radius_tl(), + .cornerRadiusTR = protoRegion.corner_radius_tr(), + .cornerRadiusBL = protoRegion.corner_radius_bl(), + .cornerRadiusBR = protoRegion.corner_radius_br(), + .left = protoRegion.left(), + .top = protoRegion.top(), + .right = protoRegion.right(), + .bottom = protoRegion.bottom() + }); + } + t.setBlurRegions(mLayers[id], regions); +} diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h index 95857e1e5b..a22262a24e 100644 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ b/cmds/surfacereplayer/replayer/Replayer.h @@ -96,10 +96,10 @@ class Replayer { layer_id id, const CornerRadiusChange& cc); void setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t, layer_id id, const BackgroundBlurRadiusChange& cc); + void setBlurRegions(SurfaceComposerClient::Transaction& t, + layer_id id, const BlurRegionsChange& cc); void setMatrix(SurfaceComposerClient::Transaction& t, layer_id id, const MatrixChange& mc); - void setOverrideScalingMode(SurfaceComposerClient::Transaction& t, - layer_id id, const OverrideScalingModeChange& osmc); void setTransparentRegionHint(SurfaceComposerClient::Transaction& t, layer_id id, const TransparentRegionHintChange& trgc); void setLayerStack(SurfaceComposerClient::Transaction& t, @@ -122,6 +122,8 @@ class Replayer { layer_id id, const ReparentChildrenChange& c); void setShadowRadiusChange(SurfaceComposerClient::Transaction& t, layer_id id, const ShadowRadiusChange& c); + void setBlurRegionsChange(SurfaceComposerClient::Transaction& t, + layer_id id, const BlurRegionsChange& c); void setDisplaySurface(SurfaceComposerClient::Transaction& t, display_id id, const DispSurfaceChange& dsc); diff --git a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py index d63d97f6b4..58bfbf3c43 100644 --- a/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py +++ b/cmds/surfacereplayer/replayer/trace_creator/trace_creator.py @@ -69,7 +69,6 @@ def transaction_menu(): print ("5. Crop Change") print ("6. Final Crop Change") print ("7. Matrix Change") - print ("8. Override Scaling Mode Change") print ("9. Transparent Region Hint Change") print ("10. Layer Stack Change") print ("11. Hidden Flag Change") @@ -128,9 +127,6 @@ def transaction(increment): change.matrix.dtdx,\ change.matrix.dsdy,\ change.matrix.dtdy = layer() - elif option == 8: - change.override_scaling_mode.override_scaling_mode \ - = override_scaling_mode() elif option == 9: for rect in transparent_region_hint(): new = increment.transparent_region_hint.region.add() @@ -227,11 +223,6 @@ def matrix(): return float(dsdx) -def override_scaling_mode(): - mode = input("Enter override scaling mode: ") - - return int(mode) - def transparent_region_hint(): num = input("Enter number of rectangles in region: ") diff --git a/data/etc/Android.bp b/data/etc/Android.bp new file mode 100644 index 0000000000..83b6aa020c --- /dev/null +++ b/data/etc/Android.bp @@ -0,0 +1,7 @@ +prebuilt_etc { + name: "android.hardware.biometrics.face.xml", + product_specific: true, + sub_dir: "permissions", + src: "android.hardware.biometrics.face.xml", + filename_from_src: true, +}
\ No newline at end of file diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 6ffb94721d..ccf4dc8f7f 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -43,6 +43,7 @@ <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> <feature name="android.software.secure_lock_screen" /> + <feature name="android.software.input_methods" /> <!-- devices with GPS must include android.hardware.location.gps.xml --> <!-- devices with an autofocus camera and/or flash must include either diff --git a/data/etc/cec_config.xml b/data/etc/cec_config.xml new file mode 100644 index 0000000000..8e78ad738e --- /dev/null +++ b/data/etc/cec_config.xml @@ -0,0 +1,40 @@ +<?xml version='1.0' encoding='utf-8' standalone='yes' ?> +<cec-settings> + <setting name="hdmi_cec_enabled" + value-type="int" + user-configurable="true"> + <allowed-values> + <value int-value="0" /> + <value int-value="1" /> + </allowed-values> + <default-value int-value="1" /> + </setting> + <setting name="send_standby_on_sleep" + value-type="string" + user-configurable="true"> + <allowed-values> + <value string-value="to_tv" /> + <value string-value="broadcast" /> + <value string-value="none" /> + </allowed-values> + <default-value string-value="to_tv" /> + </setting> + <setting name="power_state_change_on_active_source_lost" + value-type="string" + user-configurable="false"> + <allowed-values> + <value string-value="none" /> + <value string-value="standby_now" /> + </allowed-values> + <default-value string-value="none" /> + </setting> + <setting name="system_audio_mode_muting" + value-type="int" + user-configurable="false"> + <allowed-values> + <value int-value="0" /> + <value int-value="1" /> + </allowed-values> + <default-value int-value="1" /> + </setting> +</cec-settings> diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index d7e6e4118f..3dd1534f1d 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -158,7 +158,8 @@ typedef struct AImageDecoder AImageDecoder; * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not * supported. */ -int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDecoder) +int AImageDecoder_createFromAAsset(struct AAsset* _Nonnull asset, + AImageDecoder* _Nonnull * _Nonnull outDecoder) __INTRODUCED_IN(30); /** @@ -189,7 +190,8 @@ int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDeco * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not * supported. */ -int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_IN(30); +int AImageDecoder_createFromFd(int fd, AImageDecoder* _Nonnull * _Nonnull outDecoder) + __INTRODUCED_IN(30); /** * Create a new AImageDecoder from a buffer. @@ -218,15 +220,16 @@ int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_ * - {@link ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT}: The format is not * supported. */ -int AImageDecoder_createFromBuffer(const void* buffer, size_t length, - AImageDecoder** outDecoder) __INTRODUCED_IN(30); +int AImageDecoder_createFromBuffer(const void* _Nonnull buffer, size_t length, + AImageDecoder* _Nonnull * _Nonnull outDecoder) + __INTRODUCED_IN(30); /** * Delete the AImageDecoder. * * Available since API level 30. */ -void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); +void AImageDecoder_delete(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30); /** * Choose the desired output format. @@ -247,7 +250,7 @@ void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); * - {@link ANDROID_IMAGE_DECODER_INVALID_CONVERSION}: The * {@link AndroidBitmapFormat} is incompatible with the image. */ -int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, +int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* _Nonnull decoder, int32_t format) __INTRODUCED_IN(30); /** @@ -270,7 +273,7 @@ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The * {@link AImageDecoder} is null. */ -int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, +int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* _Nonnull decoder, bool unpremultipliedRequired) __INTRODUCED_IN(30); /** @@ -295,7 +298,8 @@ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, * {@link AImageDecoder} is null or |dataspace| does not correspond to an * {@link ADataSpace} value. */ -int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_IN(30); +int AImageDecoder_setDataSpace(AImageDecoder* _Nonnull decoder, int32_t dataspace) + __INTRODUCED_IN(30); /** * Specify the output size for a decoded image. @@ -324,7 +328,8 @@ int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_I * or the scale is incompatible with a previous call to * {@link AImageDecoder_setUnpremultipliedRequired}(true). */ -int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) __INTRODUCED_IN(30); +int AImageDecoder_setTargetSize(AImageDecoder* _Nonnull decoder, int32_t width, + int32_t height) __INTRODUCED_IN(30); /** @@ -353,8 +358,9 @@ int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) _ * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The * {@link AImageDecoder}, |width| or |height| is null or |sampleSize| is < 1. */ -int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, - int32_t* width, int32_t* height) __INTRODUCED_IN(30); +int AImageDecoder_computeSampledSize(const AImageDecoder* _Nonnull decoder, int sampleSize, + int32_t* _Nonnull width, int32_t* _Nonnull height) + __INTRODUCED_IN(30); /** * Specify how to crop the output after scaling (if any). * @@ -380,7 +386,7 @@ int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, * {@link AImageDecoder} is null or the crop is not contained by the * (possibly scaled) image dimensions. */ -int AImageDecoder_setCrop(AImageDecoder*, ARect crop) __INTRODUCED_IN(30); +int AImageDecoder_setCrop(AImageDecoder* _Nonnull decoder, ARect crop) __INTRODUCED_IN(30); struct AImageDecoderHeaderInfo; /** @@ -399,8 +405,8 @@ typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo; * * Available since API level 30. */ -const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( - const AImageDecoder*) __INTRODUCED_IN(30); +const AImageDecoderHeaderInfo* _Nonnull AImageDecoder_getHeaderInfo( + const AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30); /** * Report the native width of the encoded image. This is also the logical @@ -410,7 +416,8 @@ const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( * * Available since API level 30. */ -int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); +int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo* _Nonnull) + __INTRODUCED_IN(30); /** * Report the native height of the encoded image. This is also the logical @@ -420,7 +427,8 @@ int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRO * * Available since API level 30. */ -int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); +int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* _Nonnull) + __INTRODUCED_IN(30); /** * Report the mimeType of the encoded image. @@ -429,8 +437,8 @@ int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTR * * @return a string literal describing the mime type. */ -const char* AImageDecoderHeaderInfo_getMimeType( - const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); +const char* _Nonnull AImageDecoderHeaderInfo_getMimeType( + const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30); /** * Report the {@link AndroidBitmapFormat} the AImageDecoder will decode to @@ -441,7 +449,7 @@ const char* AImageDecoderHeaderInfo_getMimeType( * Available since API level 30. */ int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat( - const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30); /** * Report how the {@link AImageDecoder} will handle alpha by default. If the image @@ -453,7 +461,7 @@ int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat( * Available since API level 30. */ int AImageDecoderHeaderInfo_getAlphaFlags( - const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30); /** * Report the dataspace the AImageDecoder will decode to by default. @@ -474,7 +482,7 @@ int AImageDecoderHeaderInfo_getAlphaFlags( * no corresponding {@link ADataSpace}. */ int32_t AImageDecoderHeaderInfo_getDataSpace( - const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); + const AImageDecoderHeaderInfo* _Nonnull) __INTRODUCED_IN(30); /** * Return the minimum stride that can be used in @@ -489,13 +497,17 @@ int32_t AImageDecoderHeaderInfo_getDataSpace( * * Available since API level 30. */ -size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30); +size_t AImageDecoder_getMinimumStride(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(30); /** * Decode the image into pixels, using the settings of the {@link AImageDecoder}. * * Available since API level 30. * + * Starting in API level 31, it can be used to decode all of the frames of an + * animated image (i.e. GIF, WebP, HEIF) using new APIs (TODO (scroggo): list + * and describe here). + * * @param decoder Opaque object representing the decoder. * @param pixels On success, will be filled with the result * of the decode. Must be large enough to hold |size| bytes. @@ -523,12 +535,64 @@ size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30); * - {@link ANDROID_IMAGE_DECODER_INTERNAL_ERROR}: Some other error, like a * failure to allocate memory. */ -int AImageDecoder_decodeImage(AImageDecoder* decoder, - void* pixels, size_t stride, +int AImageDecoder_decodeImage(AImageDecoder* _Nonnull decoder, + void* _Nonnull pixels, size_t stride, size_t size) __INTRODUCED_IN(30); #endif // __ANDROID_API__ >= 30 +#if __ANDROID_API__ >= 31 + +/** + * Return true iff the image is animated - i.e. has multiple frames. + * + * Introduced in API 31. + * + * This may require seeking past the first frame to verify whether + * there is a following frame (e.g. for GIF). + * + * Errors: + * - returns false if |decoder| is null. + */ +bool AImageDecoder_isAnimated(AImageDecoder* _Nonnull decoder) + __INTRODUCED_IN(31); + +enum { + /* + * Reported by {@link AImageDecoder_getRepeatCount} if the + * animation should repeat forever. + */ + ANDROID_IMAGE_DECODER_INFINITE = INT32_MAX, +}; + +/** + * Report how many times the animation should repeat. + * + * Introduced in API 31. + * + * This does not include the first play through. e.g. a repeat + * count of 4 means that each frame is played 5 times. + * + * {@link ANDROID_IMAGE_DECODER_INFINITE} means to repeat forever. + * + * This may require seeking. + * + * For non-animated formats, this returns 0. It may return non-zero for + * an image with only one frame (i.e. {@link AImageDecoder_isAnimated} returns + * false) if the encoded image contains a repeat count. + * + * @return Number of times to repeat on success or a value + * indicating the reason for the failure. + * + * Errors: + * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder + * is null. + */ +int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder); + __INTRODUCED_IN(31); + +#endif // __ANDROID_API__ >= 31 + #ifdef __cplusplus } #endif diff --git a/include/android/input.h b/include/android/input.h index dbfd61eb05..b04775b5c3 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -55,6 +55,7 @@ #include <sys/types.h> #include <android/keycodes.h> #include <android/looper.h> +#include <jni.h> #if !defined(__INTRODUCED_IN) #define __INTRODUCED_IN(__api_level) /* nothing */ @@ -931,6 +932,15 @@ int32_t AInputEvent_getDeviceId(const AInputEvent* event); /** Get the input event source. */ int32_t AInputEvent_getSource(const AInputEvent* event); +/** + * Releases interface objects created by {@link AKeyEvent_fromJava()} + * and {@link AMotionEvent_fromJava()}. + * After returning, the specified AInputEvent* object becomes invalid and should no longer be used. + * The underlying Java object remains valid and does not change its state. + */ + +void AInputEvent_release(const AInputEvent* event); + /*** Accessors for key events only. ***/ /** Get the key event action. */ @@ -977,6 +987,13 @@ int64_t AKeyEvent_getDownTime(const AInputEvent* key_event); */ int64_t AKeyEvent_getEventTime(const AInputEvent* key_event); +/** + * Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent. + * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object + * returned by this function must be disposed using {@link AInputEvent_release()}. + */ +const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent); + /*** Accessors for motion events only. ***/ /** Get the combined motion event action code and pointer index. */ @@ -1292,6 +1309,13 @@ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, siz float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, int32_t axis, size_t pointer_index, size_t history_index); +/** + * Creates a native AInputEvent* object that is a copy of the specified Java + * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific + * AInputEvent_* functions. The object returned by this function must be disposed using + * {@link AInputEvent_release()}. + */ +const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent); struct AInputQueue; /** diff --git a/include/attestation/HmacKeyManager.h b/include/attestation/HmacKeyManager.h new file mode 100644 index 0000000000..571a361889 --- /dev/null +++ b/include/attestation/HmacKeyManager.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <array> + +namespace android { +/** + * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified. + */ +constexpr std::array<uint8_t, 32> INVALID_HMAC = {0}; + +class HmacKeyManager { +public: + HmacKeyManager(); + std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const; +private: + const std::array<uint8_t, 128> mHmacKey; +}; +} // namespace android
\ No newline at end of file diff --git a/include/ftl/ArrayTraits.h b/include/ftl/ArrayTraits.h new file mode 100644 index 0000000000..28f717ad75 --- /dev/null +++ b/include/ftl/ArrayTraits.h @@ -0,0 +1,129 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <algorithm> +#include <iterator> +#include <new> + +#define FTL_ARRAY_TRAIT(T, U) using U = typename ArrayTraits<T>::U + +namespace android::ftl { + +template <typename T> +struct ArrayTraits { + using value_type = T; + using size_type = size_t; + using difference_type = ptrdiff_t; + + using pointer = value_type*; + using reference = value_type&; + using iterator = pointer; + using reverse_iterator = std::reverse_iterator<iterator>; + + using const_pointer = const value_type*; + using const_reference = const value_type&; + using const_iterator = const_pointer; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + + // TODO: Replace with std::construct_at in C++20. + template <typename... Args> + static pointer construct_at(const_iterator it, Args&&... args) { + void* const ptr = const_cast<void*>(static_cast<const void*>(it)); + return new (ptr) value_type{std::forward<Args>(args)...}; + } +}; + +// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end. +template <typename Self, typename T> +class ArrayIterators { + FTL_ARRAY_TRAIT(T, size_type); + + FTL_ARRAY_TRAIT(T, reference); + FTL_ARRAY_TRAIT(T, iterator); + FTL_ARRAY_TRAIT(T, reverse_iterator); + + FTL_ARRAY_TRAIT(T, const_reference); + FTL_ARRAY_TRAIT(T, const_iterator); + FTL_ARRAY_TRAIT(T, const_reverse_iterator); + + Self& self() const { return *const_cast<Self*>(static_cast<const Self*>(this)); } + +public: + const_iterator begin() const { return cbegin(); } + const_iterator cbegin() const { return self().begin(); } + + const_iterator end() const { return cend(); } + const_iterator cend() const { return self().end(); } + + reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); } + const_reverse_iterator rbegin() const { return crbegin(); } + const_reverse_iterator crbegin() const { return self().rbegin(); } + + reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); } + const_reverse_iterator rend() const { return crend(); } + const_reverse_iterator crend() const { return self().rend(); } + + iterator last() { return self().end() - 1; } + const_iterator last() const { return self().last(); } + + reference front() { return *self().begin(); } + const_reference front() const { return self().front(); } + + reference back() { return *last(); } + const_reference back() const { return self().back(); } + + reference operator[](size_type i) { return *(self().begin() + i); } + const_reference operator[](size_type i) const { return self()[i]; } +}; + +// Mixin to define comparison operators for an array-like template. +// TODO: Replace with operator<=> in C++20. +template <template <typename, size_t> class Array> +struct ArrayComparators { + template <typename T, size_t N, size_t M> + friend bool operator==(const Array<T, N>& lhs, const Array<T, M>& rhs) { + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); + } + + template <typename T, size_t N, size_t M> + friend bool operator<(const Array<T, N>& lhs, const Array<T, M>& rhs) { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + template <typename T, size_t N, size_t M> + friend bool operator>(const Array<T, N>& lhs, const Array<T, M>& rhs) { + return rhs < lhs; + } + + template <typename T, size_t N, size_t M> + friend bool operator!=(const Array<T, N>& lhs, const Array<T, M>& rhs) { + return !(lhs == rhs); + } + + template <typename T, size_t N, size_t M> + friend bool operator>=(const Array<T, N>& lhs, const Array<T, M>& rhs) { + return !(lhs < rhs); + } + + template <typename T, size_t N, size_t M> + friend bool operator<=(const Array<T, N>& lhs, const Array<T, M>& rhs) { + return !(lhs > rhs); + } +}; + +} // namespace android::ftl diff --git a/include/ftl/SmallVector.h b/include/ftl/SmallVector.h new file mode 100644 index 0000000000..cecec7f873 --- /dev/null +++ b/include/ftl/SmallVector.h @@ -0,0 +1,380 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ftl/ArrayTraits.h> +#include <ftl/StaticVector.h> + +#include <algorithm> +#include <iterator> +#include <type_traits> +#include <utility> +#include <variant> +#include <vector> + +namespace android::ftl { + +template <typename> +struct IsSmallVector; + +// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement +// for std::vector with statically allocated storage for N elements, whose goal is to improve run +// time by avoiding heap allocation and increasing probability of cache hits. The standard API is +// augmented by an unstable_erase operation that does not preserve order, and a replace operation +// that destructively emplaces. +// +// SmallVector<T, 0> is a specialization that thinly wraps std::vector. +// +// Example usage: +// +// ftl::SmallVector<char, 3> vector; +// assert(vector.empty()); +// assert(!vector.dynamic()); +// +// vector = {'a', 'b', 'c'}; +// assert(vector.size() == 3u); +// assert(!vector.dynamic()); +// +// vector.push_back('d'); +// assert(vector.dynamic()); +// +// vector.unstable_erase(vector.begin()); +// assert(vector == (ftl::SmallVector{'d', 'b', 'c'})); +// +// vector.pop_back(); +// assert(vector.back() == 'b'); +// assert(vector.dynamic()); +// +// const char array[] = "hi"; +// vector = ftl::SmallVector(array); +// assert(vector == (ftl::SmallVector{'h', 'i', '\0'})); +// assert(!vector.dynamic()); +// +template <typename T, size_t N> +class SmallVector final : ArrayTraits<T>, ArrayComparators<SmallVector> { + using Static = StaticVector<T, N>; + using Dynamic = SmallVector<T, 0>; + + // TODO: Replace with std::remove_cvref_t in C++20. + template <typename U> + using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<U>>; + +public: + FTL_ARRAY_TRAIT(T, value_type); + FTL_ARRAY_TRAIT(T, size_type); + FTL_ARRAY_TRAIT(T, difference_type); + + FTL_ARRAY_TRAIT(T, pointer); + FTL_ARRAY_TRAIT(T, reference); + FTL_ARRAY_TRAIT(T, iterator); + FTL_ARRAY_TRAIT(T, reverse_iterator); + + FTL_ARRAY_TRAIT(T, const_pointer); + FTL_ARRAY_TRAIT(T, const_reference); + FTL_ARRAY_TRAIT(T, const_iterator); + FTL_ARRAY_TRAIT(T, const_reverse_iterator); + + // Creates an empty vector. + SmallVector() = default; + + // Constructs at most N elements. See StaticVector for underlying constructors. + template <typename Arg, typename... Args, + typename = std::enable_if_t<!IsSmallVector<remove_cvref_t<Arg>>{}>> + SmallVector(Arg&& arg, Args&&... args) + : mVector(std::in_place_type<Static>, std::forward<Arg>(arg), + std::forward<Args>(args)...) {} + + // Copies at most N elements from a smaller convertible vector. + template <typename U, size_t M, typename = std::enable_if_t<M <= N>> + SmallVector(const SmallVector<U, M>& other) + : SmallVector(IteratorRange, other.begin(), other.end()) {} + + void swap(SmallVector& other) { mVector.swap(other.mVector); } + + // Returns whether the vector is backed by static or dynamic storage. + bool dynamic() const { return std::holds_alternative<Dynamic>(mVector); } + + // Avoid std::visit as it generates a dispatch table. +#define DISPATCH(T, F, ...) \ + T F() __VA_ARGS__ { \ + return dynamic() ? std::get<Dynamic>(mVector).F() : std::get<Static>(mVector).F(); \ + } + + DISPATCH(size_type, max_size, const) + DISPATCH(size_type, size, const) + DISPATCH(bool, empty, const) + + // noexcept to suppress warning about zero variadic macro arguments. + DISPATCH(iterator, begin, noexcept) + DISPATCH(const_iterator, begin, const) + DISPATCH(const_iterator, cbegin, const) + + DISPATCH(iterator, end, noexcept) + DISPATCH(const_iterator, end, const) + DISPATCH(const_iterator, cend, const) + + DISPATCH(reverse_iterator, rbegin, noexcept) + DISPATCH(const_reverse_iterator, rbegin, const) + DISPATCH(const_reverse_iterator, crbegin, const) + + DISPATCH(reverse_iterator, rend, noexcept) + DISPATCH(const_reverse_iterator, rend, const) + DISPATCH(const_reverse_iterator, crend, const) + + DISPATCH(iterator, last, noexcept) + DISPATCH(const_iterator, last, const) + + DISPATCH(reference, front, noexcept) + DISPATCH(const_reference, front, const) + + DISPATCH(reference, back, noexcept) + DISPATCH(const_reference, back, const) + +#undef DISPATCH + + reference operator[](size_type i) { + return dynamic() ? std::get<Dynamic>(mVector)[i] : std::get<Static>(mVector)[i]; + } + + const_reference operator[](size_type i) const { return const_cast<SmallVector&>(*this)[i]; } + + // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so + // replacing at end() is erroneous. + // + // The element is emplaced via move constructor, so type T does not need to define copy/move + // assignment, e.g. its data members may be const. + // + // The arguments may directly or indirectly refer to the element being replaced. + // + // Iterators to the replaced element point to its replacement, and others remain valid. + // + template <typename... Args> + reference replace(const_iterator it, Args&&... args) { + if (dynamic()) { + return std::get<Dynamic>(mVector).replace(it, std::forward<Args>(args)...); + } else { + return std::get<Static>(mVector).replace(it, std::forward<Args>(args)...); + } + } + + // Appends an element, and returns a reference to it. + // + // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. + // Otherwise, only the end() iterator is invalidated. + // + template <typename... Args> + reference emplace_back(Args&&... args) { + constexpr auto insertStatic = &Static::template emplace_back<Args...>; + constexpr auto insertDynamic = &Dynamic::template emplace_back<Args...>; + return *insert<insertStatic, insertDynamic>(std::forward<Args>(args)...); + } + + // Appends an element. + // + // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. + // Otherwise, only the end() iterator is invalidated. + // + void push_back(const value_type& v) { + constexpr auto insertStatic = + static_cast<bool (Static::*)(const value_type&)>(&Static::push_back); + constexpr auto insertDynamic = + static_cast<bool (Dynamic::*)(const value_type&)>(&Dynamic::push_back); + insert<insertStatic, insertDynamic>(v); + } + + void push_back(value_type&& v) { + constexpr auto insertStatic = + static_cast<bool (Static::*)(value_type&&)>(&Static::push_back); + constexpr auto insertDynamic = + static_cast<bool (Dynamic::*)(value_type&&)>(&Dynamic::push_back); + insert<insertStatic, insertDynamic>(std::move(v)); + } + + // Removes the last element. The vector must not be empty, or the call is erroneous. + // + // The last() and end() iterators are invalidated. + // + void pop_back() { + if (dynamic()) { + std::get<Dynamic>(mVector).pop_back(); + } else { + std::get<Static>(mVector).pop_back(); + } + } + + // Erases an element, but does not preserve order. Rather than shifting subsequent elements, + // this moves the last element to the slot of the erased element. + // + // The last() and end() iterators, as well as those to the erased element, are invalidated. + // + void unstable_erase(iterator it) { + if (dynamic()) { + std::get<Dynamic>(mVector).unstable_erase(it); + } else { + std::get<Static>(mVector).unstable_erase(it); + } + } + +private: + template <auto insertStatic, auto insertDynamic, typename... Args> + auto insert(Args&&... args) { + if (Dynamic* const vector = std::get_if<Dynamic>(&mVector)) { + return (vector->*insertDynamic)(std::forward<Args>(args)...); + } + + auto& vector = std::get<Static>(mVector); + if (vector.full()) { + return (promote(vector).*insertDynamic)(std::forward<Args>(args)...); + } else { + return (vector.*insertStatic)(std::forward<Args>(args)...); + } + } + + Dynamic& promote(Static& staticVector) { + assert(staticVector.full()); + + // Allocate double capacity to reduce probability of reallocation. + Dynamic vector; + vector.reserve(Static::max_size() * 2); + std::move(staticVector.begin(), staticVector.end(), std::back_inserter(vector)); + + return mVector.template emplace<Dynamic>(std::move(vector)); + } + + std::variant<Static, Dynamic> mVector; +}; + +// Partial specialization without static storage. +template <typename T> +class SmallVector<T, 0> final : ArrayTraits<T>, + ArrayIterators<SmallVector<T, 0>, T>, + std::vector<T> { + using ArrayTraits<T>::construct_at; + + using Iter = ArrayIterators<SmallVector, T>; + using Impl = std::vector<T>; + + friend Iter; + +public: + FTL_ARRAY_TRAIT(T, value_type); + FTL_ARRAY_TRAIT(T, size_type); + FTL_ARRAY_TRAIT(T, difference_type); + + FTL_ARRAY_TRAIT(T, pointer); + FTL_ARRAY_TRAIT(T, reference); + FTL_ARRAY_TRAIT(T, iterator); + FTL_ARRAY_TRAIT(T, reverse_iterator); + + FTL_ARRAY_TRAIT(T, const_pointer); + FTL_ARRAY_TRAIT(T, const_reference); + FTL_ARRAY_TRAIT(T, const_iterator); + FTL_ARRAY_TRAIT(T, const_reverse_iterator); + + using Impl::Impl; + + using Impl::empty; + using Impl::max_size; + using Impl::size; + + using Impl::reserve; + + // std::vector iterators are not necessarily raw pointers. + iterator begin() { return Impl::data(); } + iterator end() { return Impl::data() + size(); } + + using Iter::begin; + using Iter::end; + + using Iter::cbegin; + using Iter::cend; + + using Iter::rbegin; + using Iter::rend; + + using Iter::crbegin; + using Iter::crend; + + using Iter::last; + + using Iter::back; + using Iter::front; + + using Iter::operator[]; + + template <typename... Args> + reference replace(const_iterator it, Args&&... args) { + value_type element{std::forward<Args>(args)...}; + std::destroy_at(it); + // This is only safe because exceptions are disabled. + return *construct_at(it, std::move(element)); + } + + template <typename... Args> + iterator emplace_back(Args&&... args) { + return &Impl::emplace_back(std::forward<Args>(args)...); + } + + bool push_back(const value_type& v) { + Impl::push_back(v); + return true; + } + + bool push_back(value_type&& v) { + Impl::push_back(std::move(v)); + return true; + } + + using Impl::pop_back; + + void unstable_erase(iterator it) { + if (it != last()) std::iter_swap(it, last()); + pop_back(); + } + + void swap(SmallVector& other) { Impl::swap(other); } +}; + +template <typename> +struct IsSmallVector : std::false_type {}; + +template <typename T, size_t N> +struct IsSmallVector<SmallVector<T, N>> : std::true_type {}; + +// Deduction guide for array constructor. +template <typename T, size_t N> +SmallVector(T (&)[N]) -> SmallVector<std::remove_cv_t<T>, N>; + +// Deduction guide for variadic constructor. +template <typename T, typename... Us, typename V = std::decay_t<T>, + typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>> +SmallVector(T&&, Us&&...) -> SmallVector<V, 1 + sizeof...(Us)>; + +// Deduction guide for in-place constructor. +template <typename T, typename... Us> +SmallVector(std::in_place_type_t<T>, Us&&...) -> SmallVector<T, sizeof...(Us)>; + +// Deduction guide for StaticVector conversion. +template <typename T, size_t N> +SmallVector(StaticVector<T, N>&&) -> SmallVector<T, N>; + +template <typename T, size_t N> +inline void swap(SmallVector<T, N>& lhs, SmallVector<T, N>& rhs) { + lhs.swap(rhs); +} + +} // namespace android::ftl diff --git a/include/ftl/StaticVector.h b/include/ftl/StaticVector.h new file mode 100644 index 0000000000..457095db76 --- /dev/null +++ b/include/ftl/StaticVector.h @@ -0,0 +1,350 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ftl/ArrayTraits.h> + +#include <algorithm> +#include <cassert> +#include <iterator> +#include <memory> +#include <type_traits> +#include <utility> + +namespace android::ftl { + +constexpr struct IteratorRangeTag {} IteratorRange; + +// Fixed-capacity, statically allocated counterpart of std::vector. Akin to std::array, StaticVector +// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather +// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a +// default constructor, since elements are constructed in-place as the vector grows. Operations that +// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise +// adheres to standard containers, except the unstable_erase operation that does not preserve order, +// and the replace operation that destructively emplaces. +// +// StaticVector<T, 1> is analogous to an iterable std::optional, but StaticVector<T, 0> is an error. +// +// Example usage: +// +// ftl::StaticVector<char, 3> vector; +// assert(vector.empty()); +// +// vector = {'a', 'b'}; +// assert(vector.size() == 2u); +// +// vector.push_back('c'); +// assert(vector.full()); +// +// assert(!vector.push_back('d')); +// assert(vector.size() == 3u); +// +// vector.unstable_erase(vector.begin()); +// assert(vector == (ftl::StaticVector{'c', 'b'})); +// +// vector.pop_back(); +// assert(vector.back() == 'c'); +// +// const char array[] = "hi"; +// vector = ftl::StaticVector(array); +// assert(vector == (ftl::StaticVector{'h', 'i', '\0'})); +// +template <typename T, size_t N> +class StaticVector final : ArrayTraits<T>, + ArrayIterators<StaticVector<T, N>, T>, + ArrayComparators<StaticVector> { + static_assert(N > 0); + + using ArrayTraits<T>::construct_at; + + using Iter = ArrayIterators<StaticVector, T>; + friend Iter; + + // There is ambiguity when constructing from two iterator-like elements like pointers: + // they could be an iterator range, or arguments for in-place construction. Assume the + // latter unless they are input iterators and cannot be used to construct elements. If + // the former is intended, the caller can pass an IteratorRangeTag to disambiguate. + template <typename I, typename Traits = std::iterator_traits<I>> + using IsInputIterator = std::conjunction< + std::is_base_of<std::input_iterator_tag, typename Traits::iterator_category>, + std::negation<std::is_constructible<T, I>>>; + +public: + FTL_ARRAY_TRAIT(T, value_type); + FTL_ARRAY_TRAIT(T, size_type); + FTL_ARRAY_TRAIT(T, difference_type); + + FTL_ARRAY_TRAIT(T, pointer); + FTL_ARRAY_TRAIT(T, reference); + FTL_ARRAY_TRAIT(T, iterator); + FTL_ARRAY_TRAIT(T, reverse_iterator); + + FTL_ARRAY_TRAIT(T, const_pointer); + FTL_ARRAY_TRAIT(T, const_reference); + FTL_ARRAY_TRAIT(T, const_iterator); + FTL_ARRAY_TRAIT(T, const_reverse_iterator); + + // Creates an empty vector. + StaticVector() = default; + + // Copies and moves a vector, respectively. + StaticVector(const StaticVector& other) + : StaticVector(IteratorRange, other.begin(), other.end()) {} + StaticVector(StaticVector&& other) { swap<Empty>(other); } + + // Copies at most N elements from a smaller convertible vector. + template <typename U, size_t M, typename = std::enable_if_t<M <= N>> + StaticVector(const StaticVector<U, M>& other) + : StaticVector(IteratorRange, other.begin(), other.end()) {} + + // Copies at most N elements from an array. + template <typename U, size_t M> + explicit StaticVector(U (&array)[M]) + : StaticVector(IteratorRange, std::begin(array), std::end(array)) {} + + // Copies at most N elements from the range [first, last). + // + // IteratorRangeTag disambiguates with initialization from two iterator-like elements. + // + template <typename Iterator, typename = std::enable_if_t<IsInputIterator<Iterator>{}>> + StaticVector(Iterator first, Iterator last) : StaticVector(IteratorRange, first, last) { + using V = typename std::iterator_traits<Iterator>::value_type; + static_assert(std::is_constructible_v<value_type, V>, "Incompatible iterator range"); + } + + template <typename Iterator> + StaticVector(IteratorRangeTag, Iterator first, Iterator last) + : mSize(std::min(max_size(), static_cast<size_type>(std::distance(first, last)))) { + std::uninitialized_copy(first, first + mSize, begin()); + } + + // Constructs at most N elements. The template arguments T and N are inferred using the + // deduction guide defined below. Note that T is determined from the first element, and + // subsequent elements must have convertible types: + // + // ftl::StaticVector vector = {1, 2, 3}; + // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<int, 3>>); + // + // const auto copy = "quince"s; + // auto move = "tart"s; + // ftl::StaticVector vector = {copy, std::move(move)}; + // + // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 2>>); + // + template <typename E, typename... Es, + typename = std::enable_if_t<std::is_constructible_v<value_type, E>>> + StaticVector(E&& element, Es&&... elements) + : StaticVector(std::index_sequence<0>{}, std::forward<E>(element), + std::forward<Es>(elements)...) { + static_assert(sizeof...(elements) < N, "Too many elements"); + } + + // Constructs at most N elements. The template arguments T and N are inferred using the + // deduction guide defined below. Element types must be convertible to the specified T: + // + // ftl::StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake"); + // static_assert(std::is_same_v<decltype(vector), ftl::StaticVector<std::string, 3>>); + // + template <typename... Es> + explicit StaticVector(std::in_place_type_t<T>, Es... elements) + : StaticVector(std::forward<Es>(elements)...) {} + + ~StaticVector() { std::destroy(begin(), end()); } + + StaticVector& operator=(const StaticVector& other) { + StaticVector copy(other); + swap(copy); + return *this; + } + + StaticVector& operator=(StaticVector&& other) { + std::destroy(begin(), end()); + mSize = 0; + swap<Empty>(other); + return *this; + } + + template <typename = void> + void swap(StaticVector&); + + static constexpr size_type max_size() { return N; } + size_type size() const { return mSize; } + + bool empty() const { return size() == 0; } + bool full() const { return size() == max_size(); } + + iterator begin() { return std::launder(reinterpret_cast<pointer>(mData)); } + iterator end() { return begin() + size(); } + + using Iter::begin; + using Iter::end; + + using Iter::cbegin; + using Iter::cend; + + using Iter::rbegin; + using Iter::rend; + + using Iter::crbegin; + using Iter::crend; + + using Iter::last; + + using Iter::back; + using Iter::front; + + using Iter::operator[]; + + // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so + // replacing at end() is erroneous. + // + // The element is emplaced via move constructor, so type T does not need to define copy/move + // assignment, e.g. its data members may be const. + // + // The arguments may directly or indirectly refer to the element being replaced. + // + // Iterators to the replaced element point to its replacement, and others remain valid. + // + template <typename... Args> + reference replace(const_iterator it, Args&&... args) { + value_type element{std::forward<Args>(args)...}; + std::destroy_at(it); + // This is only safe because exceptions are disabled. + return *construct_at(it, std::move(element)); + } + + // Appends an element, and returns an iterator to it. If the vector is full, the element is not + // inserted, and the end() iterator is returned. + // + // On success, the end() iterator is invalidated. + // + template <typename... Args> + iterator emplace_back(Args&&... args) { + if (full()) return end(); + const iterator it = construct_at(end(), std::forward<Args>(args)...); + ++mSize; + return it; + } + + // Appends an element unless the vector is full, and returns whether the element was inserted. + // + // On success, the end() iterator is invalidated. + // + bool push_back(const value_type& v) { + // Two statements for sequence point. + const iterator it = emplace_back(v); + return it != end(); + } + + bool push_back(value_type&& v) { + // Two statements for sequence point. + const iterator it = emplace_back(std::move(v)); + return it != end(); + } + + // Removes the last element. The vector must not be empty, or the call is erroneous. + // + // The last() and end() iterators are invalidated. + // + void pop_back() { unstable_erase(last()); } + + // Erases an element, but does not preserve order. Rather than shifting subsequent elements, + // this moves the last element to the slot of the erased element. + // + // The last() and end() iterators, as well as those to the erased element, are invalidated. + // + void unstable_erase(const_iterator it) { + std::destroy_at(it); + if (it != last()) { + // Move last element and destroy its source for destructor side effects. This is only + // safe because exceptions are disabled. + construct_at(it, std::move(back())); + std::destroy_at(last()); + } + --mSize; + } + +private: + struct Empty {}; + + // Recursion for variadic constructor. + template <size_t I, typename E, typename... Es> + StaticVector(std::index_sequence<I>, E&& element, Es&&... elements) + : StaticVector(std::index_sequence<I + 1>{}, std::forward<Es>(elements)...) { + construct_at(begin() + I, std::forward<E>(element)); + } + + // Base case for variadic constructor. + template <size_t I> + explicit StaticVector(std::index_sequence<I>) : mSize(I) {} + + size_type mSize = 0; + std::aligned_storage_t<sizeof(value_type), alignof(value_type)> mData[N]; +}; + +// Deduction guide for array constructor. +template <typename T, size_t N> +StaticVector(T (&)[N]) -> StaticVector<std::remove_cv_t<T>, N>; + +// Deduction guide for variadic constructor. +template <typename T, typename... Us, typename V = std::decay_t<T>, + typename = std::enable_if_t<(std::is_constructible_v<V, Us> && ...)>> +StaticVector(T&&, Us&&...) -> StaticVector<V, 1 + sizeof...(Us)>; + +// Deduction guide for in-place constructor. +template <typename T, typename... Us> +StaticVector(std::in_place_type_t<T>, Us&&...) -> StaticVector<T, sizeof...(Us)>; + +template <typename T, size_t N> +template <typename E> +void StaticVector<T, N>::swap(StaticVector& other) { + auto [to, from] = std::make_pair(this, &other); + if (from == this) return; + + // Assume this vector has fewer elements, so the excess of the other vector will be moved to it. + auto [min, max] = std::make_pair(size(), other.size()); + + // No elements to swap if moving into an empty vector. + if constexpr (std::is_same_v<E, Empty>) { + assert(min == 0); + } else { + if (min > max) { + std::swap(from, to); + std::swap(min, max); + } + + // Swap elements [0, min). + std::swap_ranges(begin(), begin() + min, other.begin()); + + // No elements to move if sizes are equal. + if (min == max) return; + } + + // Move elements [min, max) and destroy their source for destructor side effects. + const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max); + std::uninitialized_move(first, last, to->begin() + min); + std::destroy(first, last); + + std::swap(mSize, other.mSize); +} + +template <typename T, size_t N> +inline void swap(StaticVector<T, N>& lhs, StaticVector<T, N>& rhs) { + lhs.swap(rhs); +} + +} // namespace android::ftl diff --git a/include/gfx/.clang-format b/include/gfx/.clang-format deleted file mode 100644 index 86763a0cb4..0000000000 --- a/include/gfx/.clang-format +++ /dev/null @@ -1,11 +0,0 @@ -BasedOnStyle: Google - -AccessModifierOffset: -2 -AllowShortFunctionsOnASingleLine: Inline -BinPackParameters: false -ColumnLimit: 100 -CommentPragmas: NOLINT:.* -ConstructorInitializerAllOnOneLineOrOnePerLine: true -ConstructorInitializerIndentWidth: 2 -ContinuationIndentWidth: 8 -IndentWidth: 4 diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 2427a075a1..b90d57eb25 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -17,11 +17,12 @@ #ifndef _LIBINPUT_DISPLAY_VIEWPORT_H #define _LIBINPUT_DISPLAY_VIEWPORT_H -#include <cinttypes> -#include <optional> - #include <android-base/stringprintf.h> #include <input/Input.h> +#include <input/NamedEnum.h> + +#include <cinttypes> +#include <optional> using android::base::StringPrintf; @@ -39,24 +40,11 @@ enum { * Keep in sync with values in InputManagerService.java. */ enum class ViewportType : int32_t { - VIEWPORT_INTERNAL = 1, - VIEWPORT_EXTERNAL = 2, - VIEWPORT_VIRTUAL = 3, + INTERNAL = 1, + EXTERNAL = 2, + VIRTUAL = 3, }; -static const char* viewportTypeToString(ViewportType type) { - switch(type) { - case ViewportType::VIEWPORT_INTERNAL: - return "INTERNAL"; - case ViewportType::VIEWPORT_EXTERNAL: - return "EXTERNAL"; - case ViewportType::VIEWPORT_VIRTUAL: - return "VIRTUAL"; - default: - return "UNKNOWN"; - } -} - /* * Describes how coordinates are mapped on a physical display. * See com.android.server.display.DisplayViewport. @@ -97,7 +85,7 @@ struct DisplayViewport { isActive(false), uniqueId(), physicalPort(std::nullopt), - type(ViewportType::VIEWPORT_INTERNAL) {} + type(ViewportType::INTERNAL) {} bool operator==(const DisplayViewport& other) const { return displayId == other.displayId && orientation == other.orientation && @@ -134,7 +122,7 @@ struct DisplayViewport { isActive = false; uniqueId.clear(); physicalPort = std::nullopt; - type = ViewportType::VIEWPORT_INTERNAL; + type = ViewportType::INTERNAL; } std::string toString() const { @@ -143,7 +131,7 @@ struct DisplayViewport { "physicalFrame=[%d, %d, %d, %d], " "deviceSize=[%d, %d], " "isActive=[%d]", - viewportTypeToString(type), displayId, uniqueId.c_str(), + NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(), physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() : "<none>", orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, diff --git a/include/input/Flags.h b/include/input/Flags.h new file mode 100644 index 0000000000..072dd18cec --- /dev/null +++ b/include/input/Flags.h @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/stringprintf.h> + +#include <array> +#include <cstdint> +#include <optional> +#include <string> +#include <type_traits> + +#include "NamedEnum.h" +#include "utils/BitSet.h" + +#ifndef __UI_INPUT_FLAGS_H +#define __UI_INPUT_FLAGS_H + +namespace android { + +namespace details { + +template <typename F> +inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__; + +template <typename F, typename T, T... I> +constexpr auto generate_flag_values(std::integer_sequence<T, I...> seq) { + constexpr size_t count = seq.size(); + + std::array<F, count> values{}; + for (size_t i = 0, v = 0; v < count; ++i) { + values[v++] = static_cast<F>(T{1} << i); + } + + return values; +} + +template <typename F> +inline constexpr auto flag_values = generate_flag_values<F>( + std::make_integer_sequence<std::underlying_type_t<F>, flag_count<F>>{}); + +template <typename F, std::size_t... I> +constexpr auto generate_flag_names(std::index_sequence<I...>) noexcept { + return std::array<std::optional<std::string_view>, sizeof...(I)>{ + {enum_value_name<F, flag_values<F>[I]>()...}}; +} + +template <typename F> +inline constexpr auto flag_names = + generate_flag_names<F>(std::make_index_sequence<flag_count<F>>{}); + +// A trait for determining whether a type is specifically an enum class or not. +template <typename T, bool = std::is_enum_v<T>> +struct is_enum_class : std::false_type {}; + +// By definition, an enum class is an enum that is not implicitly convertible to its underlying +// type. +template <typename T> +struct is_enum_class<T, true> + : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {}; + +template <typename T> +inline constexpr bool is_enum_class_v = is_enum_class<T>::value; +} // namespace details + +template <auto V> +constexpr auto flag_name() { + using F = decltype(V); + return details::enum_value_name<F, V>(); +} + +template <typename F> +constexpr std::optional<std::string_view> flag_name(F flag) { + using U = std::underlying_type_t<F>; + auto idx = __builtin_ctzl(static_cast<U>(flag)); + return details::flag_names<F>[idx]; +} + +/* A class for handling flags defined by an enum or enum class in a type-safe way. */ +template <typename F> +class Flags { + // F must be an enum or its underlying type is undefined. Theoretically we could specialize this + // further to avoid this restriction but in general we want to encourage the use of enums + // anyways. + static_assert(std::is_enum_v<F>, "Flags type must be an enum"); + using U = typename std::underlying_type_t<F>; + +public: + constexpr Flags(F f) : mFlags(static_cast<U>(f)) {} + constexpr Flags() : mFlags(0) {} + constexpr Flags(const Flags<F>& f) : mFlags(f.mFlags) {} + + // Provide a non-explicit construct for non-enum classes since they easily convert to their + // underlying types (e.g. when used with bitwise operators). For enum classes, however, we + // should force them to be explicitly constructed from their underlying types to make full use + // of the type checker. + template <typename T = U> + constexpr Flags(T t, typename std::enable_if_t<!details::is_enum_class_v<F>, T>* = nullptr) + : mFlags(t) {} + template <typename T = U> + explicit constexpr Flags(T t, + typename std::enable_if_t<details::is_enum_class_v<F>, T>* = nullptr) + : mFlags(t) {} + + class Iterator { + // The type can't be larger than 64-bits otherwise it won't fit in BitSet64. + static_assert(sizeof(U) <= sizeof(uint64_t)); + + public: + Iterator(Flags<F> flags) : mRemainingFlags(flags.mFlags) { (*this)++; } + Iterator() : mRemainingFlags(0), mCurrFlag(static_cast<F>(0)) {} + + // Pre-fix ++ + Iterator& operator++() { + if (mRemainingFlags.isEmpty()) { + mCurrFlag = static_cast<F>(0); + } else { + uint64_t bit = mRemainingFlags.clearLastMarkedBit(); // counts from left + const U flag = 1 << (64 - bit - 1); + mCurrFlag = static_cast<F>(flag); + } + return *this; + } + + // Post-fix ++ + Iterator operator++(int) { + Iterator iter = *this; + ++*this; + return iter; + } + + bool operator==(Iterator other) const { + return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags; + } + + bool operator!=(Iterator other) const { return !(*this == other); } + + F operator*() { return mCurrFlag; } + + // iterator traits + + // In the future we could make this a bidirectional const iterator instead of a forward + // iterator but it doesn't seem worth the added complexity at this point. This could not, + // however, be made a non-const iterator as assigning one flag to another is a non-sensical + // operation. + using iterator_category = std::input_iterator_tag; + using value_type = F; + // Per the C++ spec, because input iterators are not assignable the iterator's reference + // type does not actually need to be a reference. In fact, making it a reference would imply + // that modifying it would change the underlying Flags object, which is obviously wrong for + // the same reason this can't be a non-const iterator. + using reference = F; + using difference_type = void; + using pointer = void; + + private: + BitSet64 mRemainingFlags; + F mCurrFlag; + }; + + /* + * Tests whether the given flag is set. + */ + bool test(F flag) const { + U f = static_cast<U>(flag); + return (f & mFlags) == f; + } + + /* Tests whether any of the given flags are set */ + bool any(Flags<F> f) { return (mFlags & f.mFlags) != 0; } + + /* Tests whether all of the given flags are set */ + bool all(Flags<F> f) { return (mFlags & f.mFlags) == f.mFlags; } + + Flags<F> operator|(Flags<F> rhs) const { return static_cast<F>(mFlags | rhs.mFlags); } + Flags<F>& operator|=(Flags<F> rhs) { + mFlags = mFlags | rhs.mFlags; + return *this; + } + + Flags<F> operator&(Flags<F> rhs) const { return static_cast<F>(mFlags & rhs.mFlags); } + Flags<F>& operator&=(Flags<F> rhs) { + mFlags = mFlags & rhs.mFlags; + return *this; + } + + Flags<F> operator^(Flags<F> rhs) const { return static_cast<F>(mFlags ^ rhs.mFlags); } + Flags<F>& operator^=(Flags<F> rhs) { + mFlags = mFlags ^ rhs.mFlags; + return *this; + } + + Flags<F> operator~() { return static_cast<F>(~mFlags); } + + bool operator==(Flags<F> rhs) const { return mFlags == rhs.mFlags; } + bool operator!=(Flags<F> rhs) const { return !operator==(rhs); } + + Flags<F>& operator=(const Flags<F>& rhs) { + mFlags = rhs.mFlags; + return *this; + } + + Iterator begin() const { return Iterator(*this); } + + Iterator end() const { return Iterator(); } + + /* + * Returns the stored set of flags. + * + * Note that this returns the underlying type rather than the base enum class. This is because + * the value is no longer necessarily a strict member of the enum since the returned value could + * be multiple enum variants OR'd together. + */ + U get() const { return mFlags; } + + std::string string() const { + std::string result; + bool first = true; + U unstringified = 0; + for (const F f : *this) { + std::optional<std::string_view> flagString = flag_name(f); + if (flagString) { + appendFlag(result, flagString.value(), first); + } else { + unstringified |= static_cast<U>(f); + } + } + + if (unstringified != 0) { + appendFlag(result, base::StringPrintf("0x%08x", unstringified), first); + } + + if (first) { + result += "0x0"; + } + + return result; + } + +private: + U mFlags; + + static void appendFlag(std::string& str, const std::string_view& flag, bool& first) { + if (first) { + first = false; + } else { + str += " | "; + } + str += flag; + } +}; + +// This namespace provides operator overloads for enum classes to make it easier to work with them +// as flags. In order to use these, add them via a `using namespace` declaration. +namespace flag_operators { + +template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>> +inline Flags<F> operator~(F f) { + using U = typename std::underlying_type_t<F>; + return static_cast<F>(~static_cast<U>(f)); +} +template <typename F, typename = std::enable_if_t<details::is_enum_class_v<F>>> +Flags<F> operator|(F lhs, F rhs) { + using U = typename std::underlying_type_t<F>; + return static_cast<F>(static_cast<U>(lhs) | static_cast<U>(rhs)); +} + +} // namespace flag_operators +} // namespace android + +#endif // __UI_INPUT_FLAGS_H
\ No newline at end of file diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h deleted file mode 100644 index d23e3b7767..0000000000 --- a/include/input/IInputFlinger.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _LIBINPUT_IINPUT_FLINGER_H -#define _LIBINPUT_IINPUT_FLINGER_H - -#include <stdint.h> -#include <sys/types.h> - -#include <binder/IInterface.h> - -#include <input/InputWindow.h> -#include <input/ISetInputWindowsListener.h> - -namespace android { - -/* - * This class defines the Binder IPC interface for accessing various - * InputFlinger features. - */ -class IInputFlinger : public IInterface { -public: - DECLARE_META_INTERFACE(InputFlinger) - - virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles, - const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0; - virtual void registerInputChannel(const sp<InputChannel>& channel) = 0; - virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0; -}; - - -/** - * Binder implementation. - */ -class BnInputFlinger : public BnInterface<IInputFlinger> { -public: - enum { - SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - REGISTER_INPUT_CHANNEL_TRANSACTION, - UNREGISTER_INPUT_CHANNEL_TRANSACTION - }; - - virtual status_t onTransact(uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags = 0); -}; - -} // namespace android - -#endif // _LIBINPUT_IINPUT_FLINGER_H diff --git a/include/input/Input.h b/include/input/Input.h index af9c41d7c3..3facfa5135 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -26,6 +26,7 @@ #include <android/input.h> #include <math.h> #include <stdint.h> +#include <ui/Transform.h> #include <utils/BitSet.h> #include <utils/KeyedVector.h> #include <utils/RefBase.h> @@ -140,14 +141,6 @@ enum { #define MAX_CONTROLLER_LEDS 4 /* - * SystemUiVisibility constants from View. - */ -enum { - ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0, - ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001, -}; - -/* * Maximum number of pointers supported per motion event. * Smallest number of pointers is 1. * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers @@ -184,7 +177,7 @@ struct AInputDevice { namespace android { -#ifdef __ANDROID__ +#ifdef __linux__ class Parcel; #endif @@ -287,9 +280,9 @@ private: public: // Used to divide integer space to ensure no conflict among these sources./ enum class Source : int32_t { - INPUT_READER = 0x0 << SOURCE_SHIFT, - INPUT_DISPATCHER = 0x1 << SOURCE_SHIFT, - OTHER = 0x3 << SOURCE_SHIFT, // E.g. app injected events + INPUT_READER = static_cast<int32_t>(0x0u << SOURCE_SHIFT), + INPUT_DISPATCHER = static_cast<int32_t>(0x1u << SOURCE_SHIFT), + OTHER = static_cast<int32_t>(0x3u << SOURCE_SHIFT), // E.g. app injected events }; IdGenerator(Source source); @@ -301,7 +294,7 @@ public: private: const Source mSource; - static constexpr int32_t SOURCE_MASK = 0x3 << SOURCE_SHIFT; + static constexpr int32_t SOURCE_MASK = static_cast<int32_t>(0x3u << SOURCE_SHIFT); }; /** @@ -311,11 +304,6 @@ private: */ constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN(); -/** - * Invalid value of HMAC - SHA256. Any events with this HMAC value will be marked as not verified. - */ -constexpr std::array<uint8_t, 32> INVALID_HMAC = {0}; - /* * Pointer coordinate data. */ @@ -348,6 +336,8 @@ struct PointerCoords { void scale(float globalScale, float windowXScale, float windowYScale); void applyOffset(float xOffset, float yOffset); + void transform(const ui::Transform& transform); + inline float getX() const { return getAxisValue(AMOTION_EVENT_AXIS_X); } @@ -356,7 +346,7 @@ struct PointerCoords { return getAxisValue(AMOTION_EVENT_AXIS_Y); } -#ifdef __ANDROID__ +#ifdef __linux__ status_t readFromParcel(Parcel* parcel); status_t writeToParcel(Parcel* parcel) const; #endif @@ -524,13 +514,11 @@ public: inline void setActionButton(int32_t button) { mActionButton = button; } - inline float getXScale() const { return mXScale; } - - inline float getYScale() const { return mYScale; } + inline float getXOffset() const { return mTransform.tx(); } - inline float getXOffset() const { return mXOffset; } + inline float getYOffset() const { return mTransform.ty(); } - inline float getYOffset() const { return mYOffset; } + inline ui::Transform getTransform() const { return mTransform; } inline float getXPrecision() const { return mXPrecision; } @@ -582,10 +570,18 @@ public: float getAxisValue(int32_t axis, size_t pointerIndex) const; + /** + * Get the X coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'. + * Identical to calling getHistoricalX(pointerIndex, getHistorySize()). + */ inline float getX(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_X, pointerIndex); } + /** + * Get the Y coordinate of the latest sample in this MotionEvent for pointer 'pointerIndex'. + * Identical to calling getHistoricalX(pointerIndex, getHistorySize()). + */ inline float getY(size_t pointerIndex) const { return getAxisValue(AMOTION_EVENT_AXIS_Y, pointerIndex); } @@ -692,8 +688,8 @@ public: void initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, - MotionClassification classification, float xScale, float yScale, float xOffset, - float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition, + MotionClassification classification, const ui::Transform& transform, + float xPrecision, float yPrecision, float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); @@ -710,9 +706,9 @@ public: // Apply 3x3 perspective matrix transformation. // Matrix is in row-major form and compatible with SkMatrix. - void transform(const float matrix[9]); + void transform(const std::array<float, 9>& matrix); -#ifdef __ANDROID__ +#ifdef __linux__ status_t readFromParcel(Parcel* parcel); status_t writeToParcel(Parcel* parcel) const; #endif @@ -726,7 +722,7 @@ public: inline const PointerProperties* getPointerProperties() const { return mPointerProperties.array(); } - inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.array(); } + inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.data(); } inline const PointerCoords* getSamplePointerCoords() const { return mSamplePointerCoords.array(); } @@ -734,7 +730,7 @@ public: static const char* getLabel(int32_t axis); static int32_t getAxisFromLabel(const char* label); - static const char* actionToString(int32_t action); + static std::string actionToString(int32_t action); protected: int32_t mAction; @@ -744,17 +740,14 @@ protected: int32_t mMetaState; int32_t mButtonState; MotionClassification mClassification; - float mXScale; - float mYScale; - float mXOffset; - float mYOffset; + ui::Transform mTransform; float mXPrecision; float mYPrecision; float mRawXCursorPosition; float mRawYCursorPosition; nsecs_t mDownTime; Vector<PointerProperties> mPointerProperties; - Vector<nsecs_t> mSampleEventTimes; + std::vector<nsecs_t> mSampleEventTimes; Vector<PointerCoords> mSamplePointerCoords; }; diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h index 86de394a31..8e4fe796a5 100644 --- a/include/input/InputApplication.h +++ b/include/input/InputApplication.h @@ -19,35 +19,24 @@ #include <string> +#include <android/InputApplicationInfo.h> + #include <binder/IBinder.h> #include <binder/Parcel.h> +#include <binder/Parcelable.h> #include <input/Input.h> #include <utils/RefBase.h> #include <utils/Timers.h> namespace android { - -/* - * Describes the properties of an application that can receive input. - */ -struct InputApplicationInfo { - sp<IBinder> token; - std::string name; - nsecs_t dispatchingTimeout; - - status_t write(Parcel& output) const; - static InputApplicationInfo read(const Parcel& from); -}; - - /* * Handle for an application that can receive input. * * Used by the native input dispatcher as a handle for the window manager objects * that describe an application. */ -class InputApplicationHandle : public RefBase { +class InputApplicationHandle { public: inline const InputApplicationInfo* getInfo() const { return &mInfo; @@ -57,19 +46,22 @@ public: return !mInfo.name.empty() ? mInfo.name : "<invalid>"; } - inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { - return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; - } - inline std::chrono::nanoseconds getDispatchingTimeout( std::chrono::nanoseconds defaultValue) const { - return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; + return mInfo.token ? std::chrono::milliseconds(mInfo.dispatchingTimeoutMillis) + : defaultValue; } inline sp<IBinder> getApplicationToken() const { return mInfo.token; } + bool operator==(const InputApplicationHandle& other) const { + return getName() == other.getName() && getApplicationToken() == other.getApplicationToken(); + } + + bool operator!=(const InputApplicationHandle& other) const { return !(*this == other); } + /** * Requests that the state of this object be updated to reflect * the most current available information about the application. @@ -80,9 +72,10 @@ public: * Returns true on success, or false if the handle is no longer valid. */ virtual bool updateInfo() = 0; + protected: - InputApplicationHandle(); - virtual ~InputApplicationHandle(); + InputApplicationHandle() = default; + virtual ~InputApplicationHandle() = default; InputApplicationInfo mInfo; }; diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 20a17e3347..60638caaab 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -108,11 +108,11 @@ public: inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } inline int32_t getKeyboardType() const { return mKeyboardType; } - inline void setKeyCharacterMap(const sp<KeyCharacterMap>& value) { + inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) { mKeyCharacterMap = value; } - inline sp<KeyCharacterMap> getKeyCharacterMap() const { + inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const { return mKeyCharacterMap; } @@ -136,7 +136,7 @@ private: bool mHasMic; uint32_t mSources; int32_t mKeyboardType; - sp<KeyCharacterMap> mKeyCharacterMap; + std::shared_ptr<KeyCharacterMap> mKeyCharacterMap; bool mHasVibrator; bool mHasButtonUnderPad; @@ -144,10 +144,10 @@ private: }; /* Types of input device configuration files. */ -enum InputDeviceConfigurationFileType { - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */ - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */ - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */ +enum class InputDeviceConfigurationFileType : int32_t { + CONFIGURATION = 0, /* .idc file */ + KEY_LAYOUT = 1, /* .kl file */ + KEY_CHARACTER_MAP = 2, /* .kcm file */ }; /* diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index b327d76b1c..2a742f9cf4 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -19,11 +19,7 @@ #include <input/Input.h> #include <android/keycodes.h> - -#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key } -#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis } -#define DEFINE_LED(led) { #led, ALED_##led } -#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag } +#include <unordered_map> namespace android { @@ -35,430 +31,41 @@ struct InputEventLabel { int value; }; +// NOTE: If you want a new key code, axis code, led code or flag code in keylayout file, +// then you must add it to InputEventLabels.cpp. -static const InputEventLabel KEYCODES[] = { - // NOTE: If you add a new keycode here you must also add it to several other files. - // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. - DEFINE_KEYCODE(UNKNOWN), - DEFINE_KEYCODE(SOFT_LEFT), - DEFINE_KEYCODE(SOFT_RIGHT), - DEFINE_KEYCODE(HOME), - DEFINE_KEYCODE(BACK), - DEFINE_KEYCODE(CALL), - DEFINE_KEYCODE(ENDCALL), - DEFINE_KEYCODE(0), - DEFINE_KEYCODE(1), - DEFINE_KEYCODE(2), - DEFINE_KEYCODE(3), - DEFINE_KEYCODE(4), - DEFINE_KEYCODE(5), - DEFINE_KEYCODE(6), - DEFINE_KEYCODE(7), - DEFINE_KEYCODE(8), - DEFINE_KEYCODE(9), - DEFINE_KEYCODE(STAR), - DEFINE_KEYCODE(POUND), - DEFINE_KEYCODE(DPAD_UP), - DEFINE_KEYCODE(DPAD_DOWN), - DEFINE_KEYCODE(DPAD_LEFT), - DEFINE_KEYCODE(DPAD_RIGHT), - DEFINE_KEYCODE(DPAD_CENTER), - DEFINE_KEYCODE(VOLUME_UP), - DEFINE_KEYCODE(VOLUME_DOWN), - DEFINE_KEYCODE(POWER), - DEFINE_KEYCODE(CAMERA), - DEFINE_KEYCODE(CLEAR), - DEFINE_KEYCODE(A), - DEFINE_KEYCODE(B), - DEFINE_KEYCODE(C), - DEFINE_KEYCODE(D), - DEFINE_KEYCODE(E), - DEFINE_KEYCODE(F), - DEFINE_KEYCODE(G), - DEFINE_KEYCODE(H), - DEFINE_KEYCODE(I), - DEFINE_KEYCODE(J), - DEFINE_KEYCODE(K), - DEFINE_KEYCODE(L), - DEFINE_KEYCODE(M), - DEFINE_KEYCODE(N), - DEFINE_KEYCODE(O), - DEFINE_KEYCODE(P), - DEFINE_KEYCODE(Q), - DEFINE_KEYCODE(R), - DEFINE_KEYCODE(S), - DEFINE_KEYCODE(T), - DEFINE_KEYCODE(U), - DEFINE_KEYCODE(V), - DEFINE_KEYCODE(W), - DEFINE_KEYCODE(X), - DEFINE_KEYCODE(Y), - DEFINE_KEYCODE(Z), - DEFINE_KEYCODE(COMMA), - DEFINE_KEYCODE(PERIOD), - DEFINE_KEYCODE(ALT_LEFT), - DEFINE_KEYCODE(ALT_RIGHT), - DEFINE_KEYCODE(SHIFT_LEFT), - DEFINE_KEYCODE(SHIFT_RIGHT), - DEFINE_KEYCODE(TAB), - DEFINE_KEYCODE(SPACE), - DEFINE_KEYCODE(SYM), - DEFINE_KEYCODE(EXPLORER), - DEFINE_KEYCODE(ENVELOPE), - DEFINE_KEYCODE(ENTER), - DEFINE_KEYCODE(DEL), - DEFINE_KEYCODE(GRAVE), - DEFINE_KEYCODE(MINUS), - DEFINE_KEYCODE(EQUALS), - DEFINE_KEYCODE(LEFT_BRACKET), - DEFINE_KEYCODE(RIGHT_BRACKET), - DEFINE_KEYCODE(BACKSLASH), - DEFINE_KEYCODE(SEMICOLON), - DEFINE_KEYCODE(APOSTROPHE), - DEFINE_KEYCODE(SLASH), - DEFINE_KEYCODE(AT), - DEFINE_KEYCODE(NUM), - DEFINE_KEYCODE(HEADSETHOOK), - DEFINE_KEYCODE(FOCUS), // *Camera* focus - DEFINE_KEYCODE(PLUS), - DEFINE_KEYCODE(MENU), - DEFINE_KEYCODE(NOTIFICATION), - DEFINE_KEYCODE(SEARCH), - DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), - DEFINE_KEYCODE(MEDIA_STOP), - DEFINE_KEYCODE(MEDIA_NEXT), - DEFINE_KEYCODE(MEDIA_PREVIOUS), - DEFINE_KEYCODE(MEDIA_REWIND), - DEFINE_KEYCODE(MEDIA_FAST_FORWARD), - DEFINE_KEYCODE(MUTE), - DEFINE_KEYCODE(PAGE_UP), - DEFINE_KEYCODE(PAGE_DOWN), - DEFINE_KEYCODE(PICTSYMBOLS), - DEFINE_KEYCODE(SWITCH_CHARSET), - DEFINE_KEYCODE(BUTTON_A), - DEFINE_KEYCODE(BUTTON_B), - DEFINE_KEYCODE(BUTTON_C), - DEFINE_KEYCODE(BUTTON_X), - DEFINE_KEYCODE(BUTTON_Y), - DEFINE_KEYCODE(BUTTON_Z), - DEFINE_KEYCODE(BUTTON_L1), - DEFINE_KEYCODE(BUTTON_R1), - DEFINE_KEYCODE(BUTTON_L2), - DEFINE_KEYCODE(BUTTON_R2), - DEFINE_KEYCODE(BUTTON_THUMBL), - DEFINE_KEYCODE(BUTTON_THUMBR), - DEFINE_KEYCODE(BUTTON_START), - DEFINE_KEYCODE(BUTTON_SELECT), - DEFINE_KEYCODE(BUTTON_MODE), - DEFINE_KEYCODE(ESCAPE), - DEFINE_KEYCODE(FORWARD_DEL), - DEFINE_KEYCODE(CTRL_LEFT), - DEFINE_KEYCODE(CTRL_RIGHT), - DEFINE_KEYCODE(CAPS_LOCK), - DEFINE_KEYCODE(SCROLL_LOCK), - DEFINE_KEYCODE(META_LEFT), - DEFINE_KEYCODE(META_RIGHT), - DEFINE_KEYCODE(FUNCTION), - DEFINE_KEYCODE(SYSRQ), - DEFINE_KEYCODE(BREAK), - DEFINE_KEYCODE(MOVE_HOME), - DEFINE_KEYCODE(MOVE_END), - DEFINE_KEYCODE(INSERT), - DEFINE_KEYCODE(FORWARD), - DEFINE_KEYCODE(MEDIA_PLAY), - DEFINE_KEYCODE(MEDIA_PAUSE), - DEFINE_KEYCODE(MEDIA_CLOSE), - DEFINE_KEYCODE(MEDIA_EJECT), - DEFINE_KEYCODE(MEDIA_RECORD), - DEFINE_KEYCODE(F1), - DEFINE_KEYCODE(F2), - DEFINE_KEYCODE(F3), - DEFINE_KEYCODE(F4), - DEFINE_KEYCODE(F5), - DEFINE_KEYCODE(F6), - DEFINE_KEYCODE(F7), - DEFINE_KEYCODE(F8), - DEFINE_KEYCODE(F9), - DEFINE_KEYCODE(F10), - DEFINE_KEYCODE(F11), - DEFINE_KEYCODE(F12), - DEFINE_KEYCODE(NUM_LOCK), - DEFINE_KEYCODE(NUMPAD_0), - DEFINE_KEYCODE(NUMPAD_1), - DEFINE_KEYCODE(NUMPAD_2), - DEFINE_KEYCODE(NUMPAD_3), - DEFINE_KEYCODE(NUMPAD_4), - DEFINE_KEYCODE(NUMPAD_5), - DEFINE_KEYCODE(NUMPAD_6), - DEFINE_KEYCODE(NUMPAD_7), - DEFINE_KEYCODE(NUMPAD_8), - DEFINE_KEYCODE(NUMPAD_9), - DEFINE_KEYCODE(NUMPAD_DIVIDE), - DEFINE_KEYCODE(NUMPAD_MULTIPLY), - DEFINE_KEYCODE(NUMPAD_SUBTRACT), - DEFINE_KEYCODE(NUMPAD_ADD), - DEFINE_KEYCODE(NUMPAD_DOT), - DEFINE_KEYCODE(NUMPAD_COMMA), - DEFINE_KEYCODE(NUMPAD_ENTER), - DEFINE_KEYCODE(NUMPAD_EQUALS), - DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), - DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), - DEFINE_KEYCODE(VOLUME_MUTE), - DEFINE_KEYCODE(INFO), - DEFINE_KEYCODE(CHANNEL_UP), - DEFINE_KEYCODE(CHANNEL_DOWN), - DEFINE_KEYCODE(ZOOM_IN), - DEFINE_KEYCODE(ZOOM_OUT), - DEFINE_KEYCODE(TV), - DEFINE_KEYCODE(WINDOW), - DEFINE_KEYCODE(GUIDE), - DEFINE_KEYCODE(DVR), - DEFINE_KEYCODE(BOOKMARK), - DEFINE_KEYCODE(CAPTIONS), - DEFINE_KEYCODE(SETTINGS), - DEFINE_KEYCODE(TV_POWER), - DEFINE_KEYCODE(TV_INPUT), - DEFINE_KEYCODE(STB_POWER), - DEFINE_KEYCODE(STB_INPUT), - DEFINE_KEYCODE(AVR_POWER), - DEFINE_KEYCODE(AVR_INPUT), - DEFINE_KEYCODE(PROG_RED), - DEFINE_KEYCODE(PROG_GREEN), - DEFINE_KEYCODE(PROG_YELLOW), - DEFINE_KEYCODE(PROG_BLUE), - DEFINE_KEYCODE(APP_SWITCH), - DEFINE_KEYCODE(BUTTON_1), - DEFINE_KEYCODE(BUTTON_2), - DEFINE_KEYCODE(BUTTON_3), - DEFINE_KEYCODE(BUTTON_4), - DEFINE_KEYCODE(BUTTON_5), - DEFINE_KEYCODE(BUTTON_6), - DEFINE_KEYCODE(BUTTON_7), - DEFINE_KEYCODE(BUTTON_8), - DEFINE_KEYCODE(BUTTON_9), - DEFINE_KEYCODE(BUTTON_10), - DEFINE_KEYCODE(BUTTON_11), - DEFINE_KEYCODE(BUTTON_12), - DEFINE_KEYCODE(BUTTON_13), - DEFINE_KEYCODE(BUTTON_14), - DEFINE_KEYCODE(BUTTON_15), - DEFINE_KEYCODE(BUTTON_16), - DEFINE_KEYCODE(LANGUAGE_SWITCH), - DEFINE_KEYCODE(MANNER_MODE), - DEFINE_KEYCODE(3D_MODE), - DEFINE_KEYCODE(CONTACTS), - DEFINE_KEYCODE(CALENDAR), - DEFINE_KEYCODE(MUSIC), - DEFINE_KEYCODE(CALCULATOR), - DEFINE_KEYCODE(ZENKAKU_HANKAKU), - DEFINE_KEYCODE(EISU), - DEFINE_KEYCODE(MUHENKAN), - DEFINE_KEYCODE(HENKAN), - DEFINE_KEYCODE(KATAKANA_HIRAGANA), - DEFINE_KEYCODE(YEN), - DEFINE_KEYCODE(RO), - DEFINE_KEYCODE(KANA), - DEFINE_KEYCODE(ASSIST), - DEFINE_KEYCODE(BRIGHTNESS_DOWN), - DEFINE_KEYCODE(BRIGHTNESS_UP), - DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), - DEFINE_KEYCODE(SLEEP), - DEFINE_KEYCODE(WAKEUP), - DEFINE_KEYCODE(PAIRING), - DEFINE_KEYCODE(MEDIA_TOP_MENU), - DEFINE_KEYCODE(11), - DEFINE_KEYCODE(12), - DEFINE_KEYCODE(LAST_CHANNEL), - DEFINE_KEYCODE(TV_DATA_SERVICE), - DEFINE_KEYCODE(VOICE_ASSIST), - DEFINE_KEYCODE(TV_RADIO_SERVICE), - DEFINE_KEYCODE(TV_TELETEXT), - DEFINE_KEYCODE(TV_NUMBER_ENTRY), - DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), - DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), - DEFINE_KEYCODE(TV_SATELLITE), - DEFINE_KEYCODE(TV_SATELLITE_BS), - DEFINE_KEYCODE(TV_SATELLITE_CS), - DEFINE_KEYCODE(TV_SATELLITE_SERVICE), - DEFINE_KEYCODE(TV_NETWORK), - DEFINE_KEYCODE(TV_ANTENNA_CABLE), - DEFINE_KEYCODE(TV_INPUT_HDMI_1), - DEFINE_KEYCODE(TV_INPUT_HDMI_2), - DEFINE_KEYCODE(TV_INPUT_HDMI_3), - DEFINE_KEYCODE(TV_INPUT_HDMI_4), - DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), - DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), - DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), - DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), - DEFINE_KEYCODE(TV_INPUT_VGA_1), - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), - DEFINE_KEYCODE(TV_ZOOM_MODE), - DEFINE_KEYCODE(TV_CONTENTS_MENU), - DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), - DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), - DEFINE_KEYCODE(HELP), - DEFINE_KEYCODE(NAVIGATE_PREVIOUS), - DEFINE_KEYCODE(NAVIGATE_NEXT), - DEFINE_KEYCODE(NAVIGATE_IN), - DEFINE_KEYCODE(NAVIGATE_OUT), - DEFINE_KEYCODE(STEM_PRIMARY), - DEFINE_KEYCODE(STEM_1), - DEFINE_KEYCODE(STEM_2), - DEFINE_KEYCODE(STEM_3), - DEFINE_KEYCODE(DPAD_UP_LEFT), - DEFINE_KEYCODE(DPAD_DOWN_LEFT), - DEFINE_KEYCODE(DPAD_UP_RIGHT), - DEFINE_KEYCODE(DPAD_DOWN_RIGHT), - DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), - DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), - DEFINE_KEYCODE(MEDIA_STEP_FORWARD), - DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), - DEFINE_KEYCODE(SOFT_SLEEP), - DEFINE_KEYCODE(CUT), - DEFINE_KEYCODE(COPY), - DEFINE_KEYCODE(PASTE), - DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), - DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), - DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), - DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), - DEFINE_KEYCODE(ALL_APPS), - DEFINE_KEYCODE(REFRESH), - DEFINE_KEYCODE(THUMBS_UP), - DEFINE_KEYCODE(THUMBS_DOWN), - DEFINE_KEYCODE(PROFILE_SWITCH), - - { nullptr, 0 } -}; - -static const InputEventLabel AXES[] = { - DEFINE_AXIS(X), - DEFINE_AXIS(Y), - DEFINE_AXIS(PRESSURE), - DEFINE_AXIS(SIZE), - DEFINE_AXIS(TOUCH_MAJOR), - DEFINE_AXIS(TOUCH_MINOR), - DEFINE_AXIS(TOOL_MAJOR), - DEFINE_AXIS(TOOL_MINOR), - DEFINE_AXIS(ORIENTATION), - DEFINE_AXIS(VSCROLL), - DEFINE_AXIS(HSCROLL), - DEFINE_AXIS(Z), - DEFINE_AXIS(RX), - DEFINE_AXIS(RY), - DEFINE_AXIS(RZ), - DEFINE_AXIS(HAT_X), - DEFINE_AXIS(HAT_Y), - DEFINE_AXIS(LTRIGGER), - DEFINE_AXIS(RTRIGGER), - DEFINE_AXIS(THROTTLE), - DEFINE_AXIS(RUDDER), - DEFINE_AXIS(WHEEL), - DEFINE_AXIS(GAS), - DEFINE_AXIS(BRAKE), - DEFINE_AXIS(DISTANCE), - DEFINE_AXIS(TILT), - DEFINE_AXIS(SCROLL), - DEFINE_AXIS(RELATIVE_X), - DEFINE_AXIS(RELATIVE_Y), - DEFINE_AXIS(GENERIC_1), - DEFINE_AXIS(GENERIC_2), - DEFINE_AXIS(GENERIC_3), - DEFINE_AXIS(GENERIC_4), - DEFINE_AXIS(GENERIC_5), - DEFINE_AXIS(GENERIC_6), - DEFINE_AXIS(GENERIC_7), - DEFINE_AXIS(GENERIC_8), - DEFINE_AXIS(GENERIC_9), - DEFINE_AXIS(GENERIC_10), - DEFINE_AXIS(GENERIC_11), - DEFINE_AXIS(GENERIC_12), - DEFINE_AXIS(GENERIC_13), - DEFINE_AXIS(GENERIC_14), - DEFINE_AXIS(GENERIC_15), - DEFINE_AXIS(GENERIC_16), - - // NOTE: If you add a new axis here you must also add it to several other files. - // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. - { nullptr, 0 } -}; +class InputEventLookup { +public: + static int lookupValueByLabel(const std::unordered_map<std::string, int>& map, + const char* literal); -static const InputEventLabel LEDS[] = { - DEFINE_LED(NUM_LOCK), - DEFINE_LED(CAPS_LOCK), - DEFINE_LED(SCROLL_LOCK), - DEFINE_LED(COMPOSE), - DEFINE_LED(KANA), - DEFINE_LED(SLEEP), - DEFINE_LED(SUSPEND), - DEFINE_LED(MUTE), - DEFINE_LED(MISC), - DEFINE_LED(MAIL), - DEFINE_LED(CHARGING), - DEFINE_LED(CONTROLLER_1), - DEFINE_LED(CONTROLLER_2), - DEFINE_LED(CONTROLLER_3), - DEFINE_LED(CONTROLLER_4), + static const char* lookupLabelByValue(const std::vector<InputEventLabel>& vec, int value); - // NOTE: If you add new LEDs here, you must also add them to Input.h - { nullptr, 0 } -}; + static int32_t getKeyCodeByLabel(const char* label); -static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL), - DEFINE_FLAG(FUNCTION), - DEFINE_FLAG(GESTURE), - DEFINE_FLAG(WAKE), + static const char* getLabelByKeyCode(int32_t keyCode); - {nullptr, 0}}; + static uint32_t getKeyFlagByLabel(const char* label); -static int lookupValueByLabel(const char* literal, const InputEventLabel *list) { - while (list->literal) { - if (strcmp(literal, list->literal) == 0) { - return list->value; - } - list++; - } - return list->value; -} + static int32_t getAxisByLabel(const char* label); -static const char* lookupLabelByValue(int value, const InputEventLabel* list) { - while (list->literal) { - if (list->value == value) { - return list->literal; - } - list++; - } - return nullptr; -} + static const char* getAxisLabel(int32_t axisId); -static inline int32_t getKeyCodeByLabel(const char* label) { - return int32_t(lookupValueByLabel(label, KEYCODES)); -} + static int32_t getLedByLabel(const char* label); -static inline const char* getLabelByKeyCode(int32_t keyCode) { - if (keyCode >= 0 && keyCode < static_cast<int32_t>(size(KEYCODES))) { - return KEYCODES[keyCode].literal; - } - return nullptr; -} +private: + static const std::unordered_map<std::string, int> KEYCODES; -static inline uint32_t getKeyFlagByLabel(const char* label) { - return uint32_t(lookupValueByLabel(label, FLAGS)); -} + static const std::vector<InputEventLabel> KEY_NAMES; -static inline int32_t getAxisByLabel(const char* label) { - return int32_t(lookupValueByLabel(label, AXES)); -} + static const std::unordered_map<std::string, int> AXES; -static inline const char* getAxisLabel(int32_t axisId) { - return lookupLabelByValue(axisId, AXES); -} + static const std::vector<InputEventLabel> AXES_NAMES; -static inline int32_t getLedByLabel(const char* label) { - return int32_t(lookupValueByLabel(label, LEDS)); -} + static const std::unordered_map<std::string, int> LEDS; + static const std::unordered_map<std::string, int> FLAGS; +}; } // namespace android #endif // _LIBINPUT_INPUT_EVENT_LABELS_H diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 7ca9031f77..ad0a14ed0b 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -34,12 +34,14 @@ #include <android-base/chrono_utils.h> #include <binder/IBinder.h> +#include <binder/Parcelable.h> #include <input/Input.h> +#include <sys/stat.h> +#include <ui/Transform.h> #include <utils/BitSet.h> #include <utils/Errors.h> #include <utils/RefBase.h> #include <utils/Timers.h> -#include <utils/Vector.h> #include <android-base/unique_fd.h> @@ -69,10 +71,7 @@ struct InputMessage { struct Header { Type type; // 4 bytes - // We don't need this field in order to align the body below but we - // leave it here because InputMessage::size() and other functions - // compute the size of this structure as sizeof(Header) + sizeof(Body). - uint32_t padding; + uint32_t seq; } header; // Body *must* be 8 byte aligned. @@ -81,8 +80,8 @@ struct InputMessage { static_assert(sizeof(std::array<uint8_t, 32>) == 32); union Body { struct Key { - uint32_t seq; int32_t eventId; + uint32_t empty1; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -101,8 +100,8 @@ struct InputMessage { } key; struct Motion { - uint32_t seq; int32_t eventId; + uint32_t empty1; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -117,10 +116,12 @@ struct InputMessage { uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); - float xScale; - float yScale; - float xOffset; - float yOffset; + float dsdx; + float dtdx; + float dtdy; + float dsdy; + float tx; + float ty; float xPrecision; float yPrecision; float xCursorPosition; @@ -151,16 +152,14 @@ struct InputMessage { } motion; struct Finished { - uint32_t seq; + uint32_t empty1; uint32_t handled; // actually a bool, but we must maintain 8-byte alignment inline size_t size() const { return sizeof(Finished); } } finished; struct Focus { - uint32_t seq; int32_t eventId; - uint32_t empty1; // The following two fields take up 4 bytes total uint16_t hasFocus; // actually a bool uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment @@ -172,6 +171,19 @@ struct InputMessage { bool isValid(size_t actualSize) const; size_t size() const; void getSanitizedCopy(InputMessage* msg) const; + + static const char* typeToString(Type type) { + switch (type) { + case Type::KEY: + return "KEY"; + case Type::MOTION: + return "MOTION"; + case Type::FINISHED: + return "FINISHED"; + case Type::FOCUS: + return "FOCUS"; + } + } }; /* @@ -182,14 +194,15 @@ struct InputMessage { * * The input channel is closed when all references to it are released. */ -class InputChannel : public RefBase { -protected: - virtual ~InputChannel(); - +class InputChannel : public Parcelable { public: - static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd, - sp<IBinder> token); - + static std::unique_ptr<InputChannel> create(const std::string& name, + android::base::unique_fd fd, sp<IBinder> token); + InputChannel() = default; + InputChannel(const InputChannel& other) + : mName(other.mName), mFd(::dup(other.mFd)), mToken(other.mToken){}; + InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); + virtual ~InputChannel(); /** * Create a pair of input channels. * The two returned input channels are equivalent, and are labeled as "server" and "client" @@ -198,10 +211,12 @@ public: * Return OK on success. */ static status_t openInputChannelPair(const std::string& name, - sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel); + std::unique_ptr<InputChannel>& outServerChannel, + std::unique_ptr<InputChannel>& outClientChannel); inline std::string getName() const { return mName; } - inline int getFd() const { return mFd.get(); } + inline const android::base::unique_fd& getFd() const { return mFd; } + inline sp<IBinder> getToken() const { return mToken; } /* Send a message to the other endpoint. * @@ -229,10 +244,12 @@ public: status_t receiveMessage(InputMessage* msg); /* Return a new object that has a duplicate of this channel's fd. */ - sp<InputChannel> dup() const; + std::unique_ptr<InputChannel> dup() const; - status_t write(Parcel& out) const; - static sp<InputChannel> read(const Parcel& from); + void copyTo(InputChannel& outChannel) const; + + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; /** * The connection token is used to identify the input connection, i.e. @@ -248,8 +265,22 @@ public: */ sp<IBinder> getConnectionToken() const; + bool operator==(const InputChannel& inputChannel) const { + struct stat lhs, rhs; + if (fstat(mFd.get(), &lhs) != 0) { + return false; + } + if (fstat(inputChannel.getFd(), &rhs) != 0) { + return false; + } + // If file descriptors are pointing to same inode they are duplicated fds. + return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken && + lhs.st_ino == rhs.st_ino; + } + private: - InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token); + base::unique_fd dupFd() const; + std::string mName; android::base::unique_fd mFd; @@ -262,13 +293,13 @@ private: class InputPublisher { public: /* Creates a publisher associated with an input channel. */ - explicit InputPublisher(const sp<InputChannel>& channel); + explicit InputPublisher(const std::shared_ptr<InputChannel>& channel); /* Destroys the publisher and releases its input channel. */ ~InputPublisher(); /* Gets the underlying input channel. */ - inline sp<InputChannel> getChannel() { return mChannel; } + inline std::shared_ptr<InputChannel> getChannel() { return mChannel; } /* Publishes a key event to the input channel. * @@ -295,11 +326,10 @@ public: int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, - MotionClassification classification, float xScale, float yScale, - float xOffset, float yOffset, float xPrecision, float yPrecision, - float xCursorPosition, float yCursorPosition, nsecs_t downTime, - nsecs_t eventTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, + MotionClassification classification, const ui::Transform& transform, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); /* Publishes a focus event to the input channel. @@ -325,8 +355,7 @@ public: status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: - - sp<InputChannel> mChannel; + std::shared_ptr<InputChannel> mChannel; }; /* @@ -335,13 +364,13 @@ private: class InputConsumer { public: /* Creates a consumer associated with an input channel. */ - explicit InputConsumer(const sp<InputChannel>& channel); + explicit InputConsumer(const std::shared_ptr<InputChannel>& channel); /* Destroys the consumer and releases its input channel. */ ~InputConsumer(); /* Gets the underlying input channel. */ - inline sp<InputChannel> getChannel() { return mChannel; } + inline std::shared_ptr<InputChannel> getChannel() { return mChannel; } /* Consumes an input event from the input channel and copies its contents into * an InputEvent object created using the specified factory. @@ -411,12 +440,13 @@ public: */ int32_t getPendingBatchSource() const; + std::string dump() const; + private: // True if touch resampling is enabled. const bool mResampleTouch; - // The input channel. - sp<InputChannel> mChannel; + std::shared_ptr<InputChannel> mChannel; // The current input message. InputMessage mMsg; @@ -427,9 +457,9 @@ private: // Batched motion events per device and source. struct Batch { - Vector<InputMessage> samples; + std::vector<InputMessage> samples; }; - Vector<Batch> mBatches; + std::vector<Batch> mBatches; // Touch state per device and source, only for sources of class pointer. struct History { @@ -516,7 +546,7 @@ private: return false; } }; - Vector<TouchState> mTouchStates; + std::vector<TouchState> mTouchStates; // Chain of batched sequence numbers. When multiple input messages are combined into // a batch, we append a record here that associates the last sequence number in the @@ -526,7 +556,7 @@ private: uint32_t seq; // sequence number of batched input message uint32_t chain; // sequence number of previous batched input message }; - Vector<SeqChain> mSeqChains; + std::vector<SeqChain> mSeqChains; status_t consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index 2dac5b62a7..36097d6d65 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -17,105 +17,117 @@ #ifndef _UI_INPUT_WINDOW_H #define _UI_INPUT_WINDOW_H +#include <android/os/TouchOcclusionMode.h> +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <input/Flags.h> #include <input/Input.h> #include <input/InputTransport.h> #include <ui/Rect.h> #include <ui/Region.h> +#include <ui/Transform.h> #include <utils/RefBase.h> #include <utils/Timers.h> #include "InputApplication.h" +using android::os::TouchOcclusionMode; + namespace android { -class Parcel; /* * Describes the properties of a window that can receive input. */ -struct InputWindowInfo { +struct InputWindowInfo : public Parcelable { InputWindowInfo() = default; - InputWindowInfo(const Parcel& from); // Window flags from WindowManager.LayoutParams - enum { - FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, - FLAG_DIM_BEHIND = 0x00000002, - FLAG_BLUR_BEHIND = 0x00000004, - FLAG_NOT_FOCUSABLE = 0x00000008, - FLAG_NOT_TOUCHABLE = 0x00000010, - FLAG_NOT_TOUCH_MODAL = 0x00000020, - FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, - FLAG_KEEP_SCREEN_ON = 0x00000080, - FLAG_LAYOUT_IN_SCREEN = 0x00000100, - FLAG_LAYOUT_NO_LIMITS = 0x00000200, - FLAG_FULLSCREEN = 0x00000400, - FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, - FLAG_DITHER = 0x00001000, - FLAG_SECURE = 0x00002000, - FLAG_SCALED = 0x00004000, - FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, - FLAG_LAYOUT_INSET_DECOR = 0x00010000, - FLAG_ALT_FOCUSABLE_IM = 0x00020000, - FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, - FLAG_SHOW_WHEN_LOCKED = 0x00080000, - FLAG_SHOW_WALLPAPER = 0x00100000, - FLAG_TURN_SCREEN_ON = 0x00200000, - FLAG_DISMISS_KEYGUARD = 0x00400000, - FLAG_SPLIT_TOUCH = 0x00800000, - FLAG_SLIPPERY = 0x20000000, - }; - - // Window types from WindowManager.LayoutParams - enum { + enum class Flag : uint32_t { + ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, + DIM_BEHIND = 0x00000002, + BLUR_BEHIND = 0x00000004, + NOT_FOCUSABLE = 0x00000008, + NOT_TOUCHABLE = 0x00000010, + NOT_TOUCH_MODAL = 0x00000020, + TOUCHABLE_WHEN_WAKING = 0x00000040, + KEEP_SCREEN_ON = 0x00000080, + LAYOUT_IN_SCREEN = 0x00000100, + LAYOUT_NO_LIMITS = 0x00000200, + FULLSCREEN = 0x00000400, + FORCE_NOT_FULLSCREEN = 0x00000800, + DITHER = 0x00001000, + SECURE = 0x00002000, + SCALED = 0x00004000, + IGNORE_CHEEK_PRESSES = 0x00008000, + LAYOUT_INSET_DECOR = 0x00010000, + ALT_FOCUSABLE_IM = 0x00020000, + WATCH_OUTSIDE_TOUCH = 0x00040000, + SHOW_WHEN_LOCKED = 0x00080000, + SHOW_WALLPAPER = 0x00100000, + TURN_SCREEN_ON = 0x00200000, + DISMISS_KEYGUARD = 0x00400000, + SPLIT_TOUCH = 0x00800000, + HARDWARE_ACCELERATED = 0x01000000, + LAYOUT_IN_OVERSCAN = 0x02000000, + TRANSLUCENT_STATUS = 0x04000000, + TRANSLUCENT_NAVIGATION = 0x08000000, + LOCAL_FOCUS_MODE = 0x10000000, + SLIPPERY = 0x20000000, + LAYOUT_ATTACHED_IN_DECOR = 0x40000000, + DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000, + }; // Window types from WindowManager.LayoutParams + + enum class Type : int32_t { + UNKNOWN = 0, FIRST_APPLICATION_WINDOW = 1, - TYPE_BASE_APPLICATION = 1, - TYPE_APPLICATION = 2, - TYPE_APPLICATION_STARTING = 3, + BASE_APPLICATION = 1, + APPLICATION = 2, + APPLICATION_STARTING = 3, LAST_APPLICATION_WINDOW = 99, FIRST_SUB_WINDOW = 1000, - TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW, - TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1, - TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2, - TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3, - TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4, + APPLICATION_PANEL = FIRST_SUB_WINDOW, + APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1, + APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2, + APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3, + APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4, LAST_SUB_WINDOW = 1999, FIRST_SYSTEM_WINDOW = 2000, - TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW, - TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1, - TYPE_PHONE = FIRST_SYSTEM_WINDOW + 2, - TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3, - TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW + 4, - TYPE_TOAST = FIRST_SYSTEM_WINDOW + 5, - TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6, - TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7, - TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8, - TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9, - TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10, - TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11, - TYPE_INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12, - TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW + 13, - TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14, - TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15, - TYPE_DRAG = FIRST_SYSTEM_WINDOW + 16, - TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17, - TYPE_POINTER = FIRST_SYSTEM_WINDOW + 18, - TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19, - TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20, - TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21, - TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22, - TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24, - TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27, - TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, - TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, - TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, - TYPE_TRUSTED_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 42, + STATUS_BAR = FIRST_SYSTEM_WINDOW, + SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1, + PHONE = FIRST_SYSTEM_WINDOW + 2, + SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3, + KEYGUARD = FIRST_SYSTEM_WINDOW + 4, + TOAST = FIRST_SYSTEM_WINDOW + 5, + SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6, + PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7, + SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8, + KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9, + SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10, + INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11, + INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12, + WALLPAPER = FIRST_SYSTEM_WINDOW + 13, + STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14, + SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15, + DRAG = FIRST_SYSTEM_WINDOW + 16, + STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17, + POINTER = FIRST_SYSTEM_WINDOW + 18, + NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19, + VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20, + BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21, + INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22, + NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24, + MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27, + ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, + DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, + ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39, + NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, LAST_SYSTEM_WINDOW = 2999, }; - enum { - INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001, - INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002, - INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004, + enum class Feature { + DISABLE_TOUCH_PAD_GESTURES = 0x00000001, + NO_INPUT_CHANNEL = 0x00000002, + DISABLE_USER_ACTIVITY = 0x00000004, }; /* These values are filled in by the WM and passed through SurfaceFlinger @@ -127,9 +139,9 @@ struct InputWindowInfo { // This uniquely identifies the input window. int32_t id = -1; std::string name; - int32_t layoutParamsFlags = 0; - int32_t layoutParamsType = 0; - nsecs_t dispatchingTimeout = -1; + Flags<Flag> flags; + Type type = Type::UNKNOWN; + std::chrono::nanoseconds dispatchingTimeout = std::chrono::seconds(5); /* These values are filled in by SurfaceFlinger. */ int32_t frameLeft = -1; @@ -149,9 +161,12 @@ struct InputWindowInfo { // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis. float globalScaleFactor = 1.0f; - // Scaling factors applied to individual windows. - float windowXScale = 1.0f; - float windowYScale = 1.0f; + // The opacity of this window, from 0.0 to 1.0 (inclusive). + // An alpha of 1.0 means fully opaque and 0.0 means fully transparent. + float alpha; + + // Transform applied to individual windows. + ui::Transform transform; /* * This is filled in by the WM relative to the frame and then translated @@ -159,13 +174,20 @@ struct InputWindowInfo { */ Region touchableRegion; bool visible = false; - bool canReceiveKeys = false; - bool hasFocus = false; + bool focusable = false; bool hasWallpaper = false; bool paused = false; + /* This flag is set when the window is of a trusted type that is allowed to silently + * overlay other windows for the purpose of implementing the secure views feature. + * Trusted overlays, such as IME windows, can partly obscure other windows without causing + * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. + */ + bool trustedOverlay = false; + TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED; int32_t ownerPid = -1; int32_t ownerUid = -1; - int32_t inputFeatures = 0; + std::string packageName; + Flags<Feature> inputFeatures; int32_t displayId = ADISPLAY_ID_NONE; int32_t portalToDisplayId = ADISPLAY_ID_NONE; InputApplicationInfo applicationInfo; @@ -175,23 +197,19 @@ struct InputWindowInfo { void addTouchableRegion(const Rect& region); bool touchableRegionContainsPoint(int32_t x, int32_t y) const; - bool frameContainsPoint(int32_t x, int32_t y) const; - /* Returns true if the window is of a trusted type that is allowed to silently - * overlay other windows for the purpose of implementing the secure views feature. - * Trusted overlays, such as IME windows, can partly obscure other windows without causing - * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. - */ - bool isTrustedOverlay() const; + bool frameContainsPoint(int32_t x, int32_t y) const; bool supportsSplitTouch() const; bool overlaps(const InputWindowInfo* other) const; - status_t write(Parcel& output) const; - static InputWindowInfo read(const Parcel& from); -}; + bool operator==(const InputWindowInfo& inputChannel) const; + status_t writeToParcel(android::Parcel* parcel) const override; + + status_t readFromParcel(const android::Parcel* parcel) override; +}; /* * Handle for a window that can receive input. @@ -201,26 +219,19 @@ struct InputWindowInfo { */ class InputWindowHandle : public RefBase { public: + explicit InputWindowHandle(); + InputWindowHandle(const InputWindowHandle& other); + InputWindowHandle(const InputWindowInfo& other); - inline const InputWindowInfo* getInfo() const { - return &mInfo; - } + inline const InputWindowInfo* getInfo() const { return &mInfo; } sp<IBinder> getToken() const; int32_t getId() const { return mInfo.id; } - sp<IBinder> getApplicationToken() { - return mInfo.applicationInfo.token; - } + sp<IBinder> getApplicationToken() { return mInfo.applicationInfo.token; } - inline std::string getName() const { - return !mInfo.name.empty() ? mInfo.name : "<invalid>"; - } - - inline nsecs_t getDispatchingTimeout(nsecs_t defaultValue) const { - return mInfo.token ? mInfo.dispatchingTimeout : defaultValue; - } + inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : "<invalid>"; } inline std::chrono::nanoseconds getDispatchingTimeout( std::chrono::nanoseconds defaultValue) const { @@ -230,13 +241,14 @@ public: /** * Requests that the state of this object be updated to reflect * the most current available information about the application. + * As this class is created as RefBase object, no pure virtual function is allowed. * * This method should only be called from within the input dispatcher's * critical section. * * Returns true on success, or false if the handle is no longer valid. */ - virtual bool updateInfo() = 0; + virtual bool updateInfo() { return false; } /** * Updates from another input window handle. @@ -249,13 +261,15 @@ public: */ void releaseChannel(); + // Not override since this class is not derrived from Parcelable. + status_t readFromParcel(const android::Parcel* parcel); + status_t writeToParcel(android::Parcel* parcel) const; + protected: - explicit InputWindowHandle(); virtual ~InputWindowHandle(); InputWindowInfo mInfo; }; - } // namespace android #endif // _UI_INPUT_WINDOW_H diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index a1a32a63de..23f8ddf764 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -19,16 +19,16 @@ #include <stdint.h> -#ifdef __ANDROID__ +#ifdef __linux__ #include <binder/IBinder.h> #endif +#include <android-base/result.h> #include <input/Input.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Tokenizer.h> #include <utils/Unicode.h> -#include <utils/RefBase.h> // Maximum number of keys supported by KeyCharacterMaps #define MAX_KEYS 8192 @@ -42,29 +42,29 @@ namespace android { * * This object is immutable after it has been loaded. */ -class KeyCharacterMap : public RefBase { +class KeyCharacterMap { public: - enum KeyboardType { - KEYBOARD_TYPE_UNKNOWN = 0, - KEYBOARD_TYPE_NUMERIC = 1, - KEYBOARD_TYPE_PREDICTIVE = 2, - KEYBOARD_TYPE_ALPHA = 3, - KEYBOARD_TYPE_FULL = 4, + enum class KeyboardType : int32_t { + UNKNOWN = 0, + NUMERIC = 1, + PREDICTIVE = 2, + ALPHA = 3, + FULL = 4, /** * Deprecated. Set 'keyboard.specialFunction' to '1' in the device's IDC file instead. */ - KEYBOARD_TYPE_SPECIAL_FUNCTION = 5, - KEYBOARD_TYPE_OVERLAY = 6, + SPECIAL_FUNCTION = 5, + OVERLAY = 6, }; - enum Format { + enum class Format { // Base keyboard layout, may contain device-specific options, such as "type" declaration. - FORMAT_BASE = 0, + BASE = 0, // Overlay keyboard layout, more restrictive, may be published by applications, // cannot override device-specific options. - FORMAT_OVERLAY = 1, + OVERLAY = 1, // Either base or overlay layout ok. - FORMAT_ANY = 2, + ANY = 2, }; // Substitute key code and meta state for fallback action. @@ -74,21 +74,21 @@ public: }; /* Loads a key character map from a file. */ - static status_t load(const std::string& filename, Format format, sp<KeyCharacterMap>* outMap); + static base::Result<std::shared_ptr<KeyCharacterMap>> load(const std::string& filename, + Format format); /* Loads a key character map from its string contents. */ - static status_t loadContents(const std::string& filename, - const char* contents, Format format, sp<KeyCharacterMap>* outMap); + static base::Result<std::shared_ptr<KeyCharacterMap>> loadContents(const std::string& filename, + const char* contents, + Format format); - /* Combines a base key character map and an overlay. */ - static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base, - const sp<KeyCharacterMap>& overlay); + const std::string getLoadFileName() const; - /* Returns an empty key character map. */ - static sp<KeyCharacterMap> empty(); + /* Combines this key character map with an overlay. */ + void combine(const KeyCharacterMap& overlay); /* Gets the keyboard type. */ - int32_t getKeyboardType() const; + KeyboardType getKeyboardType() const; /* Gets the primary character for this key as in the label physically printed on it. * Returns 0 if none (eg. for non-printing keys). */ @@ -134,15 +134,16 @@ public: void tryRemapKey(int32_t scanCode, int32_t metaState, int32_t* outKeyCode, int32_t* outMetaState) const; -#ifdef __ANDROID__ +#ifdef __linux__ /* Reads a key map from a parcel. */ - static sp<KeyCharacterMap> readFromParcel(Parcel* parcel); + static std::shared_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel); /* Writes a key map to a parcel. */ void writeToParcel(Parcel* parcel) const; #endif -protected: + KeyCharacterMap(const KeyCharacterMap& other); + virtual ~KeyCharacterMap(); private: @@ -224,16 +225,14 @@ private: status_t parseCharacterLiteral(char16_t* outCharacter); }; - static sp<KeyCharacterMap> sEmpty; - KeyedVector<int32_t, Key*> mKeys; - int mType; + KeyboardType mType; + std::string mLoadFileName; KeyedVector<int32_t, int32_t> mKeysByScanCode; KeyedVector<int32_t, int32_t> mKeysByUsageCode; KeyCharacterMap(); - KeyCharacterMap(const KeyCharacterMap& other); bool getKey(int32_t keyCode, const Key** outKey) const; bool getKeyBehavior(int32_t keyCode, int32_t metaState, @@ -242,7 +241,7 @@ private: bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const; - static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap); + static base::Result<std::shared_ptr<KeyCharacterMap>> load(Tokenizer* tokenizer, Format format); static void addKey(Vector<KeyEvent>& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time); diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 26f35012e2..872dd45c73 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -17,11 +17,12 @@ #ifndef _LIBINPUT_KEY_LAYOUT_MAP_H #define _LIBINPUT_KEY_LAYOUT_MAP_H +#include <android-base/result.h> #include <stdint.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> -#include <utils/Tokenizer.h> #include <utils/RefBase.h> +#include <utils/Tokenizer.h> namespace android { @@ -60,9 +61,12 @@ struct AxisInfo { * * This object is immutable after it has been loaded. */ -class KeyLayoutMap : public RefBase { +class KeyLayoutMap { public: - static status_t load(const std::string& filename, sp<KeyLayoutMap>* outMap); + static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename); + static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer); + static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename, + const char* contents); status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; @@ -71,8 +75,8 @@ public: status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const; status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; + const std::string getLoadFileName() const; -protected: virtual ~KeyLayoutMap(); private: @@ -91,6 +95,7 @@ private: KeyedVector<int32_t, AxisInfo> mAxes; KeyedVector<int32_t, Led> mLedsByScanCode; KeyedVector<int32_t, Led> mLedsByUsageCode; + std::string mLoadFileName; KeyLayoutMap(); diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 0a00241f8c..08ad8c6e5a 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -34,10 +34,10 @@ class KeyCharacterMap; class KeyMap { public: std::string keyLayoutFile; - sp<KeyLayoutMap> keyLayoutMap; + std::shared_ptr<KeyLayoutMap> keyLayoutMap; std::string keyCharacterMapFile; - sp<KeyCharacterMap> keyCharacterMap; + std::shared_ptr<KeyCharacterMap> keyCharacterMap; KeyMap(); ~KeyMap(); diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h new file mode 100644 index 0000000000..1d987fede2 --- /dev/null +++ b/include/input/NamedEnum.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/stringprintf.h> + +#include <array> +#include <cstdint> +#include <optional> +#include <string> + +#ifndef __UI_INPUT_NAMEDENUM_H +#define __UI_INPUT_NAMEDENUM_H + +namespace android { + +namespace details { +template <typename E, E V> +constexpr std::optional<std::string_view> enum_value_name() { + // Should look something like (but all on one line): + // std::optional<std::string_view> + // android::details::enum_value_name() + // [E = android::test::TestEnums, V = android::test::TestEnums::ONE] + std::string_view view = __PRETTY_FUNCTION__; + size_t templateStart = view.rfind("["); + size_t templateEnd = view.rfind("]"); + if (templateStart == std::string::npos || templateEnd == std::string::npos) { + return std::nullopt; + } + + // Extract the template parameters without the enclosing braces. + // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE + view = view.substr(templateStart + 1, templateEnd - templateStart - 1); + size_t valStart = view.rfind("V = "); + if (valStart == std::string::npos) { + return std::nullopt; + } + + // Example (cont'd): V = android::test::TestEnums::ONE + view = view.substr(valStart); + size_t nameStart = view.rfind("::"); + if (nameStart == std::string::npos) { + return std::nullopt; + } + + // Chop off the initial "::" + nameStart += 2; + return view.substr(nameStart); +} + +template <typename E, typename T, T... I> +constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) { + constexpr size_t count = seq.size(); + + std::array<E, count> values{}; + for (size_t i = 0, v = 0; v < count; ++i) { + values[v++] = static_cast<E>(T{0} + i); + } + + return values; +} + +template <typename E, std::size_t N> +inline constexpr auto enum_values = + generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{}); + +template <typename E, std::size_t N, std::size_t... I> +constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept { + return std::array<std::optional<std::string_view>, sizeof...(I)>{ + {enum_value_name<E, enum_values<E, N>[I]>()...}}; +} + +template <typename E, std::size_t N> +inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{}); + +} // namespace details + +class NamedEnum { +public: + // By default allowed enum value range is 0 ~ 7. + template <typename E> + static constexpr size_t max = 8; + + template <auto V> + static constexpr auto enum_name() { + using E = decltype(V); + return details::enum_value_name<E, V>(); + } + + template <typename E> + static constexpr std::optional<std::string_view> enum_name(E val) { + auto idx = static_cast<size_t>(val); + return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt; + } + + // Helper function for parsing enum value to string. + // Example : enum class TestEnums { ZERO = 0x0 }; + // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO". + // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16, + // it should be declared to specialized the maximum enum by below: + // template <> constexpr size_t NamedEnum::max<TestEnums> = 16; + // If the enum class definition is sparse and contains enum values starting from a large value, + // Do not specialize it to a large number to avoid performance issues. + // The recommended maximum enum number to specialize is 64. + template <typename E> + static const std::string string(E val, const char* fallbackFormat = "0x%08x") { + std::string result; + std::optional<std::string_view> enumString = enum_name(val); + result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val); + return result; + } +}; + +} // namespace android + +#endif // __UI_INPUT_NAMEDENUM_H
\ No newline at end of file diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h index 3d0433133c..451918bb46 100644 --- a/include/input/PropertyMap.h +++ b/include/input/PropertyMap.h @@ -17,6 +17,7 @@ #ifndef _UTILS_PROPERTY_MAP_H #define _UTILS_PROPERTY_MAP_H +#include <android-base/result.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/String8.h> @@ -78,7 +79,7 @@ public: inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; } /* Loads a property map from a file. */ - static status_t load(const String8& filename, PropertyMap** outMap); + static android::base::Result<std::unique_ptr<PropertyMap>> load(const char* filename); private: class Parser { diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 727865a933..886f1f7753 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -18,8 +18,8 @@ #define _LIBINPUT_VELOCITY_TRACKER_H #include <input/Input.h> -#include <utils/Timers.h> #include <utils/BitSet.h> +#include <utils/Timers.h> namespace android { @@ -30,6 +30,22 @@ class VelocityTrackerStrategy; */ class VelocityTracker { public: + enum class Strategy : int32_t { + DEFAULT = -1, + MIN = 0, + IMPULSE = 0, + LSQ1 = 1, + LSQ2 = 2, + LSQ3 = 3, + WLSQ2_DELTA = 4, + WLSQ2_CENTRAL = 5, + WLSQ2_RECENT = 6, + INT1 = 7, + INT2 = 8, + LEGACY = 9, + MAX = LEGACY, + }; + struct Position { float x, y; }; @@ -62,8 +78,8 @@ public: }; // Creates a velocity tracker using the specified strategy. - // If strategy is NULL, uses the default strategy for the platform. - VelocityTracker(const char* strategy = nullptr); + // If strategy is not provided, uses the default strategy for the platform. + VelocityTracker(const Strategy strategy = Strategy::DEFAULT); ~VelocityTracker(); @@ -80,7 +96,7 @@ public: // are included in the movement. // The positions array contains position information for each pointer in order by // increasing id. Its size should be equal to the number of one bits in idBits. - void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions); + void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions); // Adds movement information for all pointers in a MotionEvent, including historical samples. void addMovement(const MotionEvent* event); @@ -102,16 +118,21 @@ public: inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; } private: - static const char* DEFAULT_STRATEGY; + // The default velocity tracker strategy. + // Although other strategies are available for testing and comparison purposes, + // this is the strategy that applications will actually use. Be very careful + // when adjusting the default strategy because it can dramatically affect + // (often in a bad way) the user experience. + static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2; nsecs_t mLastEventTime; BitSet32 mCurrentPointerIdBits; int32_t mActivePointerId; - VelocityTrackerStrategy* mStrategy; + std::unique_ptr<VelocityTrackerStrategy> mStrategy; - bool configureStrategy(const char* strategy); + bool configureStrategy(const Strategy strategy); - static VelocityTrackerStrategy* createStrategy(const char* strategy); + static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy); }; @@ -128,7 +149,7 @@ public: virtual void clear() = 0; virtual void clearPointers(BitSet32 idBits) = 0; virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) = 0; + const std::vector<VelocityTracker::Position>& positions) = 0; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0; }; @@ -159,8 +180,8 @@ public: virtual void clear(); virtual void clearPointers(BitSet32 idBits); - virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions); + void addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -202,8 +223,8 @@ public: virtual void clear(); virtual void clearPointers(BitSet32 idBits); - virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions); + void addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -236,8 +257,8 @@ public: virtual void clear(); virtual void clearPointers(BitSet32 idBits); - virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions); + void addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: @@ -271,8 +292,8 @@ public: virtual void clear(); virtual void clearPointers(BitSet32 idBits); - virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions); + void addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) override; virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; private: diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h deleted file mode 100644 index 964e318584..0000000000 --- a/include/powermanager/IPowerManager.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_IPOWERMANAGER_H -#define ANDROID_IPOWERMANAGER_H - -#include <utils/Errors.h> -#include <binder/IInterface.h> -#include <hardware/power.h> - -namespace android { - -// ---------------------------------------------------------------------------- - -class IPowerManager : public IInterface -{ -public: - // These transaction IDs must be kept in sync with the method order from - // IPowerManager.aidl. - enum { - ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION, - ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1, - RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2, - UPDATE_WAKE_LOCK_UIDS = IBinder::FIRST_CALL_TRANSACTION + 3, - POWER_HINT = IBinder::FIRST_CALL_TRANSACTION + 4, - UPDATE_WAKE_LOCK_SOURCE = IBinder::FIRST_CALL_TRANSACTION + 5, - IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6, - USER_ACTIVITY = IBinder::FIRST_CALL_TRANSACTION + 7, - WAKE_UP = IBinder::FIRST_CALL_TRANSACTION + 8, - GO_TO_SLEEP = IBinder::FIRST_CALL_TRANSACTION + 9, - NAP = IBinder::FIRST_CALL_TRANSACTION + 10, - IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11, - IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12, - GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13, - SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14, - REBOOT = IBinder::FIRST_CALL_TRANSACTION + 21, - REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 22, - SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 23, - CRASH = IBinder::FIRST_CALL_TRANSACTION + 24, - }; - - DECLARE_META_INTERFACE(PowerManager) - - // The parcels created by these methods must be kept in sync with the - // corresponding methods from IPowerManager.aidl. - // FIXME remove the bool isOneWay parameters as they are not oneway in the .aidl - virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag, - const String16& packageName, bool isOneWay = false) = 0; - virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag, - const String16& packageName, int uid, bool isOneWay = false) = 0; - virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay = false) = 0; - virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids, - bool isOneWay = false) = 0; - virtual status_t powerHint(int hintId, int data) = 0; - virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) = 0; - virtual status_t reboot(bool confirm, const String16& reason, bool wait) = 0; - virtual status_t shutdown(bool confirm, const String16& reason, bool wait) = 0; - virtual status_t crash(const String16& message) = 0; -}; - -// ---------------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IPOWERMANAGER_H diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h new file mode 100644 index 0000000000..dd34c0a6c2 --- /dev/null +++ b/include/powermanager/PowerHalController.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_POWERHALCONTROLLER_H +#define ANDROID_POWERHALCONTROLLER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <powermanager/PowerHalWrapper.h> + +namespace android { + +namespace power { + +// ------------------------------------------------------------------------------------------------- + +// Connects to underlying Power HAL handles. +class HalConnector { +public: + HalConnector() = default; + virtual ~HalConnector() = default; + + virtual std::unique_ptr<HalWrapper> connect(); + virtual void reset(); +}; + +// ------------------------------------------------------------------------------------------------- + +// Controller for Power HAL handle. +// This relies on HalConnector to connect to the underlying Power HAL +// service and reconnects to it after each failed api call. This also ensures +// connecting to the service is thread-safe. +class PowerHalController : public HalWrapper { +public: + PowerHalController() : PowerHalController(std::make_unique<HalConnector>()) {} + explicit PowerHalController(std::unique_ptr<HalConnector> connector) + : mHalConnector(std::move(connector)) {} + virtual ~PowerHalController() = default; + + void init(); + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; + +private: + std::mutex mConnectedHalMutex; + std::unique_ptr<HalConnector> mHalConnector; + + // Shared pointers to keep global pointer and allow local copies to be used in + // different threads + std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex) = nullptr; + const std::shared_ptr<HalWrapper> mDefaultHal = std::make_shared<EmptyHalWrapper>(); + + std::shared_ptr<HalWrapper> initHal(); + HalResult processHalResult(HalResult result, const char* functionName); +}; + +// ------------------------------------------------------------------------------------------------- + +}; // namespace power + +}; // namespace android + +#endif // ANDROID_POWERHALCONTROLLER_H diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h new file mode 100644 index 0000000000..ed6f6f35f5 --- /dev/null +++ b/include/powermanager/PowerHalLoader.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_POWERHALLOADER_H +#define ANDROID_POWERHALLOADER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/IPower.h> + +namespace android { + +namespace power { + +// Loads available Power HAL services. +class PowerHalLoader { +public: + static void unloadAll(); + static sp<hardware::power::IPower> loadAidl(); + static sp<hardware::power::V1_0::IPower> loadHidlV1_0(); + static sp<hardware::power::V1_1::IPower> loadHidlV1_1(); + +private: + static std::mutex gHalMutex; + static sp<hardware::power::IPower> gHalAidl GUARDED_BY(gHalMutex); + static sp<hardware::power::V1_0::IPower> gHalHidlV1_0 GUARDED_BY(gHalMutex); + static sp<hardware::power::V1_1::IPower> gHalHidlV1_1 GUARDED_BY(gHalMutex); + + static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked() + EXCLUSIVE_LOCKS_REQUIRED(gHalMutex); + + PowerHalLoader() = delete; + ~PowerHalLoader() = delete; +}; + +}; // namespace power + +} // namespace android + +#endif // ANDROID_POWERHALLOADER_H diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h new file mode 100644 index 0000000000..c3e7601c29 --- /dev/null +++ b/include/powermanager/PowerHalWrapper.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_POWERHALWRAPPER_H +#define ANDROID_POWERHALWRAPPER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> + +namespace android { + +namespace power { + +// State of Power HAL support for individual apis. +enum class HalSupport { + UNKNOWN = 0, + ON = 1, + OFF = 2, +}; + +// State of the Power HAL api call result. +enum class HalResult { + SUCCESSFUL = 0, + FAILED = 1, + UNSUPPORTED = 2, +}; + +// Wrapper for Power HAL handlers. +class HalWrapper { +public: + virtual ~HalWrapper() = default; + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) = 0; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) = 0; +}; + +// Empty Power HAL wrapper that ignores all api calls. +class EmptyHalWrapper : public HalWrapper { +public: + EmptyHalWrapper() = default; + ~EmptyHalWrapper() = default; + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; +}; + +// Wrapper for the HIDL Power HAL v1.0. +class HidlHalWrapperV1_0 : public HalWrapper { +public: + explicit HidlHalWrapperV1_0(sp<hardware::power::V1_0::IPower> Hal) + : mHandleV1_0(std::move(Hal)) {} + virtual ~HidlHalWrapperV1_0() = default; + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; + +protected: + virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, uint32_t data); + +private: + sp<hardware::power::V1_0::IPower> mHandleV1_0; + HalResult setInteractive(bool enabled); + HalResult setFeature(hardware::power::V1_0::Feature feature, bool enabled); +}; + +// Wrapper for the HIDL Power HAL v1.1. +class HidlHalWrapperV1_1 : public HidlHalWrapperV1_0 { +public: + HidlHalWrapperV1_1(sp<hardware::power::V1_0::IPower> handleV1_0, + sp<hardware::power::V1_1::IPower> handleV1_1) + : HidlHalWrapperV1_0(std::move(handleV1_0)), mHandleV1_1(std::move(handleV1_1)) {} + virtual ~HidlHalWrapperV1_1() = default; + +protected: + virtual HalResult sendPowerHint(hardware::power::V1_0::PowerHint hintId, + uint32_t data) override; + +private: + sp<hardware::power::V1_1::IPower> mHandleV1_1; +}; + +// Wrapper for the AIDL Power HAL. +class AidlHalWrapper : public HalWrapper { +public: + explicit AidlHalWrapper(sp<hardware::power::IPower> handle) : mHandle(std::move(handle)) {} + virtual ~AidlHalWrapper() = default; + + virtual HalResult setBoost(hardware::power::Boost boost, int32_t durationMs) override; + virtual HalResult setMode(hardware::power::Mode mode, bool enabled) override; + +private: + // Control access to the boost and mode supported arrays. + std::mutex mBoostMutex; + std::mutex mModeMutex; + sp<hardware::power::IPower> mHandle; + // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT. + // Need to increase the array size if more boost supported. + std::array<std::atomic<HalSupport>, + static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1> + mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN}; + // Android framework only sends mode upto DISPLAY_INACTIVE. + // Need to increase the array if more mode supported. + std::array<std::atomic<HalSupport>, + static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1> + mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN}; +}; + +}; // namespace power + +}; // namespace android + +#endif // ANDROID_POWERHALWRAPPER_H diff --git a/include/ui/Rotation.h b/include/ui/Rotation.h new file mode 120000 index 0000000000..095d2cee88 --- /dev/null +++ b/include/ui/Rotation.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/Rotation.h
\ No newline at end of file diff --git a/include/ui/Transform.h b/include/ui/Transform.h new file mode 120000 index 0000000000..323f1fd1b4 --- /dev/null +++ b/include/ui/Transform.h @@ -0,0 +1 @@ +../../libs/ui/include/ui/Transform.h
\ No newline at end of file diff --git a/libs/attestation/Android.bp b/libs/attestation/Android.bp new file mode 100644 index 0000000000..b85aecd16d --- /dev/null +++ b/libs/attestation/Android.bp @@ -0,0 +1,31 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +cc_library_static { + name: "libattestation", + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + srcs: [ + "HmacKeyManager.cpp" + ], + + clang: true, + + shared_libs: [ + "liblog", + "libcrypto", + ], +}
\ No newline at end of file diff --git a/libs/attestation/HmacKeyManager.cpp b/libs/attestation/HmacKeyManager.cpp new file mode 100644 index 0000000000..b15f143014 --- /dev/null +++ b/libs/attestation/HmacKeyManager.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <attestation/HmacKeyManager.h> +#include <log/log.h> +#include <openssl/hmac.h> +#include <openssl/rand.h> + +namespace android { + +static std::array<uint8_t, 128> getRandomKey() { + std::array<uint8_t, 128> key; + if (RAND_bytes(key.data(), key.size()) != 1) { + LOG_ALWAYS_FATAL("Can't generate HMAC key"); + } + return key; +} + +HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {} + +std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const { + // SHA256 always generates 32-bytes result + std::array<uint8_t, 32> hash; + unsigned int hashLen = 0; + uint8_t* result = + HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen); + if (result == nullptr) { + ALOGE("Could not sign the data using HMAC"); + return INVALID_HMAC; + } + + if (hashLen != hash.size()) { + ALOGE("HMAC-SHA256 has unexpected length"); + return INVALID_HMAC; + } + + return hash; +} +} // namespace android
\ No newline at end of file diff --git a/libs/attestation/OWNERS b/libs/attestation/OWNERS new file mode 100644 index 0000000000..4dbb0eae5c --- /dev/null +++ b/libs/attestation/OWNERS @@ -0,0 +1,2 @@ +chaviw@google.com +svv@google.com
\ No newline at end of file diff --git a/libs/attestation/TEST_MAPPING b/libs/attestation/TEST_MAPPING new file mode 100644 index 0000000000..43be638d91 --- /dev/null +++ b/libs/attestation/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libattestation_tests" + } + ] +}
\ No newline at end of file diff --git a/libs/attestation/tests/Android.bp b/libs/attestation/tests/Android.bp new file mode 100644 index 0000000000..6ce5ea1b2d --- /dev/null +++ b/libs/attestation/tests/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libattestation_tests", + test_suites: ["device-tests"], + srcs: [ + "HmacKeyManager_test.cpp", + ], + static_libs: [ + "libattestation", + ], + shared_libs: [ + "liblog", + "libcrypto", + ], +} diff --git a/libs/attestation/tests/HmacKeyManager_test.cpp b/libs/attestation/tests/HmacKeyManager_test.cpp new file mode 100644 index 0000000000..7f7a408886 --- /dev/null +++ b/libs/attestation/tests/HmacKeyManager_test.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <attestation/HmacKeyManager.h> +#include <gtest/gtest.h> + +namespace android { + +class HmacKeyManagerTest : public testing::Test { +protected: + HmacKeyManager mHmacKeyManager; +}; + +/** + * Ensure that separate calls to sign the same data are generating the same key. + * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance + * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky + * tests. + */ +TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) { + std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8}; + + std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(data.data(), sizeof(data)); + std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(data.data(), sizeof(data)); + ASSERT_EQ(hmac1, hmac2); +} + +/** + * Ensure that changes in the hmac verification data produce a different hmac. + */ +TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) { + std::array<uint8_t, 10> data = {4, 3, 5, 1, 8, 5, 2, 7, 1, 8}; + std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(data.data(), sizeof(data)); + + data[2] = 2; + ASSERT_NE(initialHmac, mHmacKeyManager.sign(data.data(), sizeof(data))); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index 1c6b49135d..de42f36d74 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -22,6 +22,7 @@ #include <utils/SystemClock.h> #include <sys/types.h> +#include <private/android_filesystem_config.h> #ifdef LOG_TAG #undef LOG_TAG @@ -100,7 +101,7 @@ int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPa sp<IAppOpsService> service = getService(); int32_t mode = service != nullptr ? service->noteOperation(op, uid, callingPackage, attributionTag, - shouldCollectNotes(op), message) + shouldCollectNotes(op), message, uid == AID_SYSTEM) : AppOpsManager::MODE_IGNORED; return mode; @@ -118,7 +119,8 @@ int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& c sp<IAppOpsService> service = getService(); int32_t mode = service != nullptr ? service->startOperation(getClientId(), op, uid, callingPackage, - attributionTag, startIfModeDefault, shouldCollectNotes(op), message) + attributionTag, startIfModeDefault, shouldCollectNotes(op), message, + uid == AID_SYSTEM) : AppOpsManager::MODE_IGNORED; return mode; diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp index cd78866624..ee0cd6206e 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp @@ -50,15 +50,16 @@ public: virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, - const String16& message) { + const String16& message, bool shouldCollectMessage) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); data.writeInt32(uid); data.writeString16(packageName); data.writeString16(attributionTag); - data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0); + data.writeBool(shouldCollectAsyncNotedOp); data.writeString16(message); + data.writeBool(shouldCollectMessage); remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; @@ -67,7 +68,8 @@ public: virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag, - bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) { + bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message, + bool shouldCollectMessage) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); @@ -75,9 +77,10 @@ public: data.writeInt32(uid); data.writeString16(packageName); data.writeString16(attributionTag); - data.writeInt32(startIfModeDefault ? 1 : 0); - data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0); + data.writeBool(startIfModeDefault); + data.writeBool(shouldCollectAsyncNotedOp); data.writeString16(message); + data.writeBool(shouldCollectMessage); remote()->transact(START_OPERATION_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; @@ -186,10 +189,11 @@ status_t BnAppOpsService::onTransact( String16 packageName = data.readString16(); std::optional<String16> attributionTag; data.readString16(&attributionTag); - bool shouldCollectAsyncNotedOp = data.readInt32() == 1; + bool shouldCollectAsyncNotedOp = data.readBool(); String16 message = data.readString16(); + bool shouldCollectMessage = data.readBool(); int32_t res = noteOperation(code, uid, packageName, attributionTag, - shouldCollectAsyncNotedOp, message); + shouldCollectAsyncNotedOp, message, shouldCollectMessage); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; @@ -202,11 +206,12 @@ status_t BnAppOpsService::onTransact( String16 packageName = data.readString16(); std::optional<String16> attributionTag; data.readString16(&attributionTag); - bool startIfModeDefault = data.readInt32() == 1; - bool shouldCollectAsyncNotedOp = data.readInt32() == 1; + bool startIfModeDefault = data.readBool(); + bool shouldCollectAsyncNotedOp = data.readBool(); String16 message = data.readString16(); + bool shouldCollectMessage = data.readBool(); int32_t res = startOperation(token, code, uid, packageName, attributionTag, - startIfModeDefault, shouldCollectAsyncNotedOp, message); + startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp index 325e204203..2e15e50e94 100644 --- a/libs/binder/LazyServiceRegistrar.cpp +++ b/libs/binder/LazyServiceRegistrar.cpp @@ -29,16 +29,12 @@ namespace internal { using AidlServiceManager = android::os::IServiceManager; -class ClientCounterCallback : public ::android::os::BnClientCallback { +class ClientCounterCallbackImpl : public ::android::os::BnClientCallback { public: - ClientCounterCallback() : mNumConnectedServices(0), mForcePersist(false) {} + ClientCounterCallbackImpl() : mNumConnectedServices(0), mForcePersist(false) {} bool registerService(const sp<IBinder>& service, const std::string& name, bool allowIsolated, int dumpFlags); - - /** - * Set a flag to prevent services from automatically shutting down - */ void forcePersist(bool persist); protected: @@ -75,7 +71,23 @@ private: bool mForcePersist; }; -bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name, +class ClientCounterCallback { +public: + ClientCounterCallback(); + + bool registerService(const sp<IBinder>& service, const std::string& name, + bool allowIsolated, int dumpFlags); + + /** + * Set a flag to prevent services from automatically shutting down + */ + void forcePersist(bool persist); + +private: + sp<ClientCounterCallbackImpl> mImpl; +}; + +bool ClientCounterCallbackImpl::registerService(const sp<IBinder>& service, const std::string& name, bool allowIsolated, int dumpFlags) { auto manager = interface_cast<AidlServiceManager>(asBinder(defaultServiceManager())); @@ -89,7 +101,7 @@ bool ClientCounterCallback::registerService(const sp<IBinder>& service, const st } if (!reRegister) { - if (!manager->registerClientCallback(name, service, this).isOk()) { + if(!manager->registerClientCallback(name, service, this).isOk()) { ALOGE("Failed to add client callback for service %s", name.c_str()); return false; } @@ -105,7 +117,7 @@ bool ClientCounterCallback::registerService(const sp<IBinder>& service, const st return true; } -std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) { +std::map<std::string, ClientCounterCallbackImpl::Service>::iterator ClientCounterCallbackImpl::assertRegisteredService(const sp<IBinder>& service) { LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service"); for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) { auto const& [name, registered] = *it; @@ -117,7 +129,7 @@ std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCal __builtin_unreachable(); } -void ClientCounterCallback::forcePersist(bool persist) { +void ClientCounterCallbackImpl::forcePersist(bool persist) { mForcePersist = persist; if(!mForcePersist) { // Attempt a shutdown in case the number of clients hit 0 while the flag was on @@ -129,7 +141,7 @@ void ClientCounterCallback::forcePersist(bool persist) { * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple * invocations could occur on different threads however. */ -Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) { +Status ClientCounterCallbackImpl::onClients(const sp<IBinder>& service, bool clients) { auto & [name, registered] = *assertRegisteredService(service); if (registered.clients == clients) { LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has " @@ -154,7 +166,7 @@ Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients return Status::ok(); } -void ClientCounterCallback::tryShutdown() { +void ClientCounterCallbackImpl::tryShutdown() { if(mNumConnectedServices > 0) { // Should only shut down if there are no clients return; @@ -175,7 +187,6 @@ void ClientCounterCallback::tryShutdown() { bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk(); - if (!success) { ALOGI("Failed to unregister service %s", entry.first.c_str()); break; @@ -200,6 +211,19 @@ void ClientCounterCallback::tryShutdown() { } } +ClientCounterCallback::ClientCounterCallback() { + mImpl = sp<ClientCounterCallbackImpl>::make(); +} + +bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name, + bool allowIsolated, int dumpFlags) { + return mImpl->registerService(service, name, allowIsolated, dumpFlags); +} + +void ClientCounterCallback::forcePersist(bool persist) { + mImpl->forcePersist(persist); +} + } // namespace internal LazyServiceRegistrar::LazyServiceRegistrar() { diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index ddd9f9b212..4381386074 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -1068,6 +1068,7 @@ status_t Parcel::writeString8(const char* str, size_t len) { if (str == nullptr) return writeInt32(-1); + // NOTE: Keep this logic in sync with android_os_Parcel.cpp status_t err = writeInt32(len); if (err == NO_ERROR) { uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char)); @@ -1108,6 +1109,7 @@ status_t Parcel::writeString16(const char16_t* str, size_t len) { if (str == nullptr) return writeInt32(-1); + // NOTE: Keep this logic in sync with android_os_Parcel.cpp status_t err = writeInt32(len); if (err == NO_ERROR) { len *= sizeof(char16_t); diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h index a4a20c8b10..de7d12f210 100644 --- a/libs/binder/include/binder/IAppOpsService.h +++ b/libs/binder/include/binder/IAppOpsService.h @@ -39,10 +39,11 @@ public: virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0; virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, - const String16& message) = 0; + const String16& message, bool shouldCollectMessage) = 0; virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag, - bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0; + bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message, + bool shouldCollectMessage) = 0; virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, const String16& packageName, const std::optional<String16>& attributionTag) = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 7116154951..468cc163dc 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -234,6 +234,7 @@ constexpr const char* const kManualInterfaces[] = { "android.gui.IGraphicBufferConsumer", "android.gui.IRegionSamplingListener", "android.gui.ITransactionComposerListener", + "android.gui.IScreenCaptureListener", "android.gui.SensorEventConnection", "android.gui.SensorServer", "android.hardware.ICamera", @@ -247,8 +248,6 @@ constexpr const char* const kManualInterfaces[] = { "android.hardware.ISoundTriggerHwService", "android.hardware.IStreamListener", "android.hardware.IStreamSource", - "android.input.IInputFlinger", - "android.input.ISetInputWindowsListener", "android.media.IAudioFlinger", "android.media.IAudioFlingerClient", "android.media.IAudioPolicyService", @@ -257,8 +256,6 @@ constexpr const char* const kManualInterfaces[] = { "android.media.IAudioTrack", "android.media.IDataSource", "android.media.IDrmClient", - "android.media.IEffect", - "android.media.IEffectClient", "android.media.IMediaCodecList", "android.media.IMediaDrmService", "android.media.IMediaExtractor", @@ -282,7 +279,6 @@ constexpr const char* const kManualInterfaces[] = { "android.os.IComplexTypeInterface", "android.os.IPermissionController", "android.os.IPingResponder", - "android.os.IPowerManager", "android.os.IProcessInfoService", "android.os.ISchedulingPolicyService", "android.os.IStringConstants", diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 2a2eed7879..4610ba9b11 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -181,7 +181,7 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce binder_status_t status = getClass()->onTransact(this, code, &in, &out); return PruneStatusT(status); - } else if (code == SHELL_COMMAND_TRANSACTION) { + } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) { int in = data.readFileDescriptor(); int out = data.readFileDescriptor(); int err = data.readFileDescriptor(); diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index f60112787d..0fa47c64c7 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -115,13 +115,13 @@ struct AIBinder_Class { const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; } // required to be non-null, implemented for every class - const AIBinder_Class_onCreate onCreate; - const AIBinder_Class_onDestroy onDestroy; - const AIBinder_Class_onTransact onTransact; + const AIBinder_Class_onCreate onCreate = nullptr; + const AIBinder_Class_onDestroy onDestroy = nullptr; + const AIBinder_Class_onTransact onTransact = nullptr; // optional methods for a class - AIBinder_onDump onDump; - AIBinder_handleShellCommand handleShellCommand; + AIBinder_onDump onDump = nullptr; + AIBinder_handleShellCommand handleShellCommand = nullptr; private: // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp index 64832f3081..a5889856fc 100644 --- a/libs/binder/ndk/tests/iface.cpp +++ b/libs/binder/ndk/tests/iface.cpp @@ -118,7 +118,7 @@ IFoo::~IFoo() { AIBinder_Weak_delete(mWeakBinder); } -binder_status_t IFoo::addService(const char* instance) { +AIBinder* IFoo::getBinder() { AIBinder* binder = nullptr; if (mWeakBinder != nullptr) { @@ -132,8 +132,18 @@ binder_status_t IFoo::addService(const char* instance) { AIBinder_Weak_delete(mWeakBinder); } mWeakBinder = AIBinder_Weak_new(binder); + + // WARNING: it is important that this class does not implement debug or + // shell functions because it does not use special C++ wrapper + // functions, and so this is how we test those functions. } + return binder; +} + +binder_status_t IFoo::addService(const char* instance) { + AIBinder* binder = getBinder(); + binder_status_t status = AServiceManager_addService(binder, instance); // Strong references we care about kept by remote process AIBinder_decStrong(binder); diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h index cdf5493216..d9dd64b8a6 100644 --- a/libs/binder/ndk/tests/include/iface/iface.h +++ b/libs/binder/ndk/tests/include/iface/iface.h @@ -30,6 +30,9 @@ class IFoo : public virtual ::android::RefBase { static AIBinder_Class* kClass; + // binder representing this interface with one reference count + AIBinder* getBinder(); + // Takes ownership of IFoo binder_status_t addService(const char* instance); static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr); diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 44d8ebf048..160b9f2d73 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -185,6 +185,26 @@ TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder_decStrong(binder); } +TEST(NdkBinder, UnimplementedDump) { + sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); + ASSERT_NE(foo, nullptr); + AIBinder* binder = foo->getBinder(); + EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0)); + AIBinder_decStrong(binder); +} + +TEST(NdkBinder, UnimplementedShell) { + // libbinder_ndk doesn't support calling shell, so we are calling from the + // libbinder across processes to the NDK service which doesn't implement + // shell + static const sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<IBinder> testService = sm->getService(String16(IFoo::kSomeInstanceName)); + + Vector<String16> argsVec; + EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, + argsVec, nullptr, nullptr)); +} + TEST(NdkBinder, DoubleNumber) { sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 2e144084c1..143fa1397a 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -33,6 +33,7 @@ static const char* native_processes_to_dump[] = { "/system/bin/mediaextractor", // media.extractor "/system/bin/mediametrics", // media.metrics "/system/bin/mediaserver", + "/system/bin/mediatranscoding", // media.transcoding "/system/bin/netd", "/system/bin/sdcard", "/apex/com.android.os.statsd/bin/statsd", @@ -56,6 +57,7 @@ static const char* hal_interfaces_to_dump[] { "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", + "android.hardware.audio@7.0::IDevicesFactory", "android.hardware.automotive.audiocontrol@1.0::IAudioControl", "android.hardware.automotive.audiocontrol@2.0::IAudioControl", "android.hardware.automotive.evs@1.0::IEvsCamera", @@ -86,7 +88,7 @@ static std::set<const std::string> extra_hal_interfaces_to_dump; static void read_extra_hals_to_dump_from_property() { // extra hals to dump are already filled - if (extra_hal_interfaces_to_dump.size() > 0) { + if (!extra_hal_interfaces_to_dump.empty()) { return; } std::string value = android::base::GetProperty("ro.dump.hals.extra", ""); diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp new file mode 100644 index 0000000000..fb3238215c --- /dev/null +++ b/libs/ftl/Android.bp @@ -0,0 +1,17 @@ +cc_test { + name: "ftl_test", + test_suites: ["device-tests"], + sanitize: { + address: true, + }, + srcs: [ + "SmallVector_test.cpp", + "StaticVector_test.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + "-Wpedantic", + ], +} diff --git a/libs/ftl/SmallVector_test.cpp b/libs/ftl/SmallVector_test.cpp new file mode 100644 index 0000000000..c41e622bcc --- /dev/null +++ b/libs/ftl/SmallVector_test.cpp @@ -0,0 +1,455 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ftl/SmallVector.h> +#include <gtest/gtest.h> + +#include <algorithm> +#include <iterator> +#include <string> +#include <utility> + +using namespace std::string_literals; + +namespace android::test { + +using ftl::SmallVector; + +// Keep in sync with example usage in header file. +TEST(SmallVector, Example) { + ftl::SmallVector<char, 3> vector; + EXPECT_TRUE(vector.empty()); + EXPECT_FALSE(vector.dynamic()); + + vector = {'a', 'b', 'c'}; + EXPECT_EQ(vector.size(), 3u); + EXPECT_FALSE(vector.dynamic()); + + vector.push_back('d'); + EXPECT_TRUE(vector.dynamic()); + + vector.unstable_erase(vector.begin()); + EXPECT_EQ(vector, (ftl::SmallVector{'d', 'b', 'c'})); + + vector.pop_back(); + EXPECT_EQ(vector.back(), 'b'); + EXPECT_TRUE(vector.dynamic()); + + const char array[] = "hi"; + vector = ftl::SmallVector(array); + EXPECT_EQ(vector, (ftl::SmallVector{'h', 'i', '\0'})); + EXPECT_FALSE(vector.dynamic()); +} + +TEST(SmallVector, Construct) { + { + // Default constructor. + SmallVector<std::string, 2> vector; + + EXPECT_TRUE(vector.empty()); + EXPECT_FALSE(vector.dynamic()); + } + { + // Array constructor. + const float kFloats[] = {.1f, .2f, .3f}; + SmallVector vector(kFloats); + + EXPECT_EQ(vector, (SmallVector{.1f, .2f, .3f})); + EXPECT_FALSE(vector.dynamic()); + } + { + // Iterator constructor. + const char chars[] = "abcdef"; + std::string string(chars); + SmallVector<char, sizeof(chars)> vector(string.begin(), string.end()); + + EXPECT_STREQ(vector.begin(), chars); + EXPECT_FALSE(vector.dynamic()); + } + { + // Variadic constructor with same types. + SmallVector vector = {1, 2, 3}; + + static_assert(std::is_same_v<decltype(vector), SmallVector<int, 3>>); + EXPECT_EQ(vector, (SmallVector{1, 2, 3})); + EXPECT_FALSE(vector.dynamic()); + } + { + // Variadic constructor with different types. + const auto copy = "quince"s; + auto move = "tart"s; + SmallVector vector = {copy, std::move(move)}; + + static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 2>>); + EXPECT_EQ(vector, (SmallVector{"quince"s, "tart"s})); + EXPECT_FALSE(vector.dynamic()); + } + { + // In-place constructor with same types. + SmallVector vector(std::in_place_type<std::string>, "red", "velvet", "cake"); + + static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>); + EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s})); + EXPECT_FALSE(vector.dynamic()); + } + { + // In-place constructor with different types. + const auto copy = "red"s; + auto move = "velvet"s; + std::initializer_list<char> list = {'c', 'a', 'k', 'e'}; + SmallVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list); + + static_assert(std::is_same_v<decltype(vector), SmallVector<std::string, 3>>); + EXPECT_EQ(vector, (SmallVector{"red"s, "velvet"s, "cake"s})); + EXPECT_FALSE(vector.dynamic()); + } + { + // Conversion from StaticVector. + ftl::StaticVector doubles = {.1, .2, .3}; + SmallVector vector = std::move(doubles); + EXPECT_TRUE(doubles.empty()); + + static_assert(std::is_same_v<decltype(vector), SmallVector<double, 3>>); + EXPECT_EQ(vector, (SmallVector{.1, .2, .3})); + EXPECT_FALSE(vector.dynamic()); + } +} + +TEST(SmallVector, String) { + SmallVector<char, 10> chars; + char c = 'a'; + std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; }); + chars.push_back('\0'); + + EXPECT_TRUE(chars.dynamic()); + EXPECT_EQ(chars.size(), 11u); + EXPECT_STREQ(chars.begin(), "abcdefghij"); + + // Constructor takes iterator range. + const char kString[] = "123456"; + SmallVector<char, 10> string(std::begin(kString), std::end(kString)); + + EXPECT_FALSE(string.dynamic()); + EXPECT_STREQ(string.begin(), "123456"); + EXPECT_EQ(string.size(), 7u); + + // Similar to emplace, but replaces rather than inserts. + string.replace(string.begin() + 5, '\0'); + EXPECT_STREQ(string.begin(), "12345"); + + swap(chars, string); + + EXPECT_STREQ(chars.begin(), "12345"); + EXPECT_STREQ(string.begin(), "abcdefghij"); + + EXPECT_FALSE(chars.dynamic()); + EXPECT_TRUE(string.dynamic()); +} + +TEST(SmallVector, CopyableElement) { + struct Pair { + // Needed because std::vector emplace does not use uniform initialization. + Pair(int a, int b) : a(a), b(b) {} + + const int a, b; + bool operator==(Pair p) const { return p.a == a && p.b == b; } + }; + + SmallVector<Pair, 5> pairs; + + EXPECT_TRUE(pairs.empty()); + EXPECT_EQ(pairs.max_size(), 5u); + + for (size_t i = 0; i < pairs.max_size(); ++i) { + EXPECT_EQ(pairs.size(), i); + + const int a = static_cast<int>(i) * 2; + EXPECT_EQ(pairs.emplace_back(a, a + 1), Pair(a, a + 1)); + } + + EXPECT_EQ(pairs.size(), 5u); + EXPECT_FALSE(pairs.dynamic()); + + // The vector is promoted when full. + EXPECT_EQ(pairs.emplace_back(10, 11), Pair(10, 11)); + EXPECT_TRUE(pairs.dynamic()); + + EXPECT_EQ(pairs, + (SmallVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}, + Pair{10, 11}})); + + // Constructor takes at most N elements. + SmallVector<int, 6> sums = {0, 0, 0, 0, 0, 0}; + EXPECT_FALSE(sums.dynamic()); + + // Random-access iterators comply with standard. + std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; }); + EXPECT_EQ(sums, (SmallVector{1, 5, 9, 13, 17, 21})); + + sums.pop_back(); + std::reverse(sums.begin(), sums.end()); + + EXPECT_EQ(sums, (SmallVector{17, 13, 9, 5, 1})); +} + +TEST(SmallVector, MovableElement) { + // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the + // element type would be deduced from the first element, i.e. const char*. + SmallVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", ""); + strings.pop_back(); + + EXPECT_EQ(strings.max_size(), 7u); + EXPECT_EQ(strings.size(), 6u); + + // Erase "cake" and append a substring copy. + { + const auto it = std::find_if(strings.begin(), strings.end(), + [](const auto& s) { return !s.empty(); }); + ASSERT_FALSE(it == strings.end()); + EXPECT_EQ(*it, "cake"); + + strings.unstable_erase(it); + EXPECT_EQ(strings.emplace_back("cakewalk", 4u), "cake"s); + } + + strings[1] = "quince"s; + + // Replace last empty string with "tart". + { + const auto rit = std::find(strings.rbegin(), strings.rend(), std::string()); + ASSERT_FALSE(rit == strings.rend()); + + std::initializer_list<char> list = {'t', 'a', 'r', 't'}; + strings.replace(rit.base() - 1, list); + } + + strings.front().assign("pie"); + + EXPECT_EQ(strings, (SmallVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s})); + + strings.push_back("nougat"); + strings.push_back("oreo"); + EXPECT_TRUE(strings.dynamic()); + + std::rotate(strings.begin(), strings.end() - 2, strings.end()); + + EXPECT_EQ(strings, + (SmallVector{"nougat"s, "oreo"s, "pie"s, "quince"s, "tart"s, "red"s, "velvet"s, + "cake"s})); +} + +TEST(SmallVector, Replace) { + // Replacing does not require a copy/move assignment operator. + struct Word { + explicit Word(std::string str) : str(std::move(str)) {} + const std::string str; + + bool operator==(const Word& other) const { return other.str == str; } + }; + + SmallVector words(std::in_place_type<Word>, "colored", "velour"); + + // The replaced element can be referenced by the replacement. + { + const Word& word = words.replace(words.last(), words.back().str.substr(0, 3) + "vet"); + EXPECT_EQ(word, Word("velvet")); + } + + // The vector is not promoted if replacing while full. + EXPECT_FALSE(words.dynamic()); + + words.emplace_back("cake"); + EXPECT_TRUE(words.dynamic()); + + { + const Word& word = words.replace(words.begin(), words.front().str.substr(4)); + EXPECT_EQ(word, Word("red")); + } + + EXPECT_EQ(words, (SmallVector{Word("red"), Word("velvet"), Word("cake")})); +} + +TEST(SmallVector, ReverseAppend) { + SmallVector strings = {"red"s, "velvet"s, "cake"s}; + EXPECT_FALSE(strings.dynamic()); + + auto rit = strings.rbegin(); + while (rit != strings.rend()) { + // Iterator and reference are invalidated on insertion. + const auto i = std::distance(strings.begin(), rit.base()); + std::string s = *rit; + + strings.push_back(std::move(s)); + rit = std::make_reverse_iterator(strings.begin() + i) + 1; + } + + EXPECT_EQ(strings, (SmallVector{"red"s, "velvet"s, "cake"s, "cake"s, "velvet"s, "red"s})); + EXPECT_TRUE(strings.dynamic()); +} + +TEST(SmallVector, Sort) { + SmallVector strings(std::in_place_type<std::string>, "pie", "quince", "tart", "red", "velvet"); + strings.push_back("cake"s); + + auto sorted = std::move(strings); + EXPECT_TRUE(strings.empty()); + + EXPECT_TRUE(sorted.dynamic()); + EXPECT_TRUE(strings.dynamic()); + + std::sort(sorted.begin(), sorted.end()); + EXPECT_EQ(sorted, (SmallVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s})); + + // Constructor takes array reference. + { + const char* kStrings[] = {"cake", "lie"}; + strings = SmallVector(kStrings); + EXPECT_FALSE(strings.dynamic()); + } + + EXPECT_GT(sorted, strings); + swap(sorted, strings); + EXPECT_LT(sorted, strings); + + EXPECT_FALSE(sorted.dynamic()); + EXPECT_TRUE(strings.dynamic()); + + // Append remaining elements, such that "pie" is the only difference. + for (const char* str : {"quince", "red", "tart", "velvet"}) { + sorted.emplace_back(str); + } + EXPECT_TRUE(sorted.dynamic()); + + EXPECT_NE(sorted, strings); + + // Replace second element with "pie". + const auto it = sorted.begin() + 1; + EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie"); + + EXPECT_EQ(sorted, strings); +} + +namespace { + +struct DestroyCounts { + DestroyCounts(int& live, int& dead) : counts{live, dead} {} + DestroyCounts(const DestroyCounts& other) : counts(other.counts) {} + DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; } + ~DestroyCounts() { ++(alive ? counts.live : counts.dead); } + + struct { + int& live; + int& dead; + } counts; + + bool alive = true; +}; + +void swap(DestroyCounts& lhs, DestroyCounts& rhs) { + std::swap(lhs.alive, rhs.alive); +} + +} // namespace + +TEST(SmallVector, Destroy) { + int live = 0; + int dead = 0; + + { SmallVector<DestroyCounts, 3> counts; } + EXPECT_EQ(0, live); + EXPECT_EQ(0, dead); + + { + SmallVector<DestroyCounts, 3> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + EXPECT_FALSE(counts.dynamic()); + } + EXPECT_EQ(3, live); + EXPECT_EQ(0, dead); + + live = 0; + { + SmallVector<DestroyCounts, 3> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + EXPECT_TRUE(counts.dynamic()); + } + EXPECT_EQ(4, live); + EXPECT_EQ(3, dead); + + live = dead = 0; + { + SmallVector<DestroyCounts, 2> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + auto copy = counts; + EXPECT_TRUE(copy.dynamic()); + } + EXPECT_EQ(6, live); + EXPECT_EQ(2, dead); + + live = dead = 0; + { + SmallVector<DestroyCounts, 2> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + auto move = std::move(counts); + EXPECT_TRUE(move.dynamic()); + } + EXPECT_EQ(3, live); + EXPECT_EQ(2, dead); + + live = dead = 0; + { + SmallVector<DestroyCounts, 2> counts1; + counts1.emplace_back(live, dead); + counts1.emplace_back(live, dead); + counts1.emplace_back(live, dead); + + EXPECT_TRUE(counts1.dynamic()); + EXPECT_EQ(2, dead); + dead = 0; + + SmallVector<DestroyCounts, 2> counts2; + counts2.emplace_back(live, dead); + + EXPECT_FALSE(counts2.dynamic()); + + swap(counts1, counts2); + + EXPECT_FALSE(counts1.dynamic()); + EXPECT_TRUE(counts2.dynamic()); + + EXPECT_EQ(0, live); + EXPECT_EQ(1, dead); + + dead = 0; + } + EXPECT_EQ(4, live); + EXPECT_EQ(0, dead); +} + +} // namespace android::test diff --git a/libs/ftl/StaticVector_test.cpp b/libs/ftl/StaticVector_test.cpp new file mode 100644 index 0000000000..dd5ce35ebb --- /dev/null +++ b/libs/ftl/StaticVector_test.cpp @@ -0,0 +1,391 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ftl/StaticVector.h> +#include <gtest/gtest.h> + +#include <algorithm> +#include <iterator> +#include <string> +#include <utility> + +using namespace std::string_literals; + +namespace android::test { + +using ftl::StaticVector; + +// Keep in sync with example usage in header file. +TEST(StaticVector, Example) { + ftl::StaticVector<char, 3> vector; + EXPECT_TRUE(vector.empty()); + + vector = {'a', 'b'}; + EXPECT_EQ(vector.size(), 2u); + + vector.push_back('c'); + EXPECT_TRUE(vector.full()); + + EXPECT_FALSE(vector.push_back('d')); + EXPECT_EQ(vector.size(), 3u); + + vector.unstable_erase(vector.begin()); + EXPECT_EQ(vector, (ftl::StaticVector{'c', 'b'})); + + vector.pop_back(); + EXPECT_EQ(vector.back(), 'c'); + + const char array[] = "hi"; + vector = ftl::StaticVector(array); + EXPECT_EQ(vector, (ftl::StaticVector{'h', 'i', '\0'})); +} + +TEST(StaticVector, Construct) { + { + // Default constructor. + StaticVector<std::string, 2> vector; + EXPECT_TRUE(vector.empty()); + } + { + // Array constructor. + const float kFloats[] = {.1f, .2f, .3f}; + StaticVector vector(kFloats); + EXPECT_EQ(vector, (StaticVector{.1f, .2f, .3f})); + } + { + // Iterator constructor. + const char chars[] = "abcdef"; + std::string string(chars); + StaticVector<char, sizeof(chars)> vector(string.begin(), string.end()); + + EXPECT_STREQ(vector.begin(), chars); + } + { + // Variadic constructor with same types. + StaticVector vector = {1, 2, 3}; + + static_assert(std::is_same_v<decltype(vector), StaticVector<int, 3>>); + EXPECT_EQ(vector, (StaticVector{1, 2, 3})); + } + { + // Variadic constructor with different types. + const auto copy = "quince"s; + auto move = "tart"s; + StaticVector vector = {copy, std::move(move)}; + + static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 2>>); + EXPECT_EQ(vector, (StaticVector{"quince"s, "tart"s})); + } + { + // In-place constructor with same types. + StaticVector vector(std::in_place_type<std::string>, "red", "velvet", "cake"); + + static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>); + EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s})); + } + { + // In-place constructor with different types. + const auto copy = "red"s; + auto move = "velvet"s; + std::initializer_list<char> list = {'c', 'a', 'k', 'e'}; + StaticVector vector(std::in_place_type<std::string>, copy.c_str(), std::move(move), list); + + static_assert(std::is_same_v<decltype(vector), StaticVector<std::string, 3>>); + EXPECT_EQ(vector, (StaticVector{"red"s, "velvet"s, "cake"s})); + } + { + struct String { + explicit String(const char* str) : str(str) {} + explicit String(const char** ptr) : str(*ptr) {} + const char* str; + }; + + const char* kStrings[] = {"a", "b", "c", "d"}; + + { + // Two iterator-like elements. + StaticVector<String, 3> vector(kStrings, kStrings + 3); + ASSERT_EQ(vector.size(), 2u); + + EXPECT_STREQ(vector[0].str, "a"); + EXPECT_STREQ(vector[1].str, "d"); + } + { + // Disambiguating iterator constructor. + StaticVector<String, 3> vector(ftl::IteratorRange, kStrings, kStrings + 3); + ASSERT_EQ(vector.size(), 3u); + + EXPECT_STREQ(vector[0].str, "a"); + EXPECT_STREQ(vector[1].str, "b"); + EXPECT_STREQ(vector[2].str, "c"); + } + } +} + +TEST(StaticVector, String) { + StaticVector<char, 10> chars; + char c = 'a'; + std::generate_n(std::back_inserter(chars), chars.max_size(), [&c] { return c++; }); + chars.back() = '\0'; + + EXPECT_STREQ(chars.begin(), "abcdefghi"); + + // Constructor takes iterator range. + const char kString[] = "123456"; + StaticVector<char, 10> string(std::begin(kString), std::end(kString)); + + EXPECT_STREQ(string.begin(), "123456"); + EXPECT_EQ(string.size(), 7u); + + // Similar to emplace, but replaces rather than inserts. + string.replace(string.begin() + 5, '\0'); + EXPECT_STREQ(string.begin(), "12345"); + + swap(chars, string); + + EXPECT_STREQ(chars.begin(), "12345"); + EXPECT_STREQ(string.begin(), "abcdefghi"); +} + +TEST(StaticVector, CopyableElement) { + struct Pair { + const int a, b; + bool operator==(Pair p) const { return p.a == a && p.b == b; } + }; + + StaticVector<Pair, 5> pairs; + + EXPECT_TRUE(pairs.empty()); + EXPECT_EQ(pairs.max_size(), 5u); + + for (size_t i = 0; i < pairs.max_size(); ++i) { + EXPECT_EQ(pairs.size(), i); + + const int a = static_cast<int>(i) * 2; + const auto it = pairs.emplace_back(a, a + 1); + ASSERT_NE(it, pairs.end()); + EXPECT_EQ(*it, (Pair{a, a + 1})); + } + + EXPECT_TRUE(pairs.full()); + EXPECT_EQ(pairs.size(), 5u); + + // Insertion fails if the vector is full. + const auto it = pairs.emplace_back(10, 11); + EXPECT_EQ(it, pairs.end()); + + EXPECT_EQ(pairs, (StaticVector{Pair{0, 1}, Pair{2, 3}, Pair{4, 5}, Pair{6, 7}, Pair{8, 9}})); + + // Constructor takes at most N elements. + StaticVector<int, 6> sums = {0, 0, 0, 0, 0, -1}; + EXPECT_TRUE(sums.full()); + + // Random-access iterators comply with standard. + std::transform(pairs.begin(), pairs.end(), sums.begin(), [](Pair p) { return p.a + p.b; }); + EXPECT_EQ(sums, (StaticVector{1, 5, 9, 13, 17, -1})); + + sums.pop_back(); + std::reverse(sums.begin(), sums.end()); + + EXPECT_EQ(sums, (StaticVector{17, 13, 9, 5, 1})); +} + +TEST(StaticVector, MovableElement) { + // Construct std::string elements in-place from C-style strings. Without std::in_place_type, the + // element type would be deduced from the first element, i.e. const char*. + StaticVector strings(std::in_place_type<std::string>, "", "", "", "cake", "velvet", "red", ""); + strings.pop_back(); + + EXPECT_EQ(strings.max_size(), 7u); + EXPECT_EQ(strings.size(), 6u); + + // Erase "cake" and append a substring copy. + { + auto it = std::find_if(strings.begin(), strings.end(), + [](const auto& s) { return !s.empty(); }); + ASSERT_FALSE(it == strings.end()); + EXPECT_EQ(*it, "cake"); + + strings.unstable_erase(it); + + // Construct std::string from first 4 characters of C-style string. + it = strings.emplace_back("cakewalk", 4u); + ASSERT_NE(it, strings.end()); + EXPECT_EQ(*it, "cake"s); + } + + strings[1] = "quince"s; + + // Replace last empty string with "tart". + { + const auto rit = std::find(strings.rbegin(), strings.rend(), std::string()); + ASSERT_FALSE(rit == strings.rend()); + + std::initializer_list<char> list = {'t', 'a', 'r', 't'}; + strings.replace(rit.base() - 1, list); + } + + strings.front().assign("pie"); + + EXPECT_EQ(strings, (StaticVector{"pie"s, "quince"s, "tart"s, "red"s, "velvet"s, "cake"s})); +} + +TEST(StaticVector, Replace) { + // Replacing does not require a copy/move assignment operator. + struct Word { + explicit Word(std::string str) : str(std::move(str)) {} + const std::string str; + }; + + StaticVector words(std::in_place_type<Word>, "red", "velour", "cake"); + + // The replaced element can be referenced by the replacement. + const auto it = words.begin() + 1; + const Word& word = words.replace(it, it->str.substr(0, 3) + "vet"); + EXPECT_EQ(word.str, "velvet"); +} + +TEST(StaticVector, ReverseTruncate) { + StaticVector<std::string, 10> strings("pie", "quince", "tart", "red", "velvet", "cake"); + EXPECT_FALSE(strings.full()); + + for (auto it = strings.begin(); it != strings.end(); ++it) { + strings.replace(it, strings.back()); + strings.pop_back(); + } + + EXPECT_EQ(strings, (StaticVector{"cake"s, "velvet"s, "red"s})); +} + +TEST(StaticVector, Sort) { + StaticVector<std::string, 7> strings("pie", "quince", "tart", "red", "velvet", "cake"); + EXPECT_FALSE(strings.full()); + + auto sorted = std::move(strings); + EXPECT_TRUE(strings.empty()); + + std::sort(sorted.begin(), sorted.end()); + EXPECT_EQ(sorted, (StaticVector{"cake"s, "pie"s, "quince"s, "red"s, "tart"s, "velvet"s})); + + // Constructor takes array reference. + { + const char* kStrings[] = {"cake", "lie"}; + strings = StaticVector(kStrings); + } + + EXPECT_GT(sorted, strings); + swap(sorted, strings); + EXPECT_LT(sorted, strings); + + // Append remaining elements, such that "pie" is the only difference. + for (const char* str : {"quince", "red", "tart", "velvet"}) { + sorted.emplace_back(str); + } + + EXPECT_NE(sorted, strings); + + // Replace second element with "pie". + const auto it = sorted.begin() + 1; + EXPECT_EQ(sorted.replace(it, 'p' + it->substr(1)), "pie"); + + EXPECT_EQ(sorted, strings); +} + +namespace { + +struct DestroyCounts { + DestroyCounts(int& live, int& dead) : counts{live, dead} {} + DestroyCounts(const DestroyCounts& other) : counts(other.counts) {} + DestroyCounts(DestroyCounts&& other) : counts(other.counts) { other.alive = false; } + ~DestroyCounts() { ++(alive ? counts.live : counts.dead); } + + struct { + int& live; + int& dead; + } counts; + + bool alive = true; +}; + +void swap(DestroyCounts& lhs, DestroyCounts& rhs) { + std::swap(lhs.alive, rhs.alive); +} + +} // namespace + +TEST(StaticVector, Destroy) { + int live = 0; + int dead = 0; + + { StaticVector<DestroyCounts, 5> counts; } + EXPECT_EQ(0, live); + EXPECT_EQ(0, dead); + + { + StaticVector<DestroyCounts, 5> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + } + EXPECT_EQ(3, live); + EXPECT_EQ(0, dead); + + live = 0; + { + StaticVector<DestroyCounts, 5> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + auto copy = counts; + } + EXPECT_EQ(6, live); + EXPECT_EQ(0, dead); + + live = 0; + { + StaticVector<DestroyCounts, 5> counts; + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + counts.emplace_back(live, dead); + + auto move = std::move(counts); + } + EXPECT_EQ(3, live); + EXPECT_EQ(3, dead); + + live = dead = 0; + { + StaticVector<DestroyCounts, 5> counts1; + counts1.emplace_back(live, dead); + counts1.emplace_back(live, dead); + counts1.emplace_back(live, dead); + + StaticVector<DestroyCounts, 5> counts2; + counts2.emplace_back(live, dead); + + swap(counts1, counts2); + + EXPECT_EQ(0, live); + EXPECT_EQ(2, dead); + + dead = 0; + } + EXPECT_EQ(4, live); + EXPECT_EQ(0, dead); +} + +} // namespace android::test diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 3d0f8bbb11..55c5de9477 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -29,7 +29,6 @@ #include <android-base/strings.h> #include <android/dlext.h> #include <binder/IServiceManager.h> -#include <cutils/properties.h> #include <graphicsenv/IGpuService.h> #include <log/log.h> #include <nativeloader/dlext_namespaces.h> @@ -74,7 +73,7 @@ static constexpr const char* kNativeLibrariesSystemConfigPath[] = static std::string vndkVersionStr() { #ifdef __BIONIC__ - return android::base::GetProperty("ro.vndk.version", ""); + return base::GetProperty("ro.vndk.version", ""); #endif return ""; } @@ -345,10 +344,8 @@ void* GraphicsEnv::loadLibrary(std::string name) { } bool GraphicsEnv::checkAngleRules(void* so) { - char manufacturer[PROPERTY_VALUE_MAX]; - char model[PROPERTY_VALUE_MAX]; - property_get("ro.product.manufacturer", manufacturer, "UNSET"); - property_get("ro.product.model", model, "UNSET"); + auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET"); + auto model = base::GetProperty("ro.product.model", "UNSET"); auto ANGLEGetFeatureSupportUtilAPIVersion = (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, @@ -401,7 +398,8 @@ bool GraphicsEnv::checkAngleRules(void* so) { ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); break; } - if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer, model, systemInfoHandle)) { + if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(), + systemInfoHandle)) { ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); break; } @@ -468,7 +466,8 @@ void GraphicsEnv::updateUseAngle() { } void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, - const std::string developerOptIn, const int rulesFd, + const std::string developerOptIn, + const std::vector<std::string> eglFeatures, const int rulesFd, const long rulesOffset, const long rulesLength) { if (mUseAngle != UNKNOWN) { // We've already figured out an answer for this app, so just return. @@ -477,6 +476,8 @@ void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName return; } + mAngleEglFeatures = std::move(eglFeatures); + ALOGV("setting ANGLE path to '%s'", path.c_str()); mAnglePath = path; ALOGV("setting ANGLE app name to '%s'", appName.c_str()); @@ -522,6 +523,10 @@ std::string& GraphicsEnv::getAngleAppName() { return mAngleAppName; } +const std::vector<std::string>& GraphicsEnv::getAngleEglFeatures() { + return mAngleEglFeatures; +} + const std::string& GraphicsEnv::getLayerPaths() { return mLayerPaths; } @@ -653,8 +658,7 @@ android_namespace_t* GraphicsEnv::getAngleNamespace() { mAngleNamespace = android_create_namespace("ANGLE", nullptr, // ld_library_path mAnglePath.c_str(), // default_library_path - ANDROID_NAMESPACE_TYPE_SHARED | - ANDROID_NAMESPACE_TYPE_ISOLATED, + ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED, nullptr, // permitted_when_isolated_path nullptr); diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 22a2332589..900fc49b59 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -97,12 +97,15 @@ public: // in the search path must have a '!' after the zip filename, e.g. // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, - const int rulesFd, const long rulesOffset, const long rulesLength); + const std::vector<std::string> eglFeatures, const int rulesFd, + const long rulesOffset, const long rulesLength); // Get the ANGLE driver namespace. android_namespace_t* getAngleNamespace(); // Get the app name for ANGLE debug message. std::string& getAngleAppName(); + const std::vector<std::string>& getAngleEglFeatures(); + /* * Apis for debug layer */ @@ -154,6 +157,8 @@ private: std::string mAngleAppName; // ANGLE developer opt in status. std::string mAngleDeveloperOptIn; + // ANGLE EGL features; + std::vector<std::string> mAngleEglFeatures; // ANGLE rules. std::vector<char> mRulesBuffer; // Use ANGLE flag. diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 4a4510e047..a0e9cbf2eb 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -30,6 +30,12 @@ cc_library_headers { min_sdk_version: "29", } +filegroup { + name: "libgui_aidl", + srcs: ["aidl/**/*.aidl"], + path: "aidl/", +} + cc_library_shared { name: "libgui", vendor_available: false, @@ -42,6 +48,7 @@ cc_library_shared { srcs: [ ":framework_native_aidl", + ":libgui_aidl", ":libgui_bufferqueue_sources", "BitTube.cpp", @@ -55,13 +62,13 @@ cc_library_shared { "DisplayEventDispatcher.cpp", "DisplayEventReceiver.cpp", "GLConsumer.cpp", - "GuiConfig.cpp", "IConsumerListener.cpp", "IDisplayEventConnection.cpp", "IGraphicBufferConsumer.cpp", "IGraphicBufferProducer.cpp", "IProducerListener.cpp", "IRegionSamplingListener.cpp", + "IScreenCaptureListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", "ITransactionCompletedListener.cpp", @@ -74,6 +81,7 @@ cc_library_shared { "SurfaceControl.cpp", "SurfaceComposerClient.cpp", "SyncFeatures.cpp", + "TransactionTracing.cpp", "view/Surface.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", @@ -92,6 +100,7 @@ cc_library_shared { export_shared_lib_headers: [ "libbinder", + "libinput", ], // bufferhub is not used when building libgui for vendors @@ -144,6 +153,7 @@ cc_library_static { defaults: ["libgui_bufferqueue-defaults"], srcs: [ + ":libgui_aidl", ":libgui_bufferqueue_sources", ], } diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 56591bdc63..678613b1ff 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -18,10 +18,12 @@ #define LOG_TAG "BLASTBufferQueue" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 #include <gui/BLASTBufferQueue.h> #include <gui/BufferItemConsumer.h> #include <gui/GLConsumer.h> +#include <gui/Surface.h> #include <utils/Trace.h> @@ -29,8 +31,20 @@ using namespace std::chrono_literals; +namespace { +inline const char* toString(bool b) { + return b ? "true" : "false"; +} +} // namespace + namespace android { +// Macros to include adapter info in log messages +#define BQA_LOGV(x, ...) \ + ALOGV("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) +#define BQA_LOGE(x, ...) \ + ALOGE("[%s](f:%u,a:%u) " x, mName.c_str(), mNumFrameAvailable, mNumAcquired, ##__VA_ARGS__) + void BLASTBufferItemConsumer::onDisconnect() { Mutex::Autolock lock(mFrameEventHistoryMutex); mPreviouslyConnected = mCurrentlyConnected; @@ -93,9 +107,10 @@ void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* ne if (needsDisconnect != nullptr) *needsDisconnect = disconnect; } -BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height, - bool enableTripleBuffering) - : mSurfaceControl(surface), +BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, + int width, int height, bool enableTripleBuffering) + : mName(name), + mSurfaceControl(surface), mWidth(width), mHeight(height), mNextTransaction(nullptr) { @@ -110,9 +125,9 @@ BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer, GraphicBuffer::USAGE_HW_COMPOSER, 1, true); static int32_t id = 0; - auto name = std::string("BLAST Consumer") + std::to_string(id); + auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id); id++; - mBufferItemConsumer->setName(String8(name.c_str())); + mBufferItemConsumer->setName(String8(consumerName.c_str())); mBufferItemConsumer->setFrameAvailableListener(this); mBufferItemConsumer->setBufferFreedListener(this); mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight); @@ -127,7 +142,7 @@ BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, mPendingReleaseItem.releaseFence = nullptr; } -void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) { +void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height) { std::unique_lock _lock{mMutex}; mSurfaceControl = surface; @@ -144,7 +159,7 @@ static void transactionCallbackThunk(void* context, nsecs_t latchTime, if (context == nullptr) { return; } - BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context); + sp<BLASTBufferQueue> bq = static_cast<BLASTBufferQueue*>(context); bq->transactionCallback(latchTime, presentFence, stats); } @@ -152,6 +167,8 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence const std::vector<SurfaceControlStats>& stats) { std::unique_lock _lock{mMutex}; ATRACE_CALL(); + BQA_LOGV("transactionCallback"); + mInitialCallbackReceived = true; if (!stats.empty()) { mTransformHint = stats[0].transformHint; @@ -169,7 +186,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence if (!stats.empty()) { mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence; } else { - ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback"); + BQA_LOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback"); mPendingReleaseItem.releaseFence = nullptr; } mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item, @@ -182,7 +199,7 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence } if (mSubmitted.empty()) { - ALOGE("ERROR: callback with no corresponding submitted buffer item"); + BQA_LOGE("ERROR: callback with no corresponding submitted buffer item"); } mPendingReleaseItem.item = std::move(mSubmitted.front()); mSubmitted.pop(); @@ -195,12 +212,17 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { ATRACE_CALL(); - if (mNumFrameAvailable == 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) { + BQA_LOGV("processNextBufferLocked useNextTransaction=%s", toString(useNextTransaction)); + + // Wait to acquire a buffer if there are no frames available or we have acquired the max + // number of buffers. + if (mNumFrameAvailable == 0 || maxBuffersAcquired()) { + BQA_LOGV("processNextBufferLocked waiting for frame available or callback"); return; } if (mSurfaceControl == nullptr) { - ALOGE("ERROR : surface control is null"); + BQA_LOGE("ERROR : surface control is null"); return; } @@ -217,6 +239,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false); if (status != OK) { + BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str()); return; } auto buffer = bufferItem.mGraphicBuffer; @@ -224,9 +247,17 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { if (buffer == nullptr) { mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); + BQA_LOGE("Buffer was empty"); return; } + if (rejectBuffer(bufferItem)) { + BQA_LOGE("rejecting buffer:configured size=%dx%d, buffer{size=%dx%d transform=%d}", mWidth, + mHeight, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform); + // TODO(b/168917217) temporarily don't reject buffers until we can synchronize buffer size + // changes from ViewRootImpl. + } + mNumAcquired++; mSubmitted.push(bufferItem); @@ -246,15 +277,22 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE); t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this)); - t->setFrame(mSurfaceControl, {0, 0, mWidth, mHeight}); + t->setFrame(mSurfaceControl, + {0, 0, static_cast<int32_t>(mWidth), static_cast<int32_t>(mHeight)}); t->setCrop(mSurfaceControl, computeCrop(bufferItem)); t->setTransform(mSurfaceControl, bufferItem.mTransform); t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse); t->setDesiredPresentTime(bufferItem.mTimestamp); + t->setFrameNumber(mSurfaceControl, bufferItem.mFrameNumber); if (applyTransaction) { t->apply(); } + + BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 + " applyTransaction=%s mTimestamp=%" PRId64, + mWidth, mHeight, bufferItem.mFrameNumber, toString(applyTransaction), + bufferItem.mTimestamp); } Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { @@ -264,23 +302,119 @@ Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { return item.mCrop; } -void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) { +void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { ATRACE_CALL(); std::unique_lock _lock{mMutex}; - if (mNextTransaction != nullptr) { - while (mNumFrameAvailable > 0 || mNumAcquired == MAX_ACQUIRED_BUFFERS + 1) { + const bool nextTransactionSet = mNextTransaction != nullptr; + BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s mFlushShadowQueue=%s", + item.mFrameNumber, toString(nextTransactionSet), toString(mFlushShadowQueue)); + + if (nextTransactionSet || mFlushShadowQueue) { + while (mNumFrameAvailable > 0 || maxBuffersAcquired()) { + BQA_LOGV("waiting in onFrameAvailable..."); mCallbackCV.wait(_lock); } } + mFlushShadowQueue = false; // add to shadow queue mNumFrameAvailable++; processNextBufferLocked(true); } +void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) { + BQA_LOGV("onFrameReplaced framenumber=%" PRIu64, item.mFrameNumber); + // Do nothing since we are not storing unacquired buffer items locally. +} + void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) { std::lock_guard _lock{mMutex}; mNextTransaction = t; } +bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) const { + if (item.mScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE) { + // Only reject buffers if scaling mode is freeze. + return false; + } + + uint32_t bufWidth = item.mGraphicBuffer->getWidth(); + uint32_t bufHeight = item.mGraphicBuffer->getHeight(); + + // Take the buffer's orientation into account + if (item.mTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + + // reject buffers if the buffer size doesn't match. + return bufWidth != mWidth || bufHeight != mHeight; +} + +// Check if we have acquired the maximum number of buffers. +// As a special case, we wait for the first callback before acquiring the second buffer so we +// can ensure the first buffer is presented if multiple buffers are queued in succession. +bool BLASTBufferQueue::maxBuffersAcquired() const { + return mNumAcquired == MAX_ACQUIRED_BUFFERS + 1 || + (!mInitialCallbackReceived && mNumAcquired == 1); +} + +class BBQSurface : public Surface { +private: + sp<BLASTBufferQueue> mBbq; +public: + BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp, + const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq) + : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {} + + void allocateBuffers() override { + uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth; + uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight; + auto gbp = getIGraphicBufferProducer(); + std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(), + reqFormat=mReqFormat, reqUsage=mReqUsage] () { + gbp->allocateBuffers(reqWidth, reqHeight, + reqFormat, reqUsage); + + }).detach(); + } + + status_t setFrameRate(float frameRate, int8_t compatibility) override { + if (!ValidateFrameRate(frameRate, compatibility, "BBQSurface::setFrameRate")) { + return BAD_VALUE; + } + return mBbq->setFrameRate(frameRate, compatibility); + } + + status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId) override { + return mBbq->setFrameTimelineVsync(frameTimelineVsyncId); + } +}; + +// TODO: Can we coalesce this with frame updates? Need to confirm +// no timing issues. +status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility) { + std::unique_lock _lock{mMutex}; + SurfaceComposerClient::Transaction t; + + return t.setFrameRate(mSurfaceControl, frameRate, compatibility) + .apply(); +} + +status_t BLASTBufferQueue::setFrameTimelineVsync(int64_t frameTimelineVsyncId) { + std::unique_lock _lock{mMutex}; + SurfaceComposerClient::Transaction t; + + return t.setFrameTimelineVsync(mSurfaceControl, frameTimelineVsyncId) + .apply(); +} + +sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) { + std::unique_lock _lock{mMutex}; + sp<IBinder> scHandle = nullptr; + if (includeSurfaceControlHandle && mSurfaceControl) { + scHandle = mSurfaceControl->getHandle(); + } + return new BBQSurface(mProducer, true, scHandle, this); +} + } // namespace android diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index 682fe911de..abfee61685 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -73,7 +73,8 @@ status_t DisplayEventDispatcher::scheduleVsync() { nsecs_t vsyncTimestamp; PhysicalDisplayId vsyncDisplayId; uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { + VsyncEventData vsyncEventData; + if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) { ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this, ns2ms(static_cast<nsecs_t>(vsyncTimestamp))); } @@ -116,12 +117,14 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { nsecs_t vsyncTimestamp; PhysicalDisplayId vsyncDisplayId; uint32_t vsyncCount; - if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) { + VsyncEventData vsyncEventData; + if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount, &vsyncEventData)) { ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64 - ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d", - this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount); + ", displayId=%s, count=%d, vsyncId=%" PRId64, + this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount, + vsyncEventData.id); mWaitingForVsync = false; - dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount); + dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData); } return 1; // keep the callback @@ -129,7 +132,8 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, - uint32_t* outCount) { + uint32_t* outCount, + VsyncEventData* outVsyncEventData) { bool gotVsync = false; DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE]; ssize_t n; @@ -145,6 +149,8 @@ bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp, *outTimestamp = ev.header.timestamp; *outDisplayId = ev.header.displayId; *outCount = ev.vsync.count; + outVsyncEventData->id = ev.vsync.vsyncId; + outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp; break; case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); diff --git a/libs/gui/IScreenCaptureListener.cpp b/libs/gui/IScreenCaptureListener.cpp new file mode 100644 index 0000000000..0635e9cf34 --- /dev/null +++ b/libs/gui/IScreenCaptureListener.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/IScreenCaptureListener.h> +#include <gui/LayerState.h> + +namespace android { + +namespace { // Anonymous + +enum class Tag : uint32_t { + ON_SCREEN_CAPTURE_COMPLETE = IBinder::FIRST_CALL_TRANSACTION, + LAST = ON_SCREEN_CAPTURE_COMPLETE, +}; + +} // Anonymous namespace + +class BpScreenCaptureListener : public SafeBpInterface<IScreenCaptureListener> { +public: + explicit BpScreenCaptureListener(const sp<IBinder>& impl) + : SafeBpInterface<IScreenCaptureListener>(impl, "BpScreenCaptureListener") {} + + ~BpScreenCaptureListener() override; + + status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override { + Parcel data, reply; + data.writeInterfaceToken(IScreenCaptureListener::getInterfaceDescriptor()); + + SAFE_PARCEL(captureResults.write, data); + return remote()->transact(static_cast<uint32_t>(Tag::ON_SCREEN_CAPTURE_COMPLETE), data, + &reply, IBinder::FLAG_ONEWAY); + } +}; + +// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see +// clang warning -Wweak-vtables) +BpScreenCaptureListener::~BpScreenCaptureListener() = default; + +IMPLEMENT_META_INTERFACE(ScreenCaptureListener, "android.gui.IScreenCaptureListener"); + +status_t BnScreenCaptureListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + auto tag = static_cast<Tag>(code); + switch (tag) { + case Tag::ON_SCREEN_CAPTURE_COMPLETE: { + CHECK_INTERFACE(IScreenCaptureListener, data, reply); + ScreenCaptureResults captureResults; + SAFE_PARCEL(captureResults.read, data); + return onScreenCaptureComplete(captureResults); + } + default: { + return BBinder::onTransact(code, data, reply, flags); + } + } +} + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index e62a61fc55..6f92233935 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -20,6 +20,8 @@ #include <stdint.h> #include <sys/types.h> +#include <android/gui/ITransactionTraceListener.h> + #include <binder/Parcel.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -66,42 +68,43 @@ public: return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } - virtual void setTransactionState(const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, - const InputWindowCommands& commands, - int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks) { + virtual status_t setTransactionState( + int64_t frameTimelineVsyncId, const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + const InputWindowCommands& commands, int64_t desiredPresentTime, + const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeUint32(static_cast<uint32_t>(state.size())); + SAFE_PARCEL(data.writeInt64, frameTimelineVsyncId); + SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size())); for (const auto& s : state) { - s.write(data); + SAFE_PARCEL(s.write, data); } - data.writeUint32(static_cast<uint32_t>(displays.size())); + SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size())); for (const auto& d : displays) { - d.write(data); + SAFE_PARCEL(d.write, data); } - data.writeUint32(flags); - data.writeStrongBinder(applyToken); - commands.write(data); - data.writeInt64(desiredPresentTime); - data.writeStrongBinder(uncacheBuffer.token.promote()); - data.writeUint64(uncacheBuffer.id); - data.writeBool(hasListenerCallbacks); + SAFE_PARCEL(data.writeUint32, flags); + SAFE_PARCEL(data.writeStrongBinder, applyToken); + SAFE_PARCEL(commands.write, data); + SAFE_PARCEL(data.writeInt64, desiredPresentTime); + SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote()); + SAFE_PARCEL(data.writeUint64, uncacheBuffer.id); + SAFE_PARCEL(data.writeBool, hasListenerCallbacks); - if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) { - for (const auto& [listener, callbackIds] : listenerCallbacks) { - data.writeStrongBinder(listener); - data.writeInt64Vector(callbackIds); - } + SAFE_PARCEL(data.writeVectorSize, listenerCallbacks); + for (const auto& [listener, callbackIds] : listenerCallbacks) { + SAFE_PARCEL(data.writeStrongBinder, listener); + SAFE_PARCEL(data.writeInt64Vector, callbackIds); } - remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); + SAFE_PARCEL(data.writeUint64, transactionId); + + return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); } virtual void bootFinished() @@ -111,95 +114,34 @@ public: remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); } - virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers, ui::Dataspace reqDataspace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, bool captureSecureLayers) { + virtual status_t captureDisplay(const DisplayCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(display); - data.writeInt32(static_cast<int32_t>(reqDataspace)); - data.writeInt32(static_cast<int32_t>(reqPixelFormat)); - data.write(sourceCrop); - data.writeUint32(reqWidth); - data.writeUint32(reqHeight); - data.writeInt32(static_cast<int32_t>(useIdentityTransform)); - data.writeInt32(static_cast<int32_t>(rotation)); - data.writeInt32(static_cast<int32_t>(captureSecureLayers)); - status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); - if (result != NO_ERROR) { - ALOGE("captureScreen failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result != NO_ERROR) { - ALOGE("captureScreen failed to readInt32: %d", result); - return result; - } - - *outBuffer = new GraphicBuffer(); - reply.read(**outBuffer); - outCapturedSecureLayers = reply.readBool(); + SAFE_PARCEL(args.write, data); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener)); - return result; + return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY, data, &reply); } - virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer) { + virtual status_t captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeUint64(displayOrLayerStack); - status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN_BY_ID, data, &reply); - if (result != NO_ERROR) { - ALOGE("captureScreen failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result != NO_ERROR) { - ALOGE("captureScreen failed to readInt32: %d", result); - return result; - } + SAFE_PARCEL(data.writeUint64, displayOrLayerStack); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener)); - *outDataspace = static_cast<ui::Dataspace>(reply.readInt32()); - *outBuffer = new GraphicBuffer(); - reply.read(**outBuffer); - return result; + return remote()->transact(BnSurfaceComposer::CAPTURE_DISPLAY_BY_ID, data, &reply); } - virtual status_t captureLayers( - const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, - const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, - const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeLayers, float frameScale, - bool childrenOnly) { + virtual status_t captureLayers(const LayerCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(layerHandleBinder); - data.writeInt32(static_cast<int32_t>(reqDataspace)); - data.writeInt32(static_cast<int32_t>(reqPixelFormat)); - data.write(sourceCrop); - data.writeInt32(excludeLayers.size()); - for (auto el : excludeLayers) { - data.writeStrongBinder(el); - } - data.writeFloat(frameScale); - data.writeBool(childrenOnly); - status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); - if (result != NO_ERROR) { - ALOGE("captureLayers failed to transact: %d", result); - return result; - } - result = reply.readInt32(); - if (result != NO_ERROR) { - ALOGE("captureLayers failed to readInt32: %d", result); - return result; - } + SAFE_PARCEL(args.write, data); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(captureListener)); - *outBuffer = new GraphicBuffer(); - reply.read(**outBuffer); - - return result; + return remote()->transact(BnSurfaceComposer::CAPTURE_LAYERS, data, &reply); } virtual bool authenticateSurfaceTexture( @@ -308,10 +250,25 @@ public: { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeString8(displayName); - data.writeInt32(secure ? 1 : 0); - remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply); - return reply.readStrongBinder(); + status_t status = data.writeString8(displayName); + if (status) { + return nullptr; + } + status = data.writeBool(secure); + if (status) { + return nullptr; + } + + status = remote()->transact(BnSurfaceComposer::CREATE_DISPLAY, data, &reply); + if (status) { + return nullptr; + } + sp<IBinder> display; + status = reply.readNullableStrongBinder(&display); + if (status) { + return nullptr; + } + return display; } virtual void destroyDisplay(const sp<IBinder>& display) @@ -327,8 +284,11 @@ public: data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_IDS, data, &reply) == NO_ERROR) { - std::vector<PhysicalDisplayId> displayIds; - if (reply.readUint64Vector(&displayIds) == NO_ERROR) { + std::vector<uint64_t> rawIds; + if (reply.readUint64Vector(&rawIds) == NO_ERROR) { + std::vector<PhysicalDisplayId> displayIds(rawIds.size()); + std::transform(rawIds.begin(), rawIds.end(), displayIds.begin(), + [](uint64_t rawId) { return PhysicalDisplayId(rawId); }); return displayIds; } } @@ -339,7 +299,7 @@ public: virtual sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeUint64(displayId); + data.writeUint64(displayId.value); remote()->transact(BnSurfaceComposer::GET_PHYSICAL_DISPLAY_TOKEN, data, &reply); return reply.readStrongBinder(); } @@ -371,10 +331,8 @@ public: data.writeStrongBinder(display); remote()->transact(BnSurfaceComposer::GET_DISPLAY_INFO, data, &reply); const status_t result = reply.readInt32(); - if (result == NO_ERROR) { - memcpy(info, reply.readInplace(sizeof(DisplayInfo)), sizeof(DisplayInfo)); - } - return result; + if (result != NO_ERROR) return result; + return reply.read(*info); } virtual status_t getDisplayConfigs(const sp<IBinder>& display, Vector<DisplayConfig>* configs) { @@ -930,7 +888,7 @@ public: } virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, + int32_t defaultConfig, bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, @@ -951,6 +909,11 @@ public: ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result); return result; } + result = data.writeBool(allowGroupSwitching); + if (result != NO_ERROR) { + ALOGE("setDesiredDisplayConfigSpecs failed to write allowGroupSwitching: %d", result); + return result; + } result = data.writeFloat(primaryRefreshRateMin); if (result != NO_ERROR) { ALOGE("setDesiredDisplayConfigSpecs failed to write primaryRefreshRateMin: %d", result); @@ -985,12 +948,14 @@ public: virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t* outDefaultConfig, + bool* outAllowGroupSwitching, float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) { - if (!outDefaultConfig || !outPrimaryRefreshRateMin || !outPrimaryRefreshRateMax || - !outAppRequestRefreshRateMin || !outAppRequestRefreshRateMax) { + if (!outDefaultConfig || !outAllowGroupSwitching || !outPrimaryRefreshRateMin || + !outPrimaryRefreshRateMax || !outAppRequestRefreshRateMin || + !outAppRequestRefreshRateMax) { return BAD_VALUE; } Parcel data, reply; @@ -1015,6 +980,11 @@ public: ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result); return result; } + result = reply.readBool(outAllowGroupSwitching); + if (result != NO_ERROR) { + ALOGE("getDesiredDisplayConfigSpecs failed to read allowGroupSwitching: %d", result); + return result; + } result = reply.readFloat(outPrimaryRefreshRateMin); if (result != NO_ERROR) { ALOGE("getDesiredDisplayConfigSpecs failed to read primaryRefreshRateMin: %d", result); @@ -1093,22 +1063,22 @@ public: return NO_ERROR; } - virtual status_t notifyPowerHint(int32_t hintId) { + virtual status_t notifyPowerBoost(int32_t boostId) { Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to write interface token: %d", error); + ALOGE("notifyPowerBoost: failed to write interface token: %d", error); return error; } - error = data.writeInt32(hintId); + error = data.writeInt32(boostId); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to write hintId: %d", error); + ALOGE("notifyPowerBoost: failed to write boostId: %d", error); return error; } - error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_HINT, data, &reply, + error = remote()->transact(BnSurfaceComposer::NOTIFY_POWER_BOOST, data, &reply, IBinder::FLAG_ONEWAY); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to transact: %d", error); + ALOGE("notifyPowerBoost: failed to transact: %d", error); return error; } return NO_ERROR; @@ -1213,6 +1183,47 @@ public: return NO_ERROR; } + + virtual status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface, + int64_t frameTimelineVsyncId) { + Parcel data, reply; + status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + ALOGE("setFrameTimelineVsync: failed writing interface token: %s (%d)", strerror(-err), + -err); + return err; + } + + err = data.writeStrongBinder(IInterface::asBinder(surface)); + if (err != NO_ERROR) { + ALOGE("setFrameTimelineVsync: failed writing strong binder: %s (%d)", strerror(-err), + -err); + return err; + } + + err = data.writeInt64(frameTimelineVsyncId); + if (err != NO_ERROR) { + ALOGE("setFrameTimelineVsync: failed writing int64_t: %s (%d)", strerror(-err), -err); + return err; + } + + err = remote()->transact(BnSurfaceComposer::SET_FRAME_TIMELINE_VSYNC, data, &reply); + if (err != NO_ERROR) { + ALOGE("setFrameTimelineVsync: failed to transact: %s (%d)", strerror(-err), err); + return err; + } + + return reply.readInt32(); + } + + virtual status_t addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& listener) { + Parcel data, reply; + SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener)); + + return remote()->transact(BnSurfaceComposer::ADD_TRANSACTION_TRACE_LISTENER, data, &reply); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -1236,135 +1247,95 @@ status_t BnSurfaceComposer::onTransact( case SET_TRANSACTION_STATE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - size_t count = data.readUint32(); - if (count > data.dataSize()) { - return BAD_VALUE; - } + int64_t frameTimelineVsyncId; + SAFE_PARCEL(data.readInt64, &frameTimelineVsyncId); + uint32_t count = 0; + SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); Vector<ComposerState> state; state.setCapacity(count); for (size_t i = 0; i < count; i++) { ComposerState s; - if (s.read(data) == BAD_VALUE) { - return BAD_VALUE; - } + SAFE_PARCEL(s.read, data); state.add(s); } - count = data.readUint32(); - if (count > data.dataSize()) { - return BAD_VALUE; - } + SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize()); DisplayState d; Vector<DisplayState> displays; displays.setCapacity(count); for (size_t i = 0; i < count; i++) { - if (d.read(data) == BAD_VALUE) { - return BAD_VALUE; - } + SAFE_PARCEL(d.read, data); displays.add(d); } - uint32_t stateFlags = data.readUint32(); - sp<IBinder> applyToken = data.readStrongBinder(); + uint32_t stateFlags = 0; + SAFE_PARCEL(data.readUint32, &stateFlags); + sp<IBinder> applyToken; + SAFE_PARCEL(data.readStrongBinder, &applyToken); InputWindowCommands inputWindowCommands; - inputWindowCommands.read(data); + SAFE_PARCEL(inputWindowCommands.read, data); - int64_t desiredPresentTime = data.readInt64(); + int64_t desiredPresentTime = 0; + SAFE_PARCEL(data.readInt64, &desiredPresentTime); client_cache_t uncachedBuffer; - uncachedBuffer.token = data.readStrongBinder(); - uncachedBuffer.id = data.readUint64(); + sp<IBinder> tmpBinder; + SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder); + uncachedBuffer.token = tmpBinder; + SAFE_PARCEL(data.readUint64, &uncachedBuffer.id); - bool hasListenerCallbacks = data.readBool(); + bool hasListenerCallbacks = false; + SAFE_PARCEL(data.readBool, &hasListenerCallbacks); std::vector<ListenerCallbacks> listenerCallbacks; - int32_t listenersSize = data.readInt32(); + int32_t listenersSize = 0; + SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize()); for (int32_t i = 0; i < listenersSize; i++) { - auto listener = data.readStrongBinder(); + SAFE_PARCEL(data.readStrongBinder, &tmpBinder); std::vector<CallbackId> callbackIds; - data.readInt64Vector(&callbackIds); - listenerCallbacks.emplace_back(listener, callbackIds); + SAFE_PARCEL(data.readInt64Vector, &callbackIds); + listenerCallbacks.emplace_back(tmpBinder, callbackIds); } - setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands, - desiredPresentTime, uncachedBuffer, hasListenerCallbacks, - listenerCallbacks); - return NO_ERROR; + + uint64_t transactionId = -1; + SAFE_PARCEL(data.readUint64, &transactionId); + + return setTransactionState(frameTimelineVsyncId, state, displays, stateFlags, + applyToken, inputWindowCommands, desiredPresentTime, + uncachedBuffer, hasListenerCallbacks, listenerCallbacks, + transactionId); } case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); return NO_ERROR; } - case CAPTURE_SCREEN: { + case CAPTURE_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> display = data.readStrongBinder(); - ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); - ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); - sp<GraphicBuffer> outBuffer; - Rect sourceCrop(Rect::EMPTY_RECT); - data.read(sourceCrop); - uint32_t reqWidth = data.readUint32(); - uint32_t reqHeight = data.readUint32(); - bool useIdentityTransform = static_cast<bool>(data.readInt32()); - int32_t rotation = data.readInt32(); - bool captureSecureLayers = static_cast<bool>(data.readInt32()); - - bool capturedSecureLayers = false; - status_t res = captureScreen(display, &outBuffer, capturedSecureLayers, reqDataspace, - reqPixelFormat, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, ui::toRotation(rotation), - captureSecureLayers); - - reply->writeInt32(res); - if (res == NO_ERROR) { - reply->write(*outBuffer); - reply->writeBool(capturedSecureLayers); - } - return NO_ERROR; + DisplayCaptureArgs args; + sp<IScreenCaptureListener> captureListener; + SAFE_PARCEL(args.read, data); + SAFE_PARCEL(data.readStrongBinder, &captureListener); + + return captureDisplay(args, captureListener); } - case CAPTURE_SCREEN_BY_ID: { + case CAPTURE_DISPLAY_BY_ID: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - uint64_t displayOrLayerStack = data.readUint64(); - ui::Dataspace outDataspace = ui::Dataspace::V0_SRGB; - sp<GraphicBuffer> outBuffer; - status_t res = captureScreen(displayOrLayerStack, &outDataspace, &outBuffer); - reply->writeInt32(res); - if (res == NO_ERROR) { - reply->writeInt32(static_cast<int32_t>(outDataspace)); - reply->write(*outBuffer); - } - return NO_ERROR; + uint64_t displayOrLayerStack = 0; + sp<IScreenCaptureListener> captureListener; + SAFE_PARCEL(data.readUint64, &displayOrLayerStack); + SAFE_PARCEL(data.readStrongBinder, &captureListener); + + return captureDisplay(displayOrLayerStack, captureListener); } case CAPTURE_LAYERS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - sp<IBinder> layerHandleBinder = data.readStrongBinder(); - ui::Dataspace reqDataspace = static_cast<ui::Dataspace>(data.readInt32()); - ui::PixelFormat reqPixelFormat = static_cast<ui::PixelFormat>(data.readInt32()); - sp<GraphicBuffer> outBuffer; - Rect sourceCrop(Rect::EMPTY_RECT); - data.read(sourceCrop); + LayerCaptureArgs args; + sp<IScreenCaptureListener> captureListener; + SAFE_PARCEL(args.read, data); + SAFE_PARCEL(data.readStrongBinder, &captureListener); - std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles; - int numExcludeHandles = data.readInt32(); - if (numExcludeHandles >= static_cast<int>(MAX_LAYERS)) { - return BAD_VALUE; - } - excludeHandles.reserve(numExcludeHandles); - for (int i = 0; i < numExcludeHandles; i++) { - excludeHandles.emplace(data.readStrongBinder()); - } - - float frameScale = data.readFloat(); - bool childrenOnly = data.readBool(); - - status_t res = - captureLayers(layerHandleBinder, &outBuffer, reqDataspace, reqPixelFormat, - sourceCrop, excludeHandles, frameScale, childrenOnly); - reply->writeInt32(res); - if (res == NO_ERROR) { - reply->write(*outBuffer); - } - return NO_ERROR; + return captureLayers(args, captureListener); } case AUTHENTICATE_SURFACE: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1405,10 +1376,12 @@ status_t BnSurfaceComposer::onTransact( } case CREATE_DISPLAY: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - String8 displayName = data.readString8(); - bool secure = bool(data.readInt32()); - sp<IBinder> display(createDisplay(displayName, secure)); - reply->writeStrongBinder(display); + String8 displayName; + SAFE_PARCEL(data.readString8, &displayName); + bool secure = false; + SAFE_PARCEL(data.readBool, &secure); + sp<IBinder> display = createDisplay(displayName, secure); + SAFE_PARCEL(reply->writeStrongBinder, display); return NO_ERROR; } case DESTROY_DISPLAY: { @@ -1419,7 +1392,7 @@ status_t BnSurfaceComposer::onTransact( } case GET_PHYSICAL_DISPLAY_TOKEN: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - PhysicalDisplayId displayId = data.readUint64(); + PhysicalDisplayId displayId(data.readUint64()); sp<IBinder> display = getPhysicalDisplayToken(displayId); reply->writeStrongBinder(display); return NO_ERROR; @@ -1442,10 +1415,8 @@ status_t BnSurfaceComposer::onTransact( const sp<IBinder> display = data.readStrongBinder(); const status_t result = getDisplayInfo(display, &info); reply->writeInt32(result); - if (result == NO_ERROR) { - memcpy(reply->writeInplace(sizeof(DisplayInfo)), &info, sizeof(DisplayInfo)); - } - return NO_ERROR; + if (result != NO_ERROR) return result; + return reply->write(info); } case GET_DISPLAY_CONFIGS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1823,7 +1794,11 @@ status_t BnSurfaceComposer::onTransact( } case GET_PHYSICAL_DISPLAY_IDS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - return reply->writeUint64Vector(getPhysicalDisplayIds()); + std::vector<PhysicalDisplayId> ids = getPhysicalDisplayIds(); + std::vector<uint64_t> rawIds(ids.size()); + std::transform(ids.begin(), ids.end(), rawIds.begin(), + [](PhysicalDisplayId id) { return id.value; }); + return reply->writeUint64Vector(rawIds); } case ADD_REGION_SAMPLING_LISTENER: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -1866,6 +1841,13 @@ status_t BnSurfaceComposer::onTransact( ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result); return result; } + bool allowGroupSwitching; + result = data.readBool(&allowGroupSwitching); + if (result != NO_ERROR) { + ALOGE("setDesiredDisplayConfigSpecs: failed to read allowGroupSwitching: %d", + result); + return result; + } float primaryRefreshRateMin; result = data.readFloat(&primaryRefreshRateMin); if (result != NO_ERROR) { @@ -1894,10 +1876,10 @@ status_t BnSurfaceComposer::onTransact( result); return result; } - result = - setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin, - primaryRefreshRateMax, appRequestRefreshRateMin, - appRequestRefreshRateMax); + result = setDesiredDisplayConfigSpecs(displayToken, defaultConfig, allowGroupSwitching, + primaryRefreshRateMin, primaryRefreshRateMax, + appRequestRefreshRateMin, + appRequestRefreshRateMax); if (result != NO_ERROR) { ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: " "%d", @@ -1911,13 +1893,14 @@ status_t BnSurfaceComposer::onTransact( CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> displayToken = data.readStrongBinder(); int32_t defaultConfig; + bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; float appRequestRefreshRateMin; float appRequestRefreshRateMax; status_t result = - getDesiredDisplayConfigSpecs(displayToken, &defaultConfig, + getDesiredDisplayConfigSpecs(displayToken, &defaultConfig, &allowGroupSwitching, &primaryRefreshRateMin, &primaryRefreshRateMax, &appRequestRefreshRateMin, &appRequestRefreshRateMax); @@ -1933,6 +1916,12 @@ status_t BnSurfaceComposer::onTransact( ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result); return result; } + result = reply->writeBool(allowGroupSwitching); + if (result != NO_ERROR) { + ALOGE("getDesiredDisplayConfigSpecs: failed to write allowGroupSwitching: %d", + result); + return result; + } result = reply->writeFloat(primaryRefreshRateMin); if (result != NO_ERROR) { ALOGE("getDesiredDisplayConfigSpecs: failed to write primaryRefreshRateMin: %d", @@ -1989,15 +1978,15 @@ status_t BnSurfaceComposer::onTransact( } return setDisplayBrightness(displayToken, brightness); } - case NOTIFY_POWER_HINT: { + case NOTIFY_POWER_BOOST: { CHECK_INTERFACE(ISurfaceComposer, data, reply); - int32_t hintId; - status_t error = data.readInt32(&hintId); + int32_t boostId; + status_t error = data.readInt32(&boostId); if (error != NO_ERROR) { - ALOGE("notifyPowerHint: failed to read hintId: %d", error); + ALOGE("notifyPowerBoost: failed to read boostId: %d", error); return error; } - return notifyPowerHint(hintId); + return notifyPowerBoost(boostId); } case SET_GLOBAL_SHADOW_SETTINGS: { CHECK_INTERFACE(ISurfaceComposer, data, reply); @@ -2058,6 +2047,40 @@ status_t BnSurfaceComposer::onTransact( } return NO_ERROR; } + case SET_FRAME_TIMELINE_VSYNC: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> binder; + status_t err = data.readStrongBinder(&binder); + if (err != NO_ERROR) { + ALOGE("setFrameTimelineVsync: failed to read strong binder: %s (%d)", + strerror(-err), -err); + return err; + } + sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder); + if (!surface) { + ALOGE("setFrameTimelineVsync: failed to cast to IGraphicBufferProducer: %s (%d)", + strerror(-err), -err); + return err; + } + int64_t frameTimelineVsyncId; + err = data.readInt64(&frameTimelineVsyncId); + if (err != NO_ERROR) { + ALOGE("setFrameTimelineVsync: failed to read int64_t: %s (%d)", strerror(-err), + -err); + return err; + } + + status_t result = setFrameTimelineVsync(surface, frameTimelineVsyncId); + reply->writeInt32(result); + return NO_ERROR; + } + case ADD_TRANSACTION_TRACE_LISTENER: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<gui::ITransactionTraceListener> listener; + SAFE_PARCEL(data.readStrongBinder, &listener); + + return addTransactionTraceListener(listener); + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index 621cf5950b..5e7a7ec67b 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -50,12 +50,12 @@ public: status_t createSurface(const String8& name, uint32_t width, uint32_t height, PixelFormat format, uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, - uint32_t* outTransformHint) override { + int32_t* outLayerId, uint32_t* outTransformHint) override { return callRemote<decltype(&ISurfaceComposerClient::createSurface)>(Tag::CREATE_SURFACE, name, width, height, format, flags, parent, std::move(metadata), - handle, gbp, + handle, gbp, outLayerId, outTransformHint); } @@ -63,14 +63,14 @@ public: PixelFormat format, uint32_t flags, const sp<IGraphicBufferProducer>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, uint32_t* outTransformHint) override { return callRemote<decltype( &ISurfaceComposerClient::createWithSurfaceParent)>(Tag::CREATE_WITH_SURFACE_PARENT, name, width, height, format, flags, parent, std::move(metadata), handle, gbp, - outTransformHint); + outLayerId, outTransformHint); } status_t clearLayerFrameStats(const sp<IBinder>& handle) const override { @@ -85,10 +85,11 @@ public: outStats); } - status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override { + status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, + int32_t* outLayerId) override { return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE, mirrorFromHandle, - outHandle); + outHandle, outLayerId); } }; diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp index b3eb9940b2..30c9b373ef 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -122,6 +122,8 @@ std::string LayerMetadata::itemToString(uint32_t key, const char* separator) con return StringPrintf("windowType%s%d", separator, getInt32(key, 0)); case view::LayerMetadataKey::METADATA_TASK_ID: return StringPrintf("taskId%s%d", separator, getInt32(key, 0)); + case view::LayerMetadataKey::METADATA_OWNER_PID: + return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0)); default: return StringPrintf("%d%s%dbytes", key, separator, static_cast<int>(mMap.at(key).size())); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 0281279d69..9722f368f4 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -18,193 +18,272 @@ #include <inttypes.h> -#include <utils/Errors.h> #include <binder/Parcel.h> -#include <gui/ISurfaceComposerClient.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/ISurfaceComposerClient.h> #include <gui/LayerState.h> +#include <utils/Errors.h> #include <cmath> namespace android { +layer_state_t::layer_state_t() + : what(0), + x(0), + y(0), + z(0), + w(0), + h(0), + layerStack(0), + alpha(0), + flags(0), + mask(0), + reserved(0), + crop_legacy(Rect::INVALID_RECT), + cornerRadius(0.0f), + backgroundBlurRadius(0), + barrierFrameNumber(0), + transform(0), + transformToDisplayInverse(false), + crop(Rect::INVALID_RECT), + orientedDisplaySpaceRect(Rect::INVALID_RECT), + dataspace(ui::Dataspace::UNKNOWN), + surfaceDamageRegion(), + api(-1), + colorTransform(mat4()), + bgColorAlpha(0), + bgColorDataspace(ui::Dataspace::UNKNOWN), + colorSpaceAgnostic(false), + shadowRadius(0.0f), + frameRateSelectionPriority(-1), + frameRate(0.0f), + frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), + fixedTransformHint(ui::Transform::ROT_INVALID), + frameNumber(0) { + matrix.dsdx = matrix.dtdy = 1.0f; + matrix.dsdy = matrix.dtdx = 0.0f; + hdrMetadata.validTypes = 0; +} + status_t layer_state_t::write(Parcel& output) const { - output.writeStrongBinder(surface); - output.writeUint64(what); - output.writeFloat(x); - output.writeFloat(y); - output.writeInt32(z); - output.writeUint32(w); - output.writeUint32(h); - output.writeUint32(layerStack); - output.writeFloat(alpha); - output.writeUint32(flags); - output.writeUint32(mask); - *reinterpret_cast<layer_state_t::matrix22_t *>( - output.writeInplace(sizeof(layer_state_t::matrix22_t))) = matrix; - output.write(crop_legacy); - output.writeStrongBinder(barrierHandle_legacy); - output.writeStrongBinder(reparentHandle); - output.writeUint64(frameNumber_legacy); - output.writeInt32(overrideScalingMode); - output.writeStrongBinder(IInterface::asBinder(barrierGbp_legacy)); - output.writeStrongBinder(relativeLayerHandle); - output.writeStrongBinder(parentHandleForChild); - output.writeFloat(color.r); - output.writeFloat(color.g); - output.writeFloat(color.b); + SAFE_PARCEL(output.writeStrongBinder, surface); + SAFE_PARCEL(output.writeInt32, layerId); + SAFE_PARCEL(output.writeUint64, what); + SAFE_PARCEL(output.writeFloat, x); + SAFE_PARCEL(output.writeFloat, y); + SAFE_PARCEL(output.writeInt32, z); + SAFE_PARCEL(output.writeUint32, w); + SAFE_PARCEL(output.writeUint32, h); + SAFE_PARCEL(output.writeUint32, layerStack); + SAFE_PARCEL(output.writeFloat, alpha); + SAFE_PARCEL(output.writeUint32, flags); + SAFE_PARCEL(output.writeUint32, mask); + SAFE_PARCEL(matrix.write, output); + SAFE_PARCEL(output.write, crop_legacy); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, barrierSurfaceControl_legacy); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, reparentSurfaceControl); + SAFE_PARCEL(output.writeUint64, barrierFrameNumber); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl); + SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild); + SAFE_PARCEL(output.writeFloat, color.r); + SAFE_PARCEL(output.writeFloat, color.g); + SAFE_PARCEL(output.writeFloat, color.b); #ifndef NO_INPUT - inputInfo.write(output); + SAFE_PARCEL(inputHandle->writeToParcel, &output); #endif - output.write(transparentRegion); - output.writeUint32(transform); - output.writeBool(transformToDisplayInverse); - output.write(crop); - output.write(frame); + SAFE_PARCEL(output.write, transparentRegion); + SAFE_PARCEL(output.writeUint32, transform); + SAFE_PARCEL(output.writeBool, transformToDisplayInverse); + SAFE_PARCEL(output.write, crop); + SAFE_PARCEL(output.write, orientedDisplaySpaceRect); + if (buffer) { - output.writeBool(true); - output.write(*buffer); + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *buffer); } else { - output.writeBool(false); + SAFE_PARCEL(output.writeBool, false); } + if (acquireFence) { - output.writeBool(true); - output.write(*acquireFence); + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *acquireFence); } else { - output.writeBool(false); + SAFE_PARCEL(output.writeBool, false); } - output.writeUint32(static_cast<uint32_t>(dataspace)); - output.write(hdrMetadata); - output.write(surfaceDamageRegion); - output.writeInt32(api); + + SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace)); + SAFE_PARCEL(output.write, hdrMetadata); + SAFE_PARCEL(output.write, surfaceDamageRegion); + SAFE_PARCEL(output.writeInt32, api); + if (sidebandStream) { - output.writeBool(true); - output.writeNativeHandle(sidebandStream->handle()); + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.writeNativeHandle, sidebandStream->handle()); } else { - output.writeBool(false); + SAFE_PARCEL(output.writeBool, false); } - memcpy(output.writeInplace(16 * sizeof(float)), - colorTransform.asArray(), 16 * sizeof(float)); - output.writeFloat(cornerRadius); - output.writeUint32(backgroundBlurRadius); - output.writeStrongBinder(cachedBuffer.token.promote()); - output.writeUint64(cachedBuffer.id); - output.writeParcelable(metadata); - - output.writeFloat(bgColorAlpha); - output.writeUint32(static_cast<uint32_t>(bgColorDataspace)); - output.writeBool(colorSpaceAgnostic); - - auto err = output.writeVectorSize(listeners); - if (err) { - return err; - } + SAFE_PARCEL(output.write, colorTransform.asArray(), 16 * sizeof(float)); + SAFE_PARCEL(output.writeFloat, cornerRadius); + SAFE_PARCEL(output.writeUint32, backgroundBlurRadius); + SAFE_PARCEL(output.writeStrongBinder, cachedBuffer.token.promote()); + SAFE_PARCEL(output.writeUint64, cachedBuffer.id); + SAFE_PARCEL(output.writeParcelable, metadata); + SAFE_PARCEL(output.writeFloat, bgColorAlpha); + SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace)); + SAFE_PARCEL(output.writeBool, colorSpaceAgnostic); + SAFE_PARCEL(output.writeVectorSize, listeners); for (auto listener : listeners) { - err = output.writeStrongBinder(listener.transactionCompletedListener); - if (err) { - return err; - } - err = output.writeInt64Vector(listener.callbackIds); - if (err) { - return err; - } - } - output.writeFloat(shadowRadius); - output.writeInt32(frameRateSelectionPriority); - output.writeFloat(frameRate); - output.writeByte(frameRateCompatibility); - output.writeUint32(fixedTransformHint); + SAFE_PARCEL(output.writeStrongBinder, listener.transactionCompletedListener); + SAFE_PARCEL(output.writeInt64Vector, listener.callbackIds); + } + SAFE_PARCEL(output.writeFloat, shadowRadius); + SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority); + SAFE_PARCEL(output.writeFloat, frameRate); + SAFE_PARCEL(output.writeByte, frameRateCompatibility); + SAFE_PARCEL(output.writeUint32, fixedTransformHint); + SAFE_PARCEL(output.writeUint64, frameNumber); + SAFE_PARCEL(output.writeInt64, frameTimelineVsyncId); + + SAFE_PARCEL(output.writeUint32, blurRegions.size()); + for (auto region : blurRegions) { + SAFE_PARCEL(output.writeUint32, region.blurRadius); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusTL); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusTR); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusBL); + SAFE_PARCEL(output.writeFloat, region.cornerRadiusBR); + SAFE_PARCEL(output.writeFloat, region.alpha); + SAFE_PARCEL(output.writeInt32, region.left); + SAFE_PARCEL(output.writeInt32, region.top); + SAFE_PARCEL(output.writeInt32, region.right); + SAFE_PARCEL(output.writeInt32, region.bottom); + } return NO_ERROR; } status_t layer_state_t::read(const Parcel& input) { - surface = input.readStrongBinder(); - what = input.readUint64(); - x = input.readFloat(); - y = input.readFloat(); - z = input.readInt32(); - w = input.readUint32(); - h = input.readUint32(); - layerStack = input.readUint32(); - alpha = input.readFloat(); - flags = static_cast<uint8_t>(input.readUint32()); - mask = static_cast<uint8_t>(input.readUint32()); - const void* matrix_data = input.readInplace(sizeof(layer_state_t::matrix22_t)); - if (matrix_data) { - matrix = *reinterpret_cast<layer_state_t::matrix22_t const *>(matrix_data); - } else { - return BAD_VALUE; - } - input.read(crop_legacy); - barrierHandle_legacy = input.readStrongBinder(); - reparentHandle = input.readStrongBinder(); - frameNumber_legacy = input.readUint64(); - overrideScalingMode = input.readInt32(); - barrierGbp_legacy = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); - relativeLayerHandle = input.readStrongBinder(); - parentHandleForChild = input.readStrongBinder(); - color.r = input.readFloat(); - color.g = input.readFloat(); - color.b = input.readFloat(); - + SAFE_PARCEL(input.readNullableStrongBinder, &surface); + SAFE_PARCEL(input.readInt32, &layerId); + SAFE_PARCEL(input.readUint64, &what); + SAFE_PARCEL(input.readFloat, &x); + SAFE_PARCEL(input.readFloat, &y); + SAFE_PARCEL(input.readInt32, &z); + SAFE_PARCEL(input.readUint32, &w); + SAFE_PARCEL(input.readUint32, &h); + SAFE_PARCEL(input.readUint32, &layerStack); + SAFE_PARCEL(input.readFloat, &alpha); + + uint32_t tmpUint32 = 0; + SAFE_PARCEL(input.readUint32, &tmpUint32); + flags = static_cast<uint8_t>(tmpUint32); + + SAFE_PARCEL(input.readUint32, &tmpUint32); + mask = static_cast<uint8_t>(tmpUint32); + + SAFE_PARCEL(matrix.read, input); + SAFE_PARCEL(input.read, crop_legacy); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &barrierSurfaceControl_legacy); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &reparentSurfaceControl); + SAFE_PARCEL(input.readUint64, &barrierFrameNumber); + + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl); + SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild); + + float tmpFloat = 0; + SAFE_PARCEL(input.readFloat, &tmpFloat); + color.r = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + color.g = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + color.b = tmpFloat; #ifndef NO_INPUT - inputInfo = InputWindowInfo::read(input); + SAFE_PARCEL(inputHandle->readFromParcel, &input); #endif - input.read(transparentRegion); - transform = input.readUint32(); - transformToDisplayInverse = input.readBool(); - input.read(crop); - input.read(frame); - buffer = new GraphicBuffer(); - if (input.readBool()) { - input.read(*buffer); - } - acquireFence = new Fence(); - if (input.readBool()) { - input.read(*acquireFence); - } - dataspace = static_cast<ui::Dataspace>(input.readUint32()); - input.read(hdrMetadata); - input.read(surfaceDamageRegion); - api = input.readInt32(); - if (input.readBool()) { - sidebandStream = NativeHandle::create(input.readNativeHandle(), true); + SAFE_PARCEL(input.read, transparentRegion); + SAFE_PARCEL(input.readUint32, &transform); + SAFE_PARCEL(input.readBool, &transformToDisplayInverse); + SAFE_PARCEL(input.read, crop); + SAFE_PARCEL(input.read, orientedDisplaySpaceRect); + + bool tmpBool = false; + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + buffer = new GraphicBuffer(); + SAFE_PARCEL(input.read, *buffer); } - const void* color_transform_data = input.readInplace(16 * sizeof(float)); - if (color_transform_data) { - colorTransform = mat4(static_cast<const float*>(color_transform_data)); - } else { - return BAD_VALUE; + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + acquireFence = new Fence(); + SAFE_PARCEL(input.read, *acquireFence); + } + + SAFE_PARCEL(input.readUint32, &tmpUint32); + dataspace = static_cast<ui::Dataspace>(tmpUint32); + + SAFE_PARCEL(input.read, hdrMetadata); + SAFE_PARCEL(input.read, surfaceDamageRegion); + SAFE_PARCEL(input.readInt32, &api); + SAFE_PARCEL(input.readBool, &tmpBool); + if (tmpBool) { + sidebandStream = NativeHandle::create(input.readNativeHandle(), true); } - cornerRadius = input.readFloat(); - backgroundBlurRadius = input.readUint32(); - cachedBuffer.token = input.readStrongBinder(); - cachedBuffer.id = input.readUint64(); - input.readParcelable(&metadata); - bgColorAlpha = input.readFloat(); - bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32()); - colorSpaceAgnostic = input.readBool(); + SAFE_PARCEL(input.read, &colorTransform, 16 * sizeof(float)); + SAFE_PARCEL(input.readFloat, &cornerRadius); + SAFE_PARCEL(input.readUint32, &backgroundBlurRadius); + sp<IBinder> tmpBinder; + SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); + cachedBuffer.token = tmpBinder; + SAFE_PARCEL(input.readUint64, &cachedBuffer.id); + SAFE_PARCEL(input.readParcelable, &metadata); + + SAFE_PARCEL(input.readFloat, &bgColorAlpha); + SAFE_PARCEL(input.readUint32, &tmpUint32); + bgColorDataspace = static_cast<ui::Dataspace>(tmpUint32); + SAFE_PARCEL(input.readBool, &colorSpaceAgnostic); - int32_t numListeners = input.readInt32(); + int32_t numListeners = 0; + SAFE_PARCEL_READ_SIZE(input.readInt32, &numListeners, input.dataSize()); listeners.clear(); for (int i = 0; i < numListeners; i++) { - auto listener = input.readStrongBinder(); + sp<IBinder> listener; std::vector<CallbackId> callbackIds; - input.readInt64Vector(&callbackIds); + SAFE_PARCEL(input.readNullableStrongBinder, &listener); + SAFE_PARCEL(input.readInt64Vector, &callbackIds); listeners.emplace_back(listener, callbackIds); } - shadowRadius = input.readFloat(); - frameRateSelectionPriority = input.readInt32(); - frameRate = input.readFloat(); - frameRateCompatibility = input.readByte(); - fixedTransformHint = static_cast<ui::Transform::RotationFlags>(input.readUint32()); + SAFE_PARCEL(input.readFloat, &shadowRadius); + SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority); + SAFE_PARCEL(input.readFloat, &frameRate); + SAFE_PARCEL(input.readByte, &frameRateCompatibility); + SAFE_PARCEL(input.readUint32, &tmpUint32); + fixedTransformHint = static_cast<ui::Transform::RotationFlags>(tmpUint32); + SAFE_PARCEL(input.readUint64, &frameNumber); + SAFE_PARCEL(input.readInt64, &frameTimelineVsyncId); + + uint32_t numRegions = 0; + SAFE_PARCEL(input.readUint32, &numRegions); + blurRegions.clear(); + for (uint32_t i = 0; i < numRegions; i++) { + BlurRegion region; + SAFE_PARCEL(input.readUint32, ®ion.blurRadius); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTL); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusTR); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBL); + SAFE_PARCEL(input.readFloat, ®ion.cornerRadiusBR); + SAFE_PARCEL(input.readFloat, ®ion.alpha); + SAFE_PARCEL(input.readInt32, ®ion.left); + SAFE_PARCEL(input.readInt32, ®ion.top); + SAFE_PARCEL(input.readInt32, ®ion.right); + SAFE_PARCEL(input.readInt32, ®ion.bottom); + blurRegions.push_back(region); + } return NO_ERROR; } @@ -216,39 +295,43 @@ status_t ComposerState::read(const Parcel& input) { return state.read(input); } - -DisplayState::DisplayState() : - what(0), - layerStack(0), - viewport(Rect::EMPTY_RECT), - frame(Rect::EMPTY_RECT), - width(0), - height(0) { -} +DisplayState::DisplayState() + : what(0), + layerStack(0), + layerStackSpaceRect(Rect::EMPTY_RECT), + orientedDisplaySpaceRect(Rect::EMPTY_RECT), + width(0), + height(0) {} status_t DisplayState::write(Parcel& output) const { - output.writeStrongBinder(token); - output.writeStrongBinder(IInterface::asBinder(surface)); - output.writeUint32(what); - output.writeUint32(layerStack); - output.writeUint32(toRotationInt(orientation)); - output.write(viewport); - output.write(frame); - output.writeUint32(width); - output.writeUint32(height); + SAFE_PARCEL(output.writeStrongBinder, token); + SAFE_PARCEL(output.writeStrongBinder, IInterface::asBinder(surface)); + SAFE_PARCEL(output.writeUint32, what); + SAFE_PARCEL(output.writeUint32, layerStack); + SAFE_PARCEL(output.writeUint32, toRotationInt(orientation)); + SAFE_PARCEL(output.write, layerStackSpaceRect); + SAFE_PARCEL(output.write, orientedDisplaySpaceRect); + SAFE_PARCEL(output.writeUint32, width); + SAFE_PARCEL(output.writeUint32, height); return NO_ERROR; } status_t DisplayState::read(const Parcel& input) { - token = input.readStrongBinder(); - surface = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); - what = input.readUint32(); - layerStack = input.readUint32(); - orientation = ui::toRotation(input.readUint32()); - input.read(viewport); - input.read(frame); - width = input.readUint32(); - height = input.readUint32(); + SAFE_PARCEL(input.readStrongBinder, &token); + sp<IBinder> tmpBinder; + SAFE_PARCEL(input.readNullableStrongBinder, &tmpBinder); + surface = interface_cast<IGraphicBufferProducer>(tmpBinder); + + SAFE_PARCEL(input.readUint32, &what); + SAFE_PARCEL(input.readUint32, &layerStack); + uint32_t tmpUint = 0; + SAFE_PARCEL(input.readUint32, &tmpUint); + orientation = ui::toRotation(tmpUint); + + SAFE_PARCEL(input.read, layerStackSpaceRect); + SAFE_PARCEL(input.read, orientedDisplaySpaceRect); + SAFE_PARCEL(input.readUint32, &width); + SAFE_PARCEL(input.readUint32, &height); return NO_ERROR; } @@ -264,8 +347,8 @@ void DisplayState::merge(const DisplayState& other) { if (other.what & eDisplayProjectionChanged) { what |= eDisplayProjectionChanged; orientation = other.orientation; - viewport = other.viewport; - frame = other.frame; + layerStackSpaceRect = other.layerStackSpaceRect; + orientedDisplaySpaceRect = other.orientedDisplaySpaceRect; } if (other.what & eDisplaySizeChanged) { what |= eDisplaySizeChanged; @@ -324,19 +407,18 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eBackgroundBlurRadiusChanged; backgroundBlurRadius = other.backgroundBlurRadius; } + if (other.what & eBlurRegionsChanged) { + what |= eBlurRegionsChanged; + blurRegions = other.blurRegions; + } if (other.what & eDeferTransaction_legacy) { what |= eDeferTransaction_legacy; - barrierHandle_legacy = other.barrierHandle_legacy; - barrierGbp_legacy = other.barrierGbp_legacy; - frameNumber_legacy = other.frameNumber_legacy; - } - if (other.what & eOverrideScalingModeChanged) { - what |= eOverrideScalingModeChanged; - overrideScalingMode = other.overrideScalingMode; + barrierSurfaceControl_legacy = other.barrierSurfaceControl_legacy; + barrierFrameNumber = other.barrierFrameNumber; } if (other.what & eReparentChildren) { what |= eReparentChildren; - reparentHandle = other.reparentHandle; + reparentSurfaceControl = other.reparentSurfaceControl; } if (other.what & eDetachChildren) { what |= eDetachChildren; @@ -345,11 +427,11 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eRelativeLayerChanged; what &= ~eLayerChanged; z = other.z; - relativeLayerHandle = other.relativeLayerHandle; + relativeLayerSurfaceControl = other.relativeLayerSurfaceControl; } if (other.what & eReparent) { what |= eReparent; - parentHandleForChild = other.parentHandleForChild; + parentSurfaceControlForChild = other.parentSurfaceControlForChild; } if (other.what & eDestroySurface) { what |= eDestroySurface; @@ -368,7 +450,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eFrameChanged) { what |= eFrameChanged; - frame = other.frame; + orientedDisplaySpaceRect = other.orientedDisplaySpaceRect; } if (other.what & eBufferChanged) { what |= eBufferChanged; @@ -409,7 +491,7 @@ void layer_state_t::merge(const layer_state_t& other) { #ifndef NO_INPUT if (other.what & eInputInfoChanged) { what |= eInputInfoChanged; - inputInfo = other.inputInfo; + inputHandle = new InputWindowHandle(*other.inputHandle); } #endif @@ -444,6 +526,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eFixedTransformHintChanged; fixedTransformHint = other.fixedTransformHint; } + if (other.what & eFrameNumberChanged) { + what |= eFrameNumberChanged; + frameNumber = other.frameNumber; + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIu64 " what=0x%" PRIu64, @@ -451,22 +537,65 @@ void layer_state_t::merge(const layer_state_t& other) { } } +status_t layer_state_t::matrix22_t::write(Parcel& output) const { + SAFE_PARCEL(output.writeFloat, dsdx); + SAFE_PARCEL(output.writeFloat, dtdx); + SAFE_PARCEL(output.writeFloat, dtdy); + SAFE_PARCEL(output.writeFloat, dsdy); + return NO_ERROR; +} + +status_t layer_state_t::matrix22_t::read(const Parcel& input) { + SAFE_PARCEL(input.readFloat, &dsdx); + SAFE_PARCEL(input.readFloat, &dtdx); + SAFE_PARCEL(input.readFloat, &dtdy); + SAFE_PARCEL(input.readFloat, &dsdy); + return NO_ERROR; +} + // ------------------------------- InputWindowCommands ---------------------------------------- -void InputWindowCommands::merge(const InputWindowCommands& other) { +bool InputWindowCommands::merge(const InputWindowCommands& other) { + bool changes = false; +#ifndef NO_INPUT + changes |= !other.focusRequests.empty(); + focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()), + std::make_move_iterator(other.focusRequests.end())); +#endif + changes |= other.syncInputWindows && !syncInputWindows; syncInputWindows |= other.syncInputWindows; + return changes; +} + +bool InputWindowCommands::empty() const { + bool empty = true; +#ifndef NO_INPUT + empty = focusRequests.empty() && !syncInputWindows; +#endif + return empty; } void InputWindowCommands::clear() { +#ifndef NO_INPUT + focusRequests.clear(); +#endif syncInputWindows = false; } -void InputWindowCommands::write(Parcel& output) const { - output.writeBool(syncInputWindows); +status_t InputWindowCommands::write(Parcel& output) const { +#ifndef NO_INPUT + SAFE_PARCEL(output.writeParcelableVector, focusRequests); +#endif + SAFE_PARCEL(output.writeBool, syncInputWindows); + return NO_ERROR; } -void InputWindowCommands::read(const Parcel& input) { - syncInputWindows = input.readBool(); +status_t InputWindowCommands::read(const Parcel& input) { +#ifndef NO_INPUT + SAFE_PARCEL(input.readParcelableVector, &focusRequests); +#endif + SAFE_PARCEL(input.readBool, &syncInputWindows); + return NO_ERROR; } bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) { @@ -486,4 +615,110 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunc return true; } +// ---------------------------------------------------------------------------- + +status_t CaptureArgs::write(Parcel& output) const { + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(pixelFormat)); + SAFE_PARCEL(output.write, sourceCrop); + SAFE_PARCEL(output.writeFloat, frameScale); + SAFE_PARCEL(output.writeBool, captureSecureLayers); + SAFE_PARCEL(output.writeInt32, uid); + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(dataspace)); + SAFE_PARCEL(output.writeBool, allowProtected); + return NO_ERROR; +} + +status_t CaptureArgs::read(const Parcel& input) { + int32_t value = 0; + SAFE_PARCEL(input.readInt32, &value); + pixelFormat = static_cast<ui::PixelFormat>(value); + SAFE_PARCEL(input.read, sourceCrop); + SAFE_PARCEL(input.readFloat, &frameScale); + SAFE_PARCEL(input.readBool, &captureSecureLayers); + SAFE_PARCEL(input.readInt32, &uid); + SAFE_PARCEL(input.readInt32, &value); + dataspace = static_cast<ui::Dataspace>(value); + SAFE_PARCEL(input.readBool, &allowProtected); + return NO_ERROR; +} + +status_t DisplayCaptureArgs::write(Parcel& output) const { + SAFE_PARCEL(CaptureArgs::write, output); + + SAFE_PARCEL(output.writeStrongBinder, displayToken); + SAFE_PARCEL(output.writeUint32, width); + SAFE_PARCEL(output.writeUint32, height); + SAFE_PARCEL(output.writeBool, useIdentityTransform); + return NO_ERROR; +} + +status_t DisplayCaptureArgs::read(const Parcel& input) { + SAFE_PARCEL(CaptureArgs::read, input); + + SAFE_PARCEL(input.readStrongBinder, &displayToken); + SAFE_PARCEL(input.readUint32, &width); + SAFE_PARCEL(input.readUint32, &height); + SAFE_PARCEL(input.readBool, &useIdentityTransform); + return NO_ERROR; +} + +status_t LayerCaptureArgs::write(Parcel& output) const { + SAFE_PARCEL(CaptureArgs::write, output); + + SAFE_PARCEL(output.writeStrongBinder, layerHandle); + SAFE_PARCEL(output.writeInt32, excludeHandles.size()); + for (auto el : excludeHandles) { + SAFE_PARCEL(output.writeStrongBinder, el); + } + SAFE_PARCEL(output.writeBool, childrenOnly); + return NO_ERROR; +} + +status_t LayerCaptureArgs::read(const Parcel& input) { + SAFE_PARCEL(CaptureArgs::read, input); + + SAFE_PARCEL(input.readStrongBinder, &layerHandle); + + int32_t numExcludeHandles = 0; + SAFE_PARCEL_READ_SIZE(input.readInt32, &numExcludeHandles, input.dataSize()); + excludeHandles.reserve(numExcludeHandles); + for (int i = 0; i < numExcludeHandles; i++) { + sp<IBinder> binder; + SAFE_PARCEL(input.readStrongBinder, &binder); + excludeHandles.emplace(binder); + } + + SAFE_PARCEL(input.readBool, &childrenOnly); + return NO_ERROR; +} + +status_t ScreenCaptureResults::write(Parcel& output) const { + if (buffer != nullptr) { + SAFE_PARCEL(output.writeBool, true); + SAFE_PARCEL(output.write, *buffer); + } else { + SAFE_PARCEL(output.writeBool, false); + } + SAFE_PARCEL(output.writeBool, capturedSecureLayers); + SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(capturedDataspace)); + SAFE_PARCEL(output.writeInt32, result); + return NO_ERROR; +} + +status_t ScreenCaptureResults::read(const Parcel& input) { + bool hasGraphicBuffer; + SAFE_PARCEL(input.readBool, &hasGraphicBuffer); + if (hasGraphicBuffer) { + buffer = new GraphicBuffer(); + SAFE_PARCEL(input.read, *buffer); + } + + SAFE_PARCEL(input.readBool, &capturedSecureLayers); + uint32_t dataspace = 0; + SAFE_PARCEL(input.readUint32, &dataspace); + capturedDataspace = static_cast<ui::Dataspace>(dataspace); + SAFE_PARCEL(input.readInt32, &result); + return NO_ERROR; +} + }; // namespace android diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index d6f9e635f3..c1155ab73a 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -63,7 +63,8 @@ bool isInterceptorRegistrationOp(int op) { } // namespace -Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp) +Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp, + const sp<IBinder>& surfaceControlHandle) : mGraphicBufferProducer(bufferProducer), mCrop(Rect::EMPTY_RECT), mBufferAge(0), @@ -111,6 +112,7 @@ Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controll mProducerControlledByApp = controlledByApp; mSwapIntervalZero = false; mMaxBufferCount = NUM_BUFFER_SLOTS; + mSurfaceControlHandle = surfaceControlHandle; } Surface::~Surface() { @@ -1207,6 +1209,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER: res = dispatchGetLastQueuedBuffer(args); break; + case NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC: + res = dispatchSetFrameTimelineVsync(args); + break; default: res = NAME_NOT_FOUND; break; @@ -1499,7 +1504,7 @@ int Surface::dispatchGetLastQueuedBuffer(va_list args) { int result = mGraphicBufferProducer->getLastQueuedBuffer(&graphicBuffer, &spFence, matrix); if (graphicBuffer != nullptr) { - *buffer = reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()); + *buffer = graphicBuffer->toAHardwareBuffer(); AHardwareBuffer_acquire(*buffer); } else { *buffer = nullptr; @@ -1513,6 +1518,14 @@ int Surface::dispatchGetLastQueuedBuffer(va_list args) { return result; } +int Surface::dispatchSetFrameTimelineVsync(va_list args) { + ATRACE_CALL(); + auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t)); + + ALOGV("Surface::dispatchSetFrameTimelineVsync"); + return setFrameTimelineVsync(frameTimelineVsyncId); +} + bool Surface::transformToDisplayInverse() { return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) == NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; @@ -2277,4 +2290,9 @@ status_t Surface::setFrameRate(float frameRate, int8_t compatibility) { return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility); } +status_t Surface::setFrameTimelineVsync(int64_t frameTimelineVsyncId) { + return composerService()->setFrameTimelineVsync(mGraphicBufferProducer, + frameTimelineVsyncId); +} + }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 83bc06997a..039e9008e8 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -348,15 +348,25 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId) { // --------------------------------------------------------------------------- +// Initialize transaction id counter used to generate transaction ids +// Transactions will start counting at 1, 0 is used for invalid transactions +std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1; + +SurfaceComposerClient::Transaction::Transaction() { + mId = generateId(); +} + SurfaceComposerClient::Transaction::Transaction(const Transaction& other) - : mForceSynchronous(other.mForceSynchronous), + : mId(other.mId), + mForceSynchronous(other.mForceSynchronous), mTransactionNestCount(other.mTransactionNestCount), mAnimation(other.mAnimation), mEarlyWakeup(other.mEarlyWakeup), mExplicitEarlyWakeupStart(other.mExplicitEarlyWakeupStart), mExplicitEarlyWakeupEnd(other.mExplicitEarlyWakeupEnd), mContainsBuffer(other.mContainsBuffer), - mDesiredPresentTime(other.mDesiredPresentTime) { + mDesiredPresentTime(other.mDesiredPresentTime), + mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) { mDisplayStates = other.mDisplayStates; mComposerStates = other.mComposerStates; mInputWindowCommands = other.mInputWindowCommands; @@ -372,6 +382,10 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { return nullptr; } +int64_t SurfaceComposerClient::Transaction::generateId() { + return (((int64_t)getpid()) << 32) | idCounter++; +} + status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) { const uint32_t forceSynchronous = parcel->readUint32(); const uint32_t transactionNestCount = parcel->readUint32(); @@ -381,6 +395,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel const bool explicitEarlyWakeupEnd = parcel->readBool(); const bool containsBuffer = parcel->readBool(); const int64_t desiredPresentTime = parcel->readInt64(); + const int64_t frameTimelineVsyncId = parcel->readInt64(); size_t count = static_cast<size_t>(parcel->readUint32()); if (count > parcel->dataSize()) { @@ -418,7 +433,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel } for (size_t j = 0; j < numSurfaces; j++) { sp<SurfaceControl> surface; - surface = SurfaceControl::readFromParcel(parcel); + SAFE_PARCEL(SurfaceControl::readFromParcel, *parcel, &surface); listenerCallbacks[listener].surfaceControls.insert(surface); } } @@ -430,12 +445,14 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates; composerStates.reserve(count); for (size_t i = 0; i < count; i++) { - sp<IBinder> surfaceControlHandle = parcel->readStrongBinder(); + sp<IBinder> surfaceControlHandle; + SAFE_PARCEL(parcel->readStrongBinder, &surfaceControlHandle); ComposerState composerState; if (composerState.read(*parcel) == BAD_VALUE) { return BAD_VALUE; } + composerStates[surfaceControlHandle] = composerState; } @@ -451,6 +468,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel mExplicitEarlyWakeupEnd = explicitEarlyWakeupEnd; mContainsBuffer = containsBuffer; mDesiredPresentTime = desiredPresentTime; + mFrameTimelineVsyncId = frameTimelineVsyncId; mDisplayStates = displayStates; mListenerCallbacks = listenerCallbacks; mComposerStates = composerStates; @@ -480,6 +498,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const parcel->writeBool(mExplicitEarlyWakeupEnd); parcel->writeBool(mContainsBuffer); parcel->writeInt64(mDesiredPresentTime); + parcel->writeInt64(mFrameTimelineVsyncId); parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size())); for (auto const& displayState : mDisplayStates) { displayState.write(*parcel); @@ -494,13 +513,13 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const } parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size())); for (auto surfaceControl : callbackInfo.surfaceControls) { - surfaceControl->writeToParcel(parcel); + SAFE_PARCEL(surfaceControl->writeToParcel, *parcel); } } parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size())); - for (auto const& [surfaceHandle, composerState] : mComposerStates) { - parcel->writeStrongBinder(surfaceHandle); + for (auto const& [handle, composerState] : mComposerStates) { + SAFE_PARCEL(parcel->writeStrongBinder, handle); composerState.write(*parcel); } @@ -509,11 +528,11 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) { - for (auto const& [surfaceHandle, composerState] : other.mComposerStates) { - if (mComposerStates.count(surfaceHandle) == 0) { - mComposerStates[surfaceHandle] = composerState; + for (auto const& [handle, composerState] : other.mComposerStates) { + if (mComposerStates.count(handle) == 0) { + mComposerStates[handle] = composerState; } else { - mComposerStates[surfaceHandle].state.merge(composerState.state); + mComposerStates[handle].state.merge(composerState.state); } } @@ -555,6 +574,15 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup; mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart; mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd; + + // When merging vsync Ids we take the oldest one + if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID && + other.mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) { + mFrameTimelineVsyncId = std::max(mFrameTimelineVsyncId, other.mFrameTimelineVsyncId); + } else if (mFrameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID) { + mFrameTimelineVsyncId = other.mFrameTimelineVsyncId; + } + other.clear(); return *this; } @@ -572,6 +600,7 @@ void SurfaceComposerClient::Transaction::clear() { mExplicitEarlyWakeupStart = false; mExplicitEarlyWakeupEnd = false; mDesiredPresentTime = -1; + mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID; } void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { @@ -582,7 +611,8 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { uncacheBuffer.id = cacheId; sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {}); + sf->setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, {}, {}, 0, applyToken, {}, -1, + uncacheBuffer, false, {}, 0 /* Undefined transactionId */); } void SurfaceComposerClient::Transaction::cacheBuffers() { @@ -592,7 +622,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { size_t count = 0; for (auto& [handle, cs] : mComposerStates) { - layer_state_t* s = getLayerState(handle); + layer_state_t* s = &(mComposerStates[handle].state); if (!(s->what & layer_state_t::eBufferChanged)) { continue; } else if (s->what & layer_state_t::eCachedBufferChanged) { @@ -665,8 +695,6 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { } } - mListenerCallbacks.clear(); - cacheBuffers(); Vector<ComposerState> composerStates; @@ -679,10 +707,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { composerStates.add(kv.second); } - mComposerStates.clear(); - - displayStates = mDisplayStates; - mDisplayStates.clear(); + displayStates = std::move(mDisplayStates); if (mForceSynchronous) { flags |= ISurfaceComposer::eSynchronous; @@ -703,18 +728,16 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd; } - mForceSynchronous = false; - mAnimation = false; - mEarlyWakeup = false; - mExplicitEarlyWakeupStart = false; - mExplicitEarlyWakeupEnd = false; - sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands, - mDesiredPresentTime, + sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken, + mInputWindowCommands, mDesiredPresentTime, {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/, - hasListenerCallbacks, listenerCallbacks); - mInputWindowCommands.clear(); + hasListenerCallbacks, listenerCallbacks, mId); + mId = generateId(); + + // Clear the current states and flags + clear(); + mStatus = NO_ERROR; return NO_ERROR; } @@ -762,11 +785,16 @@ void SurfaceComposerClient::Transaction::setExplicitEarlyWakeupEnd() { mExplicitEarlyWakeupEnd = true; } -layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) { +layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) { + auto handle = sc->getHandle(); + if (mComposerStates.count(handle) == 0) { // we don't have it, add an initialized layer_state to our list ComposerState s; + s.state.surface = handle; + s.state.layerId = sc->getLayerId(); + mComposerStates[handle] = s; } @@ -837,8 +865,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer(const sp<SurfaceControl>& sc, const sp<IBinder>& relativeTo, - int32_t z) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelativeLayer( + const sp<SurfaceControl>& sc, const sp<SurfaceControl>& relativeTo, int32_t z) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; @@ -846,7 +874,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelat } s->what |= layer_state_t::eRelativeLayerChanged; s->what &= ~layer_state_t::eLayerChanged; - s->relativeLayerHandle = relativeTo; + s->relativeLayerSurfaceControl = relativeTo; s->z = z; registerSurfaceControlForCallback(sc); @@ -861,9 +889,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags mStatus = BAD_INDEX; return *this; } - if ((mask & layer_state_t::eLayerOpaque) || - (mask & layer_state_t::eLayerHidden) || - (mask & layer_state_t::eLayerSecure)) { + if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) || + (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot)) { s->what |= layer_state_t::eFlagsChanged; } s->flags &= ~mask; @@ -990,65 +1017,58 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackg return *this; } -SurfaceComposerClient::Transaction& -SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, - uint64_t frameNumber) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBlurRegions( + const sp<SurfaceControl>& sc, const std::vector<BlurRegion>& blurRegions) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDeferTransaction_legacy; - s->barrierHandle_legacy = handle; - s->frameNumber_legacy = frameNumber; - - registerSurfaceControlForCallback(sc); + s->what |= layer_state_t::eBlurRegionsChanged; + s->blurRegions = blurRegions; return *this; } SurfaceComposerClient::Transaction& -SurfaceComposerClient::Transaction::deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, - uint64_t frameNumber) { +SurfaceComposerClient::Transaction::deferTransactionUntil_legacy( + const sp<SurfaceControl>& sc, const sp<SurfaceControl>& barrierSurfaceControl, + uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } s->what |= layer_state_t::eDeferTransaction_legacy; - s->barrierGbp_legacy = barrierSurface->getIGraphicBufferProducer(); - s->frameNumber_legacy = frameNumber; + s->barrierSurfaceControl_legacy = barrierSurfaceControl; + s->barrierFrameNumber = frameNumber; registerSurfaceControlForCallback(sc); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparentChildren( - const sp<SurfaceControl>& sc, - const sp<IBinder>& newParentHandle) { + const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } s->what |= layer_state_t::eReparentChildren; - s->reparentHandle = newParentHandle; + s->reparentSurfaceControl = newParent; registerSurfaceControlForCallback(sc); return *this; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::reparent( - const sp<SurfaceControl>& sc, - const sp<IBinder>& newParentHandle) { + const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } s->what |= layer_state_t::eReparent; - s->parentHandleForChild = newParentHandle; + s->parentSurfaceControlForChild = newParent; registerSurfaceControlForCallback(sc); return *this; @@ -1137,7 +1157,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame return *this; } s->what |= layer_state_t::eFrameChanged; - s->frame = frame; + s->orientedDisplaySpaceRect = frame; registerSurfaceControlForCallback(sc); return *this; @@ -1308,43 +1328,28 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren( - const sp<SurfaceControl>& sc) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameNumber( + const sp<SurfaceControl>& sc, uint64_t frameNumber) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->what |= layer_state_t::eDetachChildren; - registerSurfaceControlForCallback(sc); + s->what |= layer_state_t::eFrameNumberChanged; + s->frameNumber = frameNumber; + return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverrideScalingMode( - const sp<SurfaceControl>& sc, int32_t overrideScalingMode) { +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachChildren( + const sp<SurfaceControl>& sc) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - - switch (overrideScalingMode) { - case NATIVE_WINDOW_SCALING_MODE_FREEZE: - case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: - case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: - case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: - case -1: - break; - default: - ALOGE("unknown scaling mode: %d", - overrideScalingMode); - mStatus = BAD_VALUE; - return *this; - } - - s->what |= layer_state_t::eOverrideScalingModeChanged; - s->overrideScalingMode = overrideScalingMode; + s->what |= layer_state_t::eDetachChildren; registerSurfaceControlForCallback(sc); return *this; @@ -1359,11 +1364,28 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInput mStatus = BAD_INDEX; return *this; } - s->inputInfo = info; + s->inputHandle = new InputWindowHandle(info); s->what |= layer_state_t::eInputInfoChanged; return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow( + const sp<IBinder>& token, const sp<IBinder>& focusedToken, nsecs_t timestampNanos, + int32_t displayId) { + FocusRequest request; + request.token = token; + request.focusedToken = focusedToken; + request.timestamp = timestampNanos; + request.displayId = displayId; + return setFocusedWindow(request); +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow( + const FocusRequest& request) { + mInputWindowCommands.focusRequests.push_back(request); + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() { mInputWindowCommands.syncInputWindows = true; return *this; @@ -1484,6 +1506,25 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixed return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineVsync( + int64_t frameTimelineVsyncId) { + mFrameTimelineVsyncId = frameTimelineVsyncId; + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineVsync( + const sp<SurfaceControl>& sc, int64_t frameTimelineVsyncId) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eFrameTimelineVsyncChanged; + s->frameTimelineVsyncId = frameTimelineVsyncId; + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -1530,8 +1571,8 @@ void SurfaceComposerClient::Transaction::setDisplayProjection(const sp<IBinder>& const Rect& displayRect) { DisplayState& s(getDisplayState(token)); s.orientation = orientation; - s.viewport = layerStackRect; - s.frame = displayRect; + s.layerStackSpaceRect = layerStackRect; + s.orientedDisplaySpaceRect = displayRect; s.what |= DisplayState::eDisplayProjectionChanged; mForceSynchronous = true; // TODO: do we actually still need this? } @@ -1599,11 +1640,11 @@ void SurfaceComposerClient::dispose() { sp<SurfaceControl> SurfaceComposerClient::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, - SurfaceControl* parent, + const sp<IBinder>& parentHandle, LayerMetadata metadata, uint32_t* outTransformHint) { sp<SurfaceControl> s; - createSurfaceChecked(name, w, h, format, &s, flags, parent, std::move(metadata), + createSurfaceChecked(name, w, h, format, &s, flags, parentHandle, std::move(metadata), outTransformHint); return s; } @@ -1622,14 +1663,16 @@ sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& sp<IGraphicBufferProducer> gbp; uint32_t transformHint = 0; + int32_t id = -1; err = mClient->createWithSurfaceParent(name, w, h, format, flags, parentGbp, - std::move(metadata), &handle, &gbp, &transformHint); + std::move(metadata), &handle, &gbp, &id, + &transformHint); if (outTransformHint) { *outTransformHint = transformHint; } ALOGE_IF(err, "SurfaceComposerClient::createWithSurfaceParent error %s", strerror(-err)); if (err == NO_ERROR) { - return new SurfaceControl(this, handle, gbp, transformHint); + return new SurfaceControl(this, handle, gbp, id, transformHint); } } return nullptr; @@ -1638,29 +1681,27 @@ sp<SurfaceControl> SurfaceComposerClient::createWithSurfaceParent(const String8& status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h, PixelFormat format, sp<SurfaceControl>* outSurface, uint32_t flags, - SurfaceControl* parent, LayerMetadata metadata, + const sp<IBinder>& parentHandle, + LayerMetadata metadata, uint32_t* outTransformHint) { sp<SurfaceControl> sur; status_t err = mStatus; if (mStatus == NO_ERROR) { sp<IBinder> handle; - sp<IBinder> parentHandle; sp<IGraphicBufferProducer> gbp; - if (parent != nullptr) { - parentHandle = parent->getHandle(); - } - uint32_t transformHint = 0; + int32_t id = -1; err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata), - &handle, &gbp, &transformHint); + &handle, &gbp, &id, &transformHint); + if (outTransformHint) { *outTransformHint = transformHint; } ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err)); if (err == NO_ERROR) { - *outSurface = new SurfaceControl(this, handle, gbp, transformHint); + *outSurface = new SurfaceControl(this, handle, gbp, id, transformHint); } } return err; @@ -1673,9 +1714,10 @@ sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFr sp<IBinder> handle; sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle(); - status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle); + int32_t layer_id = -1; + status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle, &layer_id); if (err == NO_ERROR) { - return new SurfaceControl(this, handle, nullptr, true /* owned */); + return new SurfaceControl(this, handle, nullptr, layer_id, true /* owned */); } return nullptr; } @@ -1743,27 +1785,24 @@ int SurfaceComposerClient::getActiveConfig(const sp<IBinder>& display) { return ComposerService::getComposerService()->getActiveConfig(display); } -status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, - float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { +status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs( + const sp<IBinder>& displayToken, int32_t defaultConfig, bool allowGroupSwitching, + float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, + float appRequestRefreshRateMax) { return ComposerService::getComposerService() - ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, primaryRefreshRateMin, - primaryRefreshRateMax, appRequestRefreshRateMin, - appRequestRefreshRateMax); + ->setDesiredDisplayConfigSpecs(displayToken, defaultConfig, allowGroupSwitching, + primaryRefreshRateMin, primaryRefreshRateMax, + appRequestRefreshRateMin, appRequestRefreshRateMax); } -status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t* outDefaultConfig, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) { +status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs( + const sp<IBinder>& displayToken, int32_t* outDefaultConfig, bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) { return ComposerService::getComposerService() - ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outPrimaryRefreshRateMin, - outPrimaryRefreshRateMax, outAppRequestRefreshRateMin, + ->getDesiredDisplayConfigSpecs(displayToken, outDefaultConfig, outAllowGroupSwitching, + outPrimaryRefreshRateMin, outPrimaryRefreshRateMax, + outAppRequestRefreshRateMin, outAppRequestRefreshRateMax); } @@ -1893,8 +1932,8 @@ status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayT return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness); } -status_t SurfaceComposerClient::notifyPowerHint(int32_t hintId) { - return ComposerService::getComposerService()->notifyPowerHint(hintId); +status_t SurfaceComposerClient::notifyPowerBoost(int32_t boostId) { + return ComposerService::getComposerService()->notifyPowerBoost(boostId); } status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor, @@ -1907,59 +1946,28 @@ status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColo // ---------------------------------------------------------------------------- -status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, bool captureSecureLayers, - sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers) { +status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs, + const sp<IScreenCaptureListener>& captureListener) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; - status_t ret = s->captureScreen(display, outBuffer, outCapturedSecureLayers, reqDataSpace, - reqPixelFormat, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, rotation, captureSecureLayers); - if (ret != NO_ERROR) { - return ret; - } - return ret; -} -status_t ScreenshotClient::capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, sp<GraphicBuffer>* outBuffer) { - bool ignored; - return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, rotation, false, outBuffer, ignored); + return s->captureDisplay(captureArgs, captureListener); } -status_t ScreenshotClient::capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer) { +status_t ScreenshotClient::captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; - return s->captureScreen(displayOrLayerStack, outDataspace, outBuffer); -} -status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - float frameScale, sp<GraphicBuffer>* outBuffer) { - sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (s == nullptr) return NO_INIT; - status_t ret = s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, - sourceCrop, {}, frameScale, false /* childrenOnly */); - return ret; + return s->captureDisplay(displayOrLayerStack, captureListener); } -status_t ScreenshotClient::captureChildLayers( - const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, ui::PixelFormat reqPixelFormat, - const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles, - float frameScale, sp<GraphicBuffer>* outBuffer) { +status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, + const sp<IScreenCaptureListener>& captureListener) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; - status_t ret = - s->captureLayers(layerHandle, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop, - excludeHandles, frameScale, true /* childrenOnly */); - return ret; + + return s->captureLayers(captureArgs, captureListener); } } // namespace android diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index a332a1f2a8..e842382ded 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -24,6 +24,7 @@ #include <android/native_window.h> #include <utils/Errors.h> +#include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/threads.h> @@ -46,11 +47,12 @@ namespace android { // ============================================================================ SurfaceControl::SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbp, + const sp<IGraphicBufferProducer>& gbp, int32_t layerId, uint32_t transform) : mClient(client), mHandle(handle), mGraphicBufferProducer(gbp), + mLayerId(layerId), mTransformHint(transform) {} SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) { @@ -58,6 +60,7 @@ SurfaceControl::SurfaceControl(const sp<SurfaceControl>& other) { mHandle = other->mHandle; mGraphicBufferProducer = other->mGraphicBufferProducer; mTransformHint = other->mTransformHint; + mLayerId = other->mLayerId; } SurfaceControl::~SurfaceControl() @@ -148,6 +151,10 @@ sp<IBinder> SurfaceControl::getHandle() const return mHandle; } +int32_t SurfaceControl::getLayerId() const { + return mLayerId; +} + sp<IGraphicBufferProducer> SurfaceControl::getIGraphicBufferProducer() const { Mutex::Autolock _l(mLock); @@ -169,31 +176,60 @@ void SurfaceControl::setTransformHint(uint32_t hint) { mTransformHint = hint; } -void SurfaceControl::writeToParcel(Parcel* parcel) -{ - parcel->writeStrongBinder(ISurfaceComposerClient::asBinder(mClient->getClient())); - parcel->writeStrongBinder(mHandle); - parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer)); - parcel->writeUint32(mTransformHint); -} - -sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) { - sp<IBinder> client = parcel->readStrongBinder(); - sp<IBinder> handle = parcel->readStrongBinder(); - if (client == nullptr || handle == nullptr) - { - ALOGE("Invalid parcel"); - return nullptr; - } +status_t SurfaceControl::writeToParcel(Parcel& parcel) { + SAFE_PARCEL(parcel.writeStrongBinder, ISurfaceComposerClient::asBinder(mClient->getClient())); + SAFE_PARCEL(parcel.writeStrongBinder, mHandle); + SAFE_PARCEL(parcel.writeStrongBinder, IGraphicBufferProducer::asBinder(mGraphicBufferProducer)); + SAFE_PARCEL(parcel.writeInt32, mLayerId); + SAFE_PARCEL(parcel.writeUint32, mTransformHint); + + return NO_ERROR; +} + +status_t SurfaceControl::readFromParcel(const Parcel& parcel, + sp<SurfaceControl>* outSurfaceControl) { + sp<IBinder> client; + sp<IBinder> handle; sp<IBinder> gbp; - parcel->readNullableStrongBinder(&gbp); + int32_t layerId; + uint32_t transformHint; + + SAFE_PARCEL(parcel.readStrongBinder, &client); + SAFE_PARCEL(parcel.readStrongBinder, &handle); + SAFE_PARCEL(parcel.readNullableStrongBinder, &gbp); + SAFE_PARCEL(parcel.readInt32, &layerId); + SAFE_PARCEL(parcel.readUint32, &transformHint); - uint32_t transformHint = parcel->readUint32(); // We aren't the original owner of the surface. - return new SurfaceControl(new SurfaceComposerClient( - interface_cast<ISurfaceComposerClient>(client)), - handle.get(), interface_cast<IGraphicBufferProducer>(gbp), + *outSurfaceControl = + new SurfaceControl(new SurfaceComposerClient( + interface_cast<ISurfaceComposerClient>(client)), + handle.get(), interface_cast<IGraphicBufferProducer>(gbp), layerId, transformHint); + + return NO_ERROR; +} + +status_t SurfaceControl::readNullableFromParcel(const Parcel& parcel, + sp<SurfaceControl>* outSurfaceControl) { + bool isNotNull; + SAFE_PARCEL(parcel.readBool, &isNotNull); + if (isNotNull) { + SAFE_PARCEL(SurfaceControl::readFromParcel, parcel, outSurfaceControl); + } + + return NO_ERROR; +} + +status_t SurfaceControl::writeNullableToParcel(Parcel& parcel, + const sp<SurfaceControl>& surfaceControl) { + auto isNotNull = surfaceControl != nullptr; + SAFE_PARCEL(parcel.writeBool, isNotNull); + if (isNotNull) { + SAFE_PARCEL(surfaceControl->writeToParcel, parcel); + } + + return NO_ERROR; } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SyncFeatures.cpp b/libs/gui/SyncFeatures.cpp index fcae05c8ad..1a8fc1a00a 100644 --- a/libs/gui/SyncFeatures.cpp +++ b/libs/gui/SyncFeatures.cpp @@ -27,8 +27,6 @@ #include <private/gui/SyncFeatures.h> -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); - namespace android { ANDROID_SINGLETON_STATIC_INSTANCE(SyncFeatures); @@ -40,8 +38,8 @@ SyncFeatures::SyncFeatures() : Singleton<SyncFeatures>(), EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); // This can only be called after EGL has been initialized; otherwise the // check below will abort. - const char* exts = eglQueryStringImplementationANDROID(dpy, EGL_EXTENSIONS); - LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryStringImplementationANDROID failed"); + const char* exts = eglQueryString(dpy, EGL_EXTENSIONS); + LOG_ALWAYS_FATAL_IF(exts == nullptr, "eglQueryString failed"); if (strstr(exts, "EGL_ANDROID_native_fence_sync")) { // This makes GLConsumer use the EGL_ANDROID_native_fence_sync // extension to create Android native fences to signal when all @@ -73,15 +71,7 @@ bool SyncFeatures::useNativeFenceSync() const { return mHasNativeFenceSync; } bool SyncFeatures::useFenceSync() const { -#ifdef DONT_USE_FENCE_SYNC - // on some devices it's better to not use EGL_KHR_fence_sync - // even if they have it - return false; -#else - // currently we shall only attempt to use EGL_KHR_fence_sync if - // USE_FENCE_SYNC is set in our makefile return !mHasNativeFenceSync && mHasFenceSync; -#endif } bool SyncFeatures::useWaitSync() const { return (useNativeFenceSync() || useFenceSync()) && mHasWaitSync; diff --git a/libs/gui/TransactionTracing.cpp b/libs/gui/TransactionTracing.cpp new file mode 100644 index 0000000000..eedc3df009 --- /dev/null +++ b/libs/gui/TransactionTracing.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gui/TransactionTracing.h" +#include "gui/ISurfaceComposer.h" + +#include <private/gui/ComposerService.h> + +namespace android { + +sp<TransactionTraceListener> TransactionTraceListener::sInstance = nullptr; +std::mutex TransactionTraceListener::sMutex; + +TransactionTraceListener::TransactionTraceListener() {} + +sp<TransactionTraceListener> TransactionTraceListener::getInstance() { + const std::lock_guard<std::mutex> lock(sMutex); + + if (sInstance == nullptr) { + sInstance = new TransactionTraceListener; + + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + sf->addTransactionTraceListener(sInstance); + } + + return sInstance; +} + +binder::Status TransactionTraceListener::onToggled(bool enabled) { + ALOGD("TransactionTraceListener: onToggled listener called"); + mTracingEnabled = enabled; + + return binder::Status::ok(); +} + +bool TransactionTraceListener::isTracingEnabled() { + return mTracingEnabled; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl new file mode 100644 index 0000000000..5cd12fdc2b --- /dev/null +++ b/libs/gui/aidl/android/gui/ITransactionTraceListener.aidl @@ -0,0 +1,6 @@ +package android.gui; + +/** @hide */ +interface ITransactionTraceListener { + void onToggled(boolean enabled); +}
\ No newline at end of file diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 2320771a38..2300e81aa7 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -66,22 +66,27 @@ class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener { public: - BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height, - bool enableTripleBuffering = true); + BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width, + int height, bool enableTripleBuffering = true); sp<IGraphicBufferProducer> getIGraphicBufferProducer() const { return mProducer; } + sp<Surface> getSurface(bool includeSurfaceControlHandle); void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ } - void onFrameReplaced(const BufferItem& item) override {onFrameAvailable(item);} + void onFrameReplaced(const BufferItem& item) override; void onFrameAvailable(const BufferItem& item) override; void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats); void setNextTransaction(SurfaceComposerClient::Transaction *t); - void update(const sp<SurfaceControl>& surface, int width, int height); + void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height); + void flushShadowQueue() { mFlushShadowQueue = true; } + + status_t setFrameRate(float frameRate, int8_t compatibility); + status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId); virtual ~BLASTBufferQueue() = default; @@ -93,8 +98,12 @@ private: BLASTBufferQueue(const BLASTBufferQueue& rhs); void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex); - Rect computeCrop(const BufferItem& item); + Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); + // Return true if we need to reject the buffer based on the scaling mode and the buffer size. + bool rejectBuffer(const BufferItem& item) const REQUIRES(mMutex); + bool maxBuffersAcquired() const REQUIRES(mMutex); + std::string mName; sp<SurfaceControl> mSurfaceControl; std::mutex mMutex; @@ -106,17 +115,19 @@ private: int32_t mNumFrameAvailable GUARDED_BY(mMutex); int32_t mNumAcquired GUARDED_BY(mMutex); - + bool mInitialCallbackReceived GUARDED_BY(mMutex) = false; struct PendingReleaseItem { BufferItem item; sp<Fence> releaseFence; }; std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex); + // Keep a reference to the currently presented buffer so we can release it when the next buffer + // is ready to be presented. PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex); - int mWidth GUARDED_BY(mMutex); - int mHeight GUARDED_BY(mMutex); + uint32_t mWidth GUARDED_BY(mMutex); + uint32_t mHeight GUARDED_BY(mMutex); uint32_t mTransformHint GUARDED_BY(mMutex); @@ -125,6 +136,9 @@ private: sp<BLASTBufferItemConsumer> mBufferItemConsumer; SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex); + // If set to true, the next queue buffer will wait until the shadow queue has been processed by + // the adapter. + bool mFlushShadowQueue = false; }; } // namespace android diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index eb5b00418a..c65618b31f 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -20,6 +20,17 @@ namespace android { +struct VsyncEventData { + // The Vsync Id corresponsing to this vsync event. This will be used to + // populate ISurfaceComposer::setFrameTimelineVsync and + // SurfaceComposerClient::setFrameTimelineVsync + int64_t id = ISurfaceComposer::INVALID_VSYNC_ID; + + // The deadline in CLOCK_MONOTONIC that the app needs to complete its + // frame by (both on the CPU and the GPU) + int64_t deadlineTimestamp = std::numeric_limits<int64_t>::max(); +}; + class DisplayEventDispatcher : public LooperCallback { public: explicit DisplayEventDispatcher( @@ -43,7 +54,8 @@ private: DisplayEventReceiver mReceiver; bool mWaitingForVsync; - virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; + virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, + VsyncEventData vsyncEventData) = 0; virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) = 0; virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, @@ -53,6 +65,6 @@ private: virtual void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) = 0; bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, - uint32_t* outCount); + uint32_t* outCount, VsyncEventData* outVsyncEventData); }; } // namespace android diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 0e10d1ad8e..df3118ff6d 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -57,16 +57,23 @@ public: }; struct Event { + // We add __attribute__((aligned(8))) for nsecs_t fields because + // we need to make sure all fields are aligned the same with x86 + // and x64 (long long has different default alignment): + // + // https://en.wikipedia.org/wiki/Data_structure_alignment struct Header { uint32_t type; - PhysicalDisplayId displayId; + PhysicalDisplayId displayId __attribute__((aligned(8))); nsecs_t timestamp __attribute__((aligned(8))); }; struct VSync { uint32_t count; - nsecs_t expectedVSyncTimestamp; + nsecs_t expectedVSyncTimestamp __attribute__((aligned(8))); + nsecs_t deadlineTimestamp __attribute__((aligned(8))); + int64_t vsyncId; }; struct Hotplug { @@ -75,7 +82,7 @@ public: struct Config { int32_t configId; - nsecs_t vsyncPeriod; + nsecs_t vsyncPeriod __attribute__((aligned(8))); }; Header header; diff --git a/libs/gui/include/gui/IScreenCaptureListener.h b/libs/gui/include/gui/IScreenCaptureListener.h new file mode 100644 index 0000000000..a2ddc9ffff --- /dev/null +++ b/libs/gui/include/gui/IScreenCaptureListener.h @@ -0,0 +1,45 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/Binder.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> +#include <binder/SafeInterface.h> + +namespace android { + +struct ScreenCaptureResults; + +// TODO(b/166271443): Convert to AIDL +class IScreenCaptureListener : public IInterface { +public: + DECLARE_META_INTERFACE(ScreenCaptureListener) + + virtual status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) = 0; +}; + +class BnScreenCaptureListener : public SafeBnInterface<IScreenCaptureListener> { +public: + BnScreenCaptureListener() + : SafeBnInterface<IScreenCaptureListener>("BnScreenCaptureListener") {} + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; +}; + +} // namespace android diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 8d3160a815..5cd9356449 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -22,16 +22,18 @@ #include <binder/IBinder.h> #include <binder/IInterface.h> +#include <android/gui/ITransactionTraceListener.h> +#include <gui/IScreenCaptureListener.h> #include <gui/ITransactionCompletedListener.h> #include <math/vec4.h> #include <ui/ConfigStoreTypes.h> +#include <ui/DisplayId.h> #include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> #include <ui/GraphicTypes.h> -#include <ui/PhysicalDisplayId.h> #include <ui/PixelFormat.h> #include <ui/Rotation.h> @@ -48,11 +50,14 @@ namespace android { struct client_cache_t; struct ComposerState; +struct DisplayCaptureArgs; struct DisplayConfig; struct DisplayInfo; struct DisplayStatInfo; struct DisplayState; struct InputWindowCommands; +struct LayerCaptureArgs; +struct ScreenCaptureResults; class LayerDebugInfo; class HdrCapabilities; class IDisplayEventConnection; @@ -104,6 +109,9 @@ public: enum ConfigChanged { eConfigChangedSuppress = 0, eConfigChangedDispatch = 1 }; + // Needs to be in sync with android.graphics.FrameInfo.INVALID_VSYNC_ID in java + static constexpr int64_t INVALID_VSYNC_ID = -1; + /* * Create a connection with SurfaceFlinger. */ @@ -147,13 +155,12 @@ public: } /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */ - virtual void setTransactionState(const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, - const InputWindowCommands& inputWindowCommands, - int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks) = 0; + virtual status_t setTransactionState( + int64_t frameTimelineVsyncId, const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, + const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) = 0; /* signal that we're done booting. * Requires ACCESS_SURFACE_FLINGER permission @@ -246,65 +253,17 @@ public: /** * Capture the specified screen. This requires READ_FRAME_BUFFER * permission. This function will fail if there is a secure window on - * screen. + * screen and DisplayCaptureArgs.captureSecureLayers is false. * * This function can capture a subregion (the source crop) of the screen. * The subregion can be optionally rotated. It will also be scaled to * match the size of the output buffer. - * - * reqDataspace and reqPixelFormat specify the data space and pixel format - * of the buffer. The caller should pick the data space and pixel format - * that it can consume. - * - * sourceCrop is the crop on the logical display. - * - * reqWidth and reqHeight specifies the size of the buffer. When either - * of them is 0, they are set to the size of the logical display viewport. - * - * When useIdentityTransform is true, layer transformations are disabled. - * - * rotation specifies the rotation of the source crop (and the pixels in - * it) around its center. - */ - virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers, ui::Dataspace reqDataspace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation = ui::ROTATION_0, - bool captureSecureLayers = false) = 0; - /** - * Capture the specified screen. This requires READ_FRAME_BUFFER - * permission. This function will fail if there is a secure window on - * screen. - * - * This function can capture a subregion (the source crop) of the screen - * into an sRGB buffer with RGBA_8888 pixel format. - * The subregion can be optionally rotated. It will also be scaled to - * match the size of the output buffer. - * - * At the moment, sourceCrop is ignored and is always set to the visible - * region (projected display viewport) of the screen. - * - * reqWidth and reqHeight specifies the size of the buffer. When either - * of them is 0, they are set to the size of the logical display viewport. - * - * When useIdentityTransform is true, layer transformations are disabled. - * - * rotation specifies the rotation of the source crop (and the pixels in - * it) around its center. - */ - virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer, - const Rect& sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - bool useIdentityTransform, - ui::Rotation rotation = ui::ROTATION_0) { - bool outIgnored; - return captureScreen(display, outBuffer, outIgnored, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, sourceCrop, reqWidth, reqHeight, - useIdentityTransform, rotation); - } + */ + virtual status_t captureDisplay(const DisplayCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) = 0; - virtual status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer) = 0; + virtual status_t captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener) = 0; template <class AA> struct SpHash { @@ -313,27 +272,11 @@ public: /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. - * - * reqDataspace and reqPixelFormat specify the data space and pixel format - * of the buffer. The caller should pick the data space and pixel format - * that it can consume. + * This requires READ_FRAME_BUFFER permission. This function will fail if there + * is a secure window on screen */ - virtual status_t captureLayers( - const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, - ui::Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, SpHash<IBinder>>& excludeHandles, - float frameScale = 1.0, bool childrenOnly = false) = 0; - - /** - * Capture a subtree of the layer hierarchy into an sRGB buffer with RGBA_8888 pixel format, - * potentially ignoring the root node. - */ - status_t captureLayers(const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, - const Rect& sourceCrop, float frameScale = 1.0, - bool childrenOnly = false) { - return captureLayers(layerHandleBinder, outBuffer, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, sourceCrop, {}, frameScale, childrenOnly); - } + virtual status_t captureLayers(const LayerCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) = 0; /* Clears the frame statistics for animations. * @@ -452,7 +395,7 @@ public: * returned from getDisplayConfigs(). */ virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, + int32_t defaultConfig, bool allowGroupSwitching, float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, @@ -460,6 +403,7 @@ public: virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t* outDefaultConfig, + bool* outAllowGroupSwitching, float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, float* outAppRequestRefreshRateMin, @@ -496,14 +440,14 @@ public: virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) = 0; /* - * Sends a power hint to the composer. This function is asynchronous. + * Sends a power boost to the composer. This function is asynchronous. * - * hintId - * hint id according to android::hardware::power::V1_0::PowerHint + * boostId + * boost id according to android::hardware::power::Boost * * Returns NO_ERROR upon success. */ - virtual status_t notifyPowerHint(int32_t hintId) = 0; + virtual status_t notifyPowerBoost(int32_t boostId) = 0; /* * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows @@ -540,6 +484,19 @@ public: * for tests. Release the token by releasing the returned IBinder reference. */ virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0; + + /* + * Sets the frame timeline vsync id received from choreographer that corresponds to next + * buffer submitted on that surface. + */ + virtual status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface, + int64_t frameTimelineVsyncId) = 0; + + /* + * Adds a TransactionTraceListener to listen for transaction tracing state updates. + */ + virtual status_t addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& listener) = 0; }; // ---------------------------------------------------------------------------- @@ -562,7 +519,7 @@ public: GET_DISPLAY_CONFIGS, GET_ACTIVE_CONFIG, GET_DISPLAY_STATE, - CAPTURE_SCREEN, + CAPTURE_DISPLAY, CAPTURE_LAYERS, CLEAR_ANIMATION_FRAME_STATS, GET_ANIMATION_FRAME_STATS, @@ -590,8 +547,8 @@ public: GET_DESIRED_DISPLAY_CONFIG_SPECS, GET_DISPLAY_BRIGHTNESS_SUPPORT, SET_DISPLAY_BRIGHTNESS, - CAPTURE_SCREEN_BY_ID, - NOTIFY_POWER_HINT, + CAPTURE_DISPLAY_BY_ID, + NOTIFY_POWER_BOOST, SET_GLOBAL_SHADOW_SETTINGS, GET_AUTO_LOW_LATENCY_MODE_SUPPORT, SET_AUTO_LOW_LATENCY_MODE, @@ -599,6 +556,8 @@ public: SET_GAME_CONTENT_TYPE, SET_FRAME_RATE, ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, + SET_FRAME_TIMELINE_VSYNC, + ADD_TRANSACTION_TRACE_LISTENER, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h index 3afbabf1dc..9e9e191480 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -36,6 +36,7 @@ public: enum { // (keep in sync with SurfaceControl.java) eHidden = 0x00000004, eDestroyBackbuffer = 0x00000020, + eSkipScreenshot = 0x00000040, eSecure = 0x00000080, eNonPremultiplied = 0x00000100, eOpaque = 0x00000400, @@ -51,13 +52,15 @@ public: eFXSurfaceMask = 0x000F0000, }; + // TODO(b/172002646): Clean up the Surface Creation Arguments /* * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) = 0; + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, + uint32_t* outTransformHint) = 0; /* * Requires ACCESS_SURFACE_FLINGER permission @@ -66,7 +69,7 @@ public: PixelFormat format, uint32_t flags, const sp<IGraphicBufferProducer>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, uint32_t* outTransformHint) = 0; /* @@ -79,7 +82,8 @@ public: */ virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0; - virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0; + virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, + int32_t* outLayerId) = 0; }; class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> { diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index d58e019799..ac48aefae6 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -27,6 +27,8 @@ enum { METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3, METADATA_MOUSE_CURSOR = 4, + METADATA_ACCESSIBILITY_ID = 5, + METADATA_OWNER_PID = 6, }; struct LayerMetadata : public Parcelable { diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index e60f6777ae..a73d9a68a5 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -16,6 +16,23 @@ #ifndef ANDROID_SF_LAYER_STATE_H #define ANDROID_SF_LAYER_STATE_H +#define SAFE_PARCEL(FUNC, ...) \ + { \ + status_t error = FUNC(__VA_ARGS__); \ + if (error) { \ + ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \ + return error; \ + } \ + } + +#define SAFE_PARCEL_READ_SIZE(FUNC, COUNT, SIZE) \ + { \ + SAFE_PARCEL(FUNC, COUNT); \ + if (static_cast<unsigned int>(*COUNT) > SIZE) { \ + ALOGE("ERROR(BAD_VALUE). %s was greater than dataSize", #COUNT); \ + return BAD_VALUE; \ + } \ + } #include <stdint.h> #include <sys/types.h> @@ -26,11 +43,15 @@ #include <math/mat4.h> #ifndef NO_INPUT +#include <android/FocusRequest.h> #include <input/InputWindow.h> #endif +#include <gui/ISurfaceComposer.h> #include <gui/LayerMetadata.h> +#include <gui/SurfaceControl.h> #include <math/vec3.h> +#include <ui/BlurRegion.h> #include <ui/GraphicTypes.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -57,9 +78,10 @@ struct client_cache_t { */ struct layer_state_t { enum { - eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java - eLayerOpaque = 0x02, // SURFACE_OPAQUE - eLayerSecure = 0x80, // SECURE + eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java + eLayerOpaque = 0x02, // SURFACE_OPAQUE + eLayerSkipScreenshot = 0x40, // SKIP_SCREENSHOT + eLayerSecure = 0x80, // SECURE }; enum { @@ -73,7 +95,7 @@ struct layer_state_t { eLayerStackChanged = 0x00000080, eCropChanged_legacy = 0x00000100, eDeferTransaction_legacy = 0x00000200, - eOverrideScalingModeChanged = 0x00000400, + /* was ScalingModeChanged, now available 0x00000400, */ eShadowRadiusChanged = 0x00000800, eReparentChildren = 0x00001000, eDetachChildren = 0x00002000, @@ -105,45 +127,12 @@ struct layer_state_t { eBackgroundBlurRadiusChanged = 0x80'00000000, eProducerDisconnect = 0x100'00000000, eFixedTransformHintChanged = 0x200'00000000, + eFrameNumberChanged = 0x400'00000000, + eFrameTimelineVsyncChanged = 0x800'00000000, + eBlurRegionsChanged = 0x1000'00000000, }; - layer_state_t() - : what(0), - x(0), - y(0), - z(0), - w(0), - h(0), - layerStack(0), - alpha(0), - flags(0), - mask(0), - reserved(0), - crop_legacy(Rect::INVALID_RECT), - cornerRadius(0.0f), - backgroundBlurRadius(0), - frameNumber_legacy(0), - overrideScalingMode(-1), - transform(0), - transformToDisplayInverse(false), - crop(Rect::INVALID_RECT), - frame(Rect::INVALID_RECT), - dataspace(ui::Dataspace::UNKNOWN), - surfaceDamageRegion(), - api(-1), - colorTransform(mat4()), - bgColorAlpha(0), - bgColorDataspace(ui::Dataspace::UNKNOWN), - colorSpaceAgnostic(false), - shadowRadius(0.0f), - frameRateSelectionPriority(-1), - frameRate(0.0f), - frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT), - fixedTransformHint(ui::Transform::ROT_INVALID) { - matrix.dsdx = matrix.dtdy = 1.0f; - matrix.dsdy = matrix.dtdx = 0.0f; - hdrMetadata.validTypes = 0; - } + layer_state_t(); void merge(const layer_state_t& other); status_t write(Parcel& output) const; @@ -154,8 +143,11 @@ struct layer_state_t { float dtdx{0}; float dtdy{0}; float dsdy{0}; + status_t write(Parcel& output) const; + status_t read(const Parcel& input); }; sp<IBinder> surface; + int32_t layerId; uint64_t what; float x; float y; @@ -171,16 +163,13 @@ struct layer_state_t { Rect crop_legacy; float cornerRadius; uint32_t backgroundBlurRadius; - sp<IBinder> barrierHandle_legacy; - sp<IBinder> reparentHandle; - uint64_t frameNumber_legacy; - int32_t overrideScalingMode; + sp<SurfaceControl> barrierSurfaceControl_legacy; + sp<SurfaceControl> reparentSurfaceControl; + uint64_t barrierFrameNumber; - sp<IGraphicBufferProducer> barrierGbp_legacy; + sp<SurfaceControl> relativeLayerSurfaceControl; - sp<IBinder> relativeLayerHandle; - - sp<IBinder> parentHandleForChild; + sp<SurfaceControl> parentSurfaceControlForChild; half3 color; @@ -190,7 +179,7 @@ struct layer_state_t { uint32_t transform; bool transformToDisplayInverse; Rect crop; - Rect frame; + Rect orientedDisplaySpaceRect; sp<GraphicBuffer> buffer; sp<Fence> acquireFence; ui::Dataspace dataspace; @@ -199,9 +188,10 @@ struct layer_state_t { int32_t api; sp<NativeHandle> sidebandStream; mat4 colorTransform; + std::vector<BlurRegion> blurRegions; #ifndef NO_INPUT - InputWindowInfo inputInfo; + sp<InputWindowHandle> inputHandle = new InputWindowHandle(); #endif client_cache_t cachedBuffer; @@ -237,6 +227,12 @@ struct layer_state_t { // a buffer of a different size. -1 means the transform hint is not set, // otherwise the value will be a valid ui::Rotation. ui::Transform::RotationFlags fixedTransformHint; + + // Used by BlastBufferQueue to forward the framenumber generated by the + // graphics producer. + uint64_t frameNumber; + + int64_t frameTimelineVsyncId; }; struct ComposerState { @@ -263,18 +259,18 @@ struct DisplayState { // These states define how layers are projected onto the physical display. // - // Layers are first clipped to `viewport'. They are then translated and - // scaled from `viewport' to `frame'. Finally, they are rotated according - // to `orientation', `width', and `height'. + // Layers are first clipped to `layerStackSpaceRect'. They are then translated and + // scaled from `layerStackSpaceRect' to `orientedDisplaySpaceRect'. Finally, they are rotated + // according to `orientation', `width', and `height'. // - // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20, - // 10, 420, 210), and the size of the display is WxH. When orientation is - // 0, layers will be scaled by a factor of 2 and translated by (20, 10). - // When orientation is 1, layers will be additionally rotated by 90 - // degrees around the origin clockwise and translated by (W, 0). + // For example, assume layerStackSpaceRect is Rect(0, 0, 200, 100), orientedDisplaySpaceRect is + // Rect(20, 10, 420, 210), and the size of the display is WxH. When orientation is 0, layers + // will be scaled by a factor of 2 and translated by (20, 10). When orientation is 1, layers + // will be additionally rotated by 90 degrees around the origin clockwise and translated by (W, + // 0). ui::Rotation orientation = ui::ROTATION_0; - Rect viewport; - Rect frame; + Rect layerStackSpaceRect; + Rect orientedDisplaySpaceRect; uint32_t width, height; @@ -283,12 +279,17 @@ struct DisplayState { }; struct InputWindowCommands { +#ifndef NO_INPUT + std::vector<FocusRequest> focusRequests; +#endif bool syncInputWindows{false}; - void merge(const InputWindowCommands& other); + // Merges the passed in commands and returns true if there were any changes. + bool merge(const InputWindowCommands& other); + bool empty() const; void clear(); - void write(Parcel& output) const; - void read(const Parcel& input); + status_t write(Parcel& output) const; + status_t read(const Parcel& input); }; static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { @@ -307,6 +308,63 @@ static inline int compare_type(const DisplayState& lhs, const DisplayState& rhs) // functionName can be null. bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName); +struct CaptureArgs { + const static int32_t UNSET_UID = -1; + virtual ~CaptureArgs() = default; + + ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888}; + Rect sourceCrop; + float frameScale{1}; + bool captureSecureLayers{false}; + int32_t uid{UNSET_UID}; + // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured + // result will be in the display's colorspace. + // The display may use non-RGB dataspace (ex. displayP3) that could cause pixel data could be + // different from SRGB (byte per color), and failed when checking colors in tests. + // NOTE: In normal cases, we want the screen to be captured in display's colorspace. + ui::Dataspace dataspace = ui::Dataspace::UNKNOWN; + + // The receiver of the capture can handle protected buffer. A protected buffer has + // GRALLOC_USAGE_PROTECTED usage bit and must not be accessed unprotected behaviour. + // Any read/write access from unprotected context will result in undefined behaviour. + // Protected contents are typically DRM contents. This has no direct implication to the + // secure property of the surface, which is specified by the application explicitly to avoid + // the contents being accessed/captured by screenshot or unsecure display. + bool allowProtected = false; + + virtual status_t write(Parcel& output) const; + virtual status_t read(const Parcel& input); +}; + +struct DisplayCaptureArgs : CaptureArgs { + sp<IBinder> displayToken; + uint32_t width{0}; + uint32_t height{0}; + bool useIdentityTransform{false}; + + status_t write(Parcel& output) const override; + status_t read(const Parcel& input) override; +}; + +struct LayerCaptureArgs : CaptureArgs { + sp<IBinder> layerHandle; + std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeHandles; + bool childrenOnly{false}; + + status_t write(Parcel& output) const override; + status_t read(const Parcel& input) override; +}; + +struct ScreenCaptureResults { + sp<GraphicBuffer> buffer; + bool capturedSecureLayers{false}; + ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB}; + status_t result = OK; + + status_t write(Parcel& output) const; + status_t read(const Parcel& input); +}; + }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 55b4101908..4aa076e7b2 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -68,7 +68,6 @@ class Surface : public ANativeObjectBase<ANativeWindow, Surface, RefBase> { public: - /* * creates a Surface from the given IGraphicBufferProducer (which concrete * implementation is a BufferQueue). @@ -83,9 +82,15 @@ public: * * the controlledByApp flag indicates that this Surface (producer) is * controlled by the application. This flag is used at connect time. + * + * Pass in the SurfaceControlHandle to store a weak reference to the layer + * that the Surface was created from. This handle can be used to create a + * child surface without using the IGBP to identify the layer. This is used + * for surfaces created by the BlastBufferQueue whose IGBP is created on the + * client and cannot be verified in SF. */ - explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, - bool controlledByApp = false); + explicit Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controlledByApp = false, + const sp<IBinder>& surfaceControlHandle = nullptr); /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this * Surface was created with. Usually it's an error to use the @@ -93,6 +98,8 @@ public: */ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const; + sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; } + /* convenience function to check that the given surface is non NULL as * well as its IGraphicBufferProducer */ static bool isValid(const sp<Surface>& surface) { @@ -120,7 +127,7 @@ public: * delay during dequeueBuffer. If there are already the maximum number of * buffers allocated, this function has no effect. */ - void allocateBuffers(); + virtual void allocateBuffers(); /* Sets the generation number on the IGraphicBufferProducer and updates the * generation number on any buffers attached to the Surface after this call. @@ -179,7 +186,8 @@ public: status_t getUniqueId(uint64_t* outId) const; status_t getConsumerUsage(uint64_t* outUsage) const; - status_t setFrameRate(float frameRate, int8_t compatibility); + virtual status_t setFrameRate(float frameRate, int8_t compatibility); + virtual status_t setFrameTimelineVsync(int64_t frameTimelineVsyncId); protected: virtual ~Surface(); @@ -265,6 +273,7 @@ private: int dispatchAddQueueInterceptor(va_list args); int dispatchAddQueryInterceptor(va_list args); int dispatchGetLastQueuedBuffer(va_list args); + int dispatchSetFrameTimelineVsync(va_list args); bool transformToDisplayInverse(); protected: @@ -539,6 +548,11 @@ protected: bool mEnableFrameTimestamps = false; std::unique_ptr<ProducerFrameEventHistory> mFrameEventHistory; + // Reference to the SurfaceFlinger layer that was used to create this + // surface. This is only populated when the Surface is created from + // a BlastBufferQueue. + sp<IBinder> mSurfaceControlHandle; + bool mReportRemovedBuffers = false; std::vector<sp<GraphicBuffer>> mRemovedBuffers; int mMaxBufferCount; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index adcb8982a0..73909a30bb 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -29,6 +29,7 @@ #include <utils/SortedVector.h> #include <utils/threads.h> +#include <ui/BlurRegion.h> #include <ui/ConfigStoreTypes.h> #include <ui/DisplayedFrameStats.h> #include <ui/FrameStats.h> @@ -120,13 +121,15 @@ public: // Sets the refresh rate boundaries for the display. static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, float primaryRefreshRateMin, + int32_t defaultConfig, bool allowGroupSwitching, + float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, float appRequestRefreshRateMax); // Gets the refresh rate boundaries for the display. static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t* outDefaultConfig, + bool* outAllowGroupSwitching, float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, float* outAppRequestRefreshRateMin, @@ -217,14 +220,14 @@ public: static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness); /* - * Sends a power hint to the composer. This function is asynchronous. + * Sends a power boost to the composer. This function is asynchronous. * - * hintId - * hint id according to android::hardware::power::V1_0::PowerHint + * boostId + * boost id according to android::hardware::power::Boost * * Returns NO_ERROR upon success. */ - static status_t notifyPowerHint(int32_t hintId); + static status_t notifyPowerBoost(int32_t boostId); /* * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows @@ -253,13 +256,13 @@ public: static sp<SurfaceComposerClient> getDefault(); //! Create a surface - sp<SurfaceControl> createSurface(const String8& name, // name of the surface - uint32_t w, // width in pixel - uint32_t h, // height in pixel - PixelFormat format, // pixel-format desired - uint32_t flags = 0, // usage flags - SurfaceControl* parent = nullptr, // parent - LayerMetadata metadata = LayerMetadata(), // metadata + sp<SurfaceControl> createSurface(const String8& name, // name of the surface + uint32_t w, // width in pixel + uint32_t h, // height in pixel + PixelFormat format, // pixel-format desired + uint32_t flags = 0, // usage flags + const sp<IBinder>& parentHandle = nullptr, // parentHandle + LayerMetadata metadata = LayerMetadata(), // metadata uint32_t* outTransformHint = nullptr); status_t createSurfaceChecked(const String8& name, // name of the surface @@ -267,9 +270,9 @@ public: uint32_t h, // height in pixel PixelFormat format, // pixel-format desired sp<SurfaceControl>* outSurface, - uint32_t flags = 0, // usage flags - SurfaceControl* parent = nullptr, // parent - LayerMetadata metadata = LayerMetadata(), // metadata + uint32_t flags = 0, // usage flags + const sp<IBinder>& parentHandle = nullptr, // parentHandle + LayerMetadata metadata = LayerMetadata(), // metadata uint32_t* outTransformHint = nullptr); //! Create a surface @@ -339,12 +342,18 @@ public: }; class Transaction : public Parcelable { + private: + static std::atomic<uint32_t> idCounter; + int64_t generateId(); + protected: std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates; - SortedVector<DisplayState > mDisplayStates; + SortedVector<DisplayState> mDisplayStates; std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> mListenerCallbacks; + uint64_t mId; + uint32_t mForceSynchronous = 0; uint32_t mTransactionNestCount = 0; bool mAnimation = false; @@ -367,20 +376,20 @@ public: // The desired present time does not affect this ordering. int64_t mDesiredPresentTime = -1; + // The vsync Id provided by Choreographer.getVsyncId + int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID; + InputWindowCommands mInputWindowCommands; int mStatus = NO_ERROR; - layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle); - layer_state_t* getLayerState(const sp<SurfaceControl>& sc) { - return getLayerState(sc->getHandle()); - } + layer_state_t* getLayerState(const sp<SurfaceControl>& sc); DisplayState& getDisplayState(const sp<IBinder>& token); void cacheBuffers(); void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc); public: - Transaction() = default; + Transaction(); virtual ~Transaction() = default; Transaction(Transaction const& other); @@ -418,7 +427,7 @@ public: // If the relative is removed, the Surface will have no layer and be // invisible, until the next time set(Relative)Layer is called. Transaction& setRelativeLayer(const sp<SurfaceControl>& sc, - const sp<IBinder>& relativeTo, int32_t z); + const sp<SurfaceControl>& relativeTo, int32_t z); Transaction& setFlags(const sp<SurfaceControl>& sc, uint32_t flags, uint32_t mask); Transaction& setTransparentRegionHint(const sp<SurfaceControl>& sc, @@ -431,6 +440,8 @@ public: Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc, int backgroundBlurRadius); + Transaction& setBlurRegions(const sp<SurfaceControl>& sc, + const std::vector<BlurRegion>& regions); Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p); // Defers applying any changes made in this transaction until the Layer @@ -438,22 +449,16 @@ public: // by handle is removed, then we will apply this transaction regardless of // what frame number has been reached. Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, - const sp<IBinder>& handle, uint64_t frameNumber); - // A variant of deferTransactionUntil_legacy which identifies the Layer we wait for by - // Surface instead of Handle. Useful for clients which may not have the - // SurfaceControl for some of their Surfaces. Otherwise behaves identically. - Transaction& deferTransactionUntil_legacy(const sp<SurfaceControl>& sc, - const sp<Surface>& barrierSurface, + const sp<SurfaceControl>& barrierSurfaceControl, uint64_t frameNumber); // Reparents all children of this layer to the new parent handle. Transaction& reparentChildren(const sp<SurfaceControl>& sc, - const sp<IBinder>& newParentHandle); + const sp<SurfaceControl>& newParent); /// Reparents the current layer to the new parent handle. The new parent must not be null. // This can be used instead of reparentChildren if the caller wants to // only re-parent a specific child. - Transaction& reparent(const sp<SurfaceControl>& sc, - const sp<IBinder>& newParentHandle); + Transaction& reparent(const sp<SurfaceControl>& sc, const sp<SurfaceControl>& newParent); Transaction& setColor(const sp<SurfaceControl>& sc, const half3& color); @@ -487,6 +492,8 @@ public: // ONLY FOR BLAST ADAPTER Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc); + // Set the framenumber generated by the graphics producer to mimic BufferQueue behaviour. + Transaction& setFrameNumber(const sp<SurfaceControl>& sc, uint64_t frameNumber); // Detaches all child surfaces (and their children recursively) // from their SurfaceControl. @@ -499,14 +506,12 @@ public: // Sometimes the WindowManager needs to extend their lifetime slightly // in order to perform an exit animation or prevent flicker. Transaction& detachChildren(const sp<SurfaceControl>& sc); - // Set an override scaling mode as documented in <system/window.h> - // the override scaling mode will take precedence over any client - // specified scaling mode. -1 will clear the override scaling mode. - Transaction& setOverrideScalingMode(const sp<SurfaceControl>& sc, - int32_t overrideScalingMode); #ifndef NO_INPUT Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info); + Transaction& setFocusedWindow(const sp<IBinder>& token, const sp<IBinder>& focusedToken, + nsecs_t timestampNanos, int32_t displayId); + Transaction& setFocusedWindow(const FocusRequest& request); Transaction& syncInputWindows(); #endif @@ -529,6 +534,13 @@ public: // a buffer of a different size. Transaction& setFixedTransformHint(const sp<SurfaceControl>& sc, int32_t transformHint); + // Sets the frame timeline vsync id received from choreographer that corresponds + // to the transaction. + Transaction& setFrameTimelineVsync(int64_t frameTimelineVsyncId); + // Variant that only applies to a specific SurfaceControl. + Transaction& setFrameTimelineVsync(const sp<SurfaceControl>& sc, + int64_t frameTimelineVsyncId); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -592,28 +604,12 @@ private: class ScreenshotClient { public: - // if cropping isn't required, callers may pass in a default Rect, e.g.: - // capture(display, producer, Rect(), reqWidth, ...); - static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, bool captureSecureLayers, - sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers); - static status_t capture(const sp<IBinder>& display, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, sp<GraphicBuffer>* outBuffer); - static status_t capture(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer); - static status_t captureLayers(const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - float frameScale, sp<GraphicBuffer>* outBuffer); - static status_t captureChildLayers( - const sp<IBinder>& layerHandle, ui::Dataspace reqDataSpace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& - excludeHandles, - float frameScale, sp<GraphicBuffer>* outBuffer); + static status_t captureDisplay(const DisplayCaptureArgs& captureArgs, + const sp<IScreenCaptureListener>& captureListener); + static status_t captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener); + static status_t captureLayers(const LayerCaptureArgs& captureArgs, + const sp<IScreenCaptureListener>& captureListener); }; // --------------------------------------------------------------------------- diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index ac2bbccfd2..35bdfc155d 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -20,7 +20,6 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/threads.h> @@ -44,8 +43,12 @@ class SurfaceComposerClient; class SurfaceControl : public RefBase { public: - static sp<SurfaceControl> readFromParcel(const Parcel* parcel); - void writeToParcel(Parcel* parcel); + static status_t readFromParcel(const Parcel& parcel, sp<SurfaceControl>* outSurfaceControl); + status_t writeToParcel(Parcel& parcel); + + static status_t readNullableFromParcel(const Parcel& parcel, + sp<SurfaceControl>* outSurfaceControl); + static status_t writeNullableToParcel(Parcel& parcel, const sp<SurfaceControl>& surfaceControl); static bool isValid(const sp<SurfaceControl>& surface) { return (surface != nullptr) && surface->isValid(); @@ -70,6 +73,7 @@ public: sp<Surface> getSurface() const; sp<Surface> createSurface() const; sp<IBinder> getHandle() const; + int32_t getLayerId() const; sp<IGraphicBufferProducer> getIGraphicBufferProducer() const; @@ -85,7 +89,8 @@ public: explicit SurfaceControl(const sp<SurfaceControl>& other); SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, - const sp<IGraphicBufferProducer>& gbp, uint32_t transformHint = 0); + const sp<IGraphicBufferProducer>& gbp, int32_t layerId, + uint32_t transformHint = 0); private: // can't be copied @@ -105,6 +110,7 @@ private: sp<IGraphicBufferProducer> mGraphicBufferProducer; mutable Mutex mLock; mutable sp<Surface> mSurfaceData; + int32_t mLayerId; uint32_t mTransformHint; }; diff --git a/include/input/ISetInputWindowsListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h index 15d31b25c1..2857996c23 100644 --- a/include/input/ISetInputWindowsListener.h +++ b/libs/gui/include/gui/SyncScreenCaptureListener.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,25 +16,25 @@ #pragma once -#include <binder/IInterface.h> -#include <binder/Parcel.h> +#include <gui/SurfaceComposerClient.h> +#include <future> namespace android { -class ISetInputWindowsListener : public IInterface { +class SyncScreenCaptureListener : public BnScreenCaptureListener { public: - DECLARE_META_INTERFACE(SetInputWindowsListener) - virtual void onSetInputWindowsFinished() = 0; -}; + status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override { + resultsPromise.set_value(captureResults); + return NO_ERROR; + } -class BnSetInputWindowsListener: public BnInterface<ISetInputWindowsListener> { -public: - enum SetInputWindowsTag : uint32_t { - ON_SET_INPUT_WINDOWS_FINISHED - }; + ScreenCaptureResults waitForResults() { + std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future(); + return resultsFuture.get(); + } - virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0) override; +private: + std::promise<ScreenCaptureResults> resultsPromise; }; -}; // namespace android +} // namespace android
\ No newline at end of file diff --git a/libs/gui/include/gui/TransactionTracing.h b/libs/gui/include/gui/TransactionTracing.h new file mode 100644 index 0000000000..9efba47a18 --- /dev/null +++ b/libs/gui/include/gui/TransactionTracing.h @@ -0,0 +1,41 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/gui/BnTransactionTraceListener.h> +#include <utils/Mutex.h> + +namespace android { + +class TransactionTraceListener : public gui::BnTransactionTraceListener { + static std::mutex sMutex; + static sp<TransactionTraceListener> sInstance; + + TransactionTraceListener(); + +public: + static sp<TransactionTraceListener> getInstance(); + + binder::Status onToggled(bool enabled) override; + + bool isTracingEnabled(); + +private: + bool mTracingEnabled = false; +}; + +} // namespace android diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h index cc64fd45dd..f7dcbc698d 100644 --- a/libs/gui/include/gui/view/Surface.h +++ b/libs/gui/include/gui/view/Surface.h @@ -21,6 +21,7 @@ #include <utils/StrongPointer.h> #include <utils/String16.h> +#include <binder/IBinder.h> #include <binder/Parcelable.h> namespace android { @@ -43,6 +44,7 @@ class Surface : public Parcelable { String16 name; sp<IGraphicBufferProducer> graphicBufferProducer; + sp<IBinder> surfaceControlHandle; virtual status_t writeToParcel(Parcel* parcel) const override; virtual status_t readFromParcel(const Parcel* parcel) override; diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index a6bcd107af..53c13c8859 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -14,7 +14,7 @@ cc_test { srcs: [ "BLASTBufferQueue_test.cpp", - "BufferItemConsumer_test.cpp", + "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", @@ -58,6 +58,30 @@ cc_test { header_libs: ["libsurfaceflinger_headers"], } +// Build the tests that need to run with both 32bit and 64bit. +cc_test { + name: "libgui_multilib_test", + test_suites: ["device-tests"], + + clang: true, + cflags: [ + "-Wall", + "-Werror", + ], + + srcs: [ + "DisplayEventStructLayout_test.cpp", + ], + + shared_libs: [ + "libgui", + ], + + compile_multilib: "both", + + header_libs: ["libsurfaceflinger_headers"], +} + // Build a separate binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) // This test has a main method, and requires a separate binary to be built. // To add move tests like this, just add additional cc_test statements, diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index da5bbdd2e6..9299721be3 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -25,6 +25,7 @@ #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> #include <gui/SurfaceComposerClient.h> +#include <gui/SyncScreenCaptureListener.h> #include <private/gui/ComposerService.h> #include <ui/DisplayConfig.h> #include <ui/GraphicBuffer.h> @@ -43,7 +44,7 @@ using android::hardware::graphics::common::V1_2::BufferUsage; class BLASTBufferQueueHelper { public: BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) { - mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height); + mBlastBufferQueueAdapter = new BLASTBufferQueue("TestBLASTBufferQueue", sc, width, height); } void update(const sp<SurfaceControl>& sc, int width, int height) { @@ -120,6 +121,8 @@ protected: .show(mSurfaceControl) .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB) .apply(); + + mCaptureArgs.displayToken = mDisplayToken; } void setUpProducer(BLASTBufferQueueHelper adapter, sp<IGraphicBufferProducer>& producer) { @@ -165,14 +168,15 @@ protected: void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0, bool outsideRegion = false) { + sp<GraphicBuffer>& captureBuf = mCaptureResults.buffer; const auto epsilon = 3; - const auto width = mScreenCaptureBuf->getWidth(); - const auto height = mScreenCaptureBuf->getHeight(); - const auto stride = mScreenCaptureBuf->getStride(); + const auto width = captureBuf->getWidth(); + const auto height = captureBuf->getHeight(); + const auto stride = captureBuf->getStride(); uint32_t* bufData; - mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN), - reinterpret_cast<void**>(&bufData)); + captureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN), + reinterpret_cast<void**>(&bufData)); for (uint32_t row = 0; row < height; row++) { for (uint32_t col = 0; col < width; col++) { @@ -196,20 +200,36 @@ protected: } } } - mScreenCaptureBuf->unlock(); + captureBuf->unlock(); ASSERT_EQ(false, ::testing::Test::HasFailure()); } + static status_t captureDisplay(DisplayCaptureArgs& captureArgs, + ScreenCaptureResults& captureResults) { + const auto sf = ComposerService::getComposerService(); + SurfaceComposerClient::Transaction().apply(true); + + const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + status_t status = sf->captureDisplay(captureArgs, captureListener); + if (status != NO_ERROR) { + return status; + } + captureResults = captureListener->waitForResults(); + return captureResults.result; + } + sp<SurfaceComposerClient> mClient; sp<ISurfaceComposer> mComposer; sp<IBinder> mDisplayToken; sp<SurfaceControl> mSurfaceControl; - sp<GraphicBuffer> mScreenCaptureBuf; uint32_t mDisplayWidth; uint32_t mDisplayHeight; + + DisplayCaptureArgs mCaptureArgs; + ScreenCaptureResults mCaptureResults; }; TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { @@ -301,12 +321,7 @@ TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) { adapter.waitForCallbacks(); // capture screen and verify that it is red - bool capturedSecureLayers; - ASSERT_EQ(NO_ERROR, - mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), - mDisplayWidth, mDisplayHeight, - /*useIdentityTransform*/ false)); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); ASSERT_NO_FATAL_FAILURE( checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } @@ -383,12 +398,8 @@ TEST_F(BLASTBufferQueueTest, SetCrop_Item) { adapter.waitForCallbacks(); // capture screen and verify that it is red - bool capturedSecureLayers; - ASSERT_EQ(NO_ERROR, - mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), - mDisplayWidth, mDisplayHeight, - /*useIdentityTransform*/ false)); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); } @@ -444,12 +455,8 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) { adapter.waitForCallbacks(); // capture screen and verify that it is red - bool capturedSecureLayers; - ASSERT_EQ(NO_ERROR, - mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), - mDisplayWidth, mDisplayHeight, - /*useIdentityTransform*/ false)); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( checkScreenCapture(r, g, b, {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength})); @@ -483,18 +490,14 @@ public: IGraphicBufferProducer::QueueBufferOutput qbOutput; IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN, Rect(bufWidth, bufHeight), - NATIVE_WINDOW_SCALING_MODE_FREEZE, tr, - Fence::NO_FENCE); + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, + tr, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); adapter.waitForCallbacks(); - bool capturedSecureLayers; - ASSERT_EQ(NO_ERROR, - mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, - Rect(), mDisplayWidth, mDisplayHeight, - /*useIdentityTransform*/ false)); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + switch (tr) { case ui::Transform::ROT_0: ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0, diff --git a/libs/gui/tests/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp new file mode 100644 index 0000000000..72109109c4 --- /dev/null +++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gui/DisplayEventReceiver.h> + +namespace android::test { + +#define CHECK_OFFSET(type, member, expected_offset) \ + static_assert((offsetof(type, member) == (expected_offset)), "") + +TEST(DisplayEventStructLayoutTest, TestEventAlignment) { + CHECK_OFFSET(DisplayEventReceiver::Event, vsync, 24); + CHECK_OFFSET(DisplayEventReceiver::Event, hotplug, 24); + CHECK_OFFSET(DisplayEventReceiver::Event, config, 24); + + CHECK_OFFSET(DisplayEventReceiver::Event::Header, type, 0); + CHECK_OFFSET(DisplayEventReceiver::Event::Header, displayId, 8); + CHECK_OFFSET(DisplayEventReceiver::Event::Header, timestamp, 16); + + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, count, 0); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, expectedVSyncTimestamp, 8); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, deadlineTimestamp, 16); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncId, 24); + + CHECK_OFFSET(DisplayEventReceiver::Event::Hotplug, connected, 0); + + CHECK_OFFSET(DisplayEventReceiver::Event::Config, configId, 0); + CHECK_OFFSET(DisplayEventReceiver::Event::Config, vsyncPeriod, 8); +} + +} // namespace android::test diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index b1d3ecbf36..483f171564 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -36,18 +36,18 @@ #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <input/InputWindow.h> -#include <input/IInputFlinger.h> -#include <input/InputTransport.h> +#include <android/os/IInputFlinger.h> #include <input/Input.h> +#include <input/InputTransport.h> +#include <input/InputWindow.h> #include <ui/DisplayConfig.h> #include <ui/Rect.h> #include <ui/Region.h> +using android::os::IInputFlinger; -namespace android { -namespace test { +namespace android::test { using Transaction = SurfaceComposerClient::Transaction; @@ -62,16 +62,16 @@ sp<IInputFlinger> getInputFlinger() { // We use the top 10 layers as a way to haphazardly place ourselves above anything else. static const int LAYER_BASE = INT32_MAX - 10; +static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 5s; class InputSurface { public: InputSurface(const sp<SurfaceControl> &sc, int width, int height) { mSurfaceControl = sc; - InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); - mInputFlinger = getInputFlinger(); - mInputFlinger->registerInputChannel(mServerChannel); + mClientChannel = std::make_shared<InputChannel>(); + mInputFlinger->createInputChannel("testchannels", mClientChannel.get()); populateInputInfo(width, height); @@ -153,9 +153,7 @@ public: EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS); } - ~InputSurface() { - mInputFlinger->unregisterInputChannel(mServerChannel); - } + ~InputSurface() { mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken()); } void doTransaction(std::function<void(SurfaceComposerClient::Transaction&, const sp<SurfaceControl>&)> transactionBody) { @@ -175,6 +173,13 @@ public: t.apply(true); } + void requestFocus() { + SurfaceComposerClient::Transaction t; + t.setFocusedWindow(mInputInfo.token, nullptr, systemTime(SYSTEM_TIME_MONOTONIC), + 0 /* displayId */); + t.apply(true); + } + private: void waitForEventAvailable() { struct pollfd fd; @@ -185,14 +190,13 @@ private: } void populateInputInfo(int width, int height) { - mInputInfo.token = mServerChannel->getConnectionToken(); + mInputInfo.token = mClientChannel->getConnectionToken(); mInputInfo.name = "Test info"; - mInputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; - mInputInfo.layoutParamsType = InputWindowInfo::TYPE_BASE_APPLICATION; - mInputInfo.dispatchingTimeout = seconds_to_nanoseconds(5); + mInputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL; + mInputInfo.type = InputWindowInfo::Type::BASE_APPLICATION; + mInputInfo.dispatchingTimeout = 5s; mInputInfo.globalScaleFactor = 1.0; - mInputInfo.canReceiveKeys = true; - mInputInfo.hasFocus = true; + mInputInfo.focusable = true; mInputInfo.hasWallpaper = false; mInputInfo.paused = false; @@ -201,19 +205,19 @@ private: // TODO: Fill in from SF? mInputInfo.ownerPid = 11111; mInputInfo.ownerUid = 11111; - mInputInfo.inputFeatures = 0; mInputInfo.displayId = 0; InputApplicationInfo aInfo; aInfo.token = new BBinder(); aInfo.name = "Test app info"; - aInfo.dispatchingTimeout = seconds_to_nanoseconds(5); + aInfo.dispatchingTimeoutMillis = + std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count(); mInputInfo.applicationInfo = aInfo; } public: sp<SurfaceControl> mSurfaceControl; - sp<InputChannel> mServerChannel, mClientChannel; + std::shared_ptr<InputChannel> mClientChannel; sp<IInputFlinger> mInputFlinger; InputWindowInfo mInputInfo; @@ -280,7 +284,6 @@ void injectTap(int x, int y) { TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); - surface->assertFocusChange(true); injectTap(101, 101); @@ -296,12 +299,9 @@ TEST_F(InputSurfacesTest, can_receive_input) { TEST_F(InputSurfacesTest, input_respects_positioning) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); - surface->assertFocusChange(true); std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); surface2->showAt(200, 200); - surface->assertFocusChange(false); - surface2->assertFocusChange(true); injectTap(201, 201); surface2->expectTap(1, 1); @@ -328,16 +328,11 @@ TEST_F(InputSurfacesTest, input_respects_layering) { std::unique_ptr<InputSurface> surface2 = makeSurface(100, 100); surface->showAt(10, 10); - surface->assertFocusChange(true); surface2->showAt(10, 10); - surface->assertFocusChange(false); - surface2->assertFocusChange(true); surface->doTransaction([](auto &t, auto &sc) { t.setLayer(sc, LAYER_BASE + 1); }); - surface2->assertFocusChange(false); - surface->assertFocusChange(true); injectTap(11, 11); surface->expectTap(1, 1); @@ -345,8 +340,6 @@ TEST_F(InputSurfacesTest, input_respects_layering) { surface2->doTransaction([](auto &t, auto &sc) { t.setLayer(sc, LAYER_BASE + 1); }); - surface2->assertFocusChange(true); - surface->assertFocusChange(false); injectTap(11, 11); surface2->expectTap(1, 1); @@ -354,8 +347,6 @@ TEST_F(InputSurfacesTest, input_respects_layering) { surface2->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); - surface2->assertFocusChange(false); - surface->assertFocusChange(true); injectTap(11, 11); surface->expectTap(1, 1); @@ -368,12 +359,9 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - bgSurface->assertFocusChange(true); fgSurface->mInputInfo.surfaceInset = 5; fgSurface->showAt(100, 100); - fgSurface->assertFocusChange(true); - bgSurface->assertFocusChange(false); injectTap(106, 106); fgSurface->expectTap(1, 1); @@ -387,16 +375,13 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100); parentSurface->showAt(100, 100); - parentSurface->assertFocusChange(true); childSurface->mInputInfo.surfaceInset = 10; childSurface->showAt(100, 100); - childSurface->assertFocusChange(true); - parentSurface->assertFocusChange(false); childSurface->doTransaction([&](auto &t, auto &sc) { t.setPosition(sc, -5, -5); - t.reparent(sc, parentSurface->mSurfaceControl->getHandle()); + t.reparent(sc, parentSurface->mSurfaceControl); }); injectTap(106, 106); @@ -411,12 +396,9 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - bgSurface->assertFocusChange(true); fgSurface->mInputInfo.surfaceInset = 5; fgSurface->showAt(100, 100); - bgSurface->assertFocusChange(false); - fgSurface->assertFocusChange(true); fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); }); @@ -433,7 +415,6 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { // In case we pass the very big inset without any checking. fgSurface->mInputInfo.surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); - fgSurface->assertFocusChange(true); fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -450,7 +431,6 @@ TEST_F(InputSurfacesTest, input_ignores_transparent_region) { t.setTransparentRegionHint(sc, transparentRegion); }); surface->showAt(100, 100); - surface->assertFocusChange(true); injectTap(101, 101); surface->expectTap(1, 1); } @@ -465,10 +445,7 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) { InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); bgSurface->showAt(10, 10); - bgSurface->assertFocusChange(true); bufferSurface->showAt(10, 10); - bgSurface->assertFocusChange(false); - bufferSurface->assertFocusChange(true); injectTap(11, 11); bufferSurface->expectTap(1, 1); @@ -485,10 +462,7 @@ TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) { postBuffer(bufferSurface->mSurfaceControl); bgSurface->showAt(10, 10); - bgSurface->assertFocusChange(true); bufferSurface->showAt(10, 10); - bufferSurface->assertFocusChange(true); - bgSurface->assertFocusChange(false); injectTap(11, 11); bufferSurface->expectTap(1, 1); @@ -504,10 +478,7 @@ TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(10, 10); - bgSurface->assertFocusChange(true); fgSurface->showAt(10, 10); - bgSurface->assertFocusChange(false); - fgSurface->assertFocusChange(true); injectTap(11, 11); fgSurface->expectTap(1, 1); @@ -524,17 +495,12 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); bgSurface->showAt(10, 10); - bgSurface->assertFocusChange(true); containerSurface->showAt(10, 10); - bgSurface->assertFocusChange(false); - containerSurface->assertFocusChange(true); injectTap(11, 11); containerSurface->expectTap(1, 1); containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); }); - containerSurface->assertFocusChange(false); - bgSurface->assertFocusChange(true); injectTap(11, 11); bgSurface->expectTap(1, 1); @@ -543,7 +509,6 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { TEST_F(InputSurfacesTest, input_respects_outscreen) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(-1, -1); - surface->assertFocusChange(true); injectTap(0, 0); surface->expectTap(1, 1); @@ -555,11 +520,86 @@ TEST_F(InputSurfacesTest, input_ignores_cursor_layer) { InputSurface::makeCursorInputSurface(mComposerClient, 10, 10); surface->showAt(10, 10); - surface->assertFocusChange(true); cursorSurface->showAt(10, 10); injectTap(11, 11); surface->expectTap(1, 1); } + +TEST_F(InputSurfacesTest, can_be_focused) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(100, 100); + surface->requestFocus(); + + surface->assertFocusChange(true); +} + +TEST_F(InputSurfacesTest, rotate_surface) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(10, 10); + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees + }); + injectTap(8, 11); + surface->expectTap(1, 2); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees + }); + injectTap(9, 8); + surface->expectTap(1, 2); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees + }); + injectTap(12, 9); + surface->expectTap(1, 2); +} + +TEST_F(InputSurfacesTest, rotate_surface_with_scale) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->showAt(10, 10); + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees + }); + injectTap(2, 12); + surface->expectTap(1, 2); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees + }); + injectTap(8, 2); + surface->expectTap(1, 2); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees + }); + injectTap(18, 8); + surface->expectTap(1, 2); } + +TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) { + std::unique_ptr<InputSurface> surface = makeSurface(100, 100); + surface->mInputInfo.surfaceInset = 5; + surface->showAt(100, 100); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees + }); + injectTap(40, 120); + surface->expectTap(5, 10); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees + }); + injectTap(80, 40); + surface->expectTap(5, 10); + + surface->doTransaction([](auto &t, auto &sc) { + t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees + }); + injectTap(160, 80); + surface->expectTap(5, 10); } + +} // namespace android::test diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 592913c46b..0cd3962aa1 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -28,6 +28,7 @@ #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#include <gui/SyncScreenCaptureListener.h> #include <inttypes.h> #include <private/gui/ComposerService.h> #include <ui/BufferQueueDefs.h> @@ -197,6 +198,20 @@ protected: ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); } + static status_t captureDisplay(DisplayCaptureArgs& captureArgs, + ScreenCaptureResults& captureResults) { + const auto sf = ComposerService::getComposerService(); + SurfaceComposerClient::Transaction().apply(true); + + const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + status_t status = sf->captureDisplay(captureArgs, captureListener); + if (status != NO_ERROR) { + return status; + } + captureResults = captureListener->waitForResults(); + return captureResults.result; + } + sp<Surface> mSurface; sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mSurfaceControl; @@ -244,11 +259,13 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) { const sp<IBinder> display = sf->getInternalDisplayToken(); ASSERT_FALSE(display == nullptr); - sp<GraphicBuffer> outBuffer; - bool ignored; - ASSERT_EQ(NO_ERROR, - sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); + DisplayCaptureArgs captureArgs; + captureArgs.displayToken = display; + captureArgs.width = 64; + captureArgs.height = 64; + + ScreenCaptureResults captureResults; + ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults)); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); @@ -278,9 +295,7 @@ TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersDontSucceed) { &buf)); ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1)); } - ASSERT_EQ(NO_ERROR, - sf->captureScreen(display, &outBuffer, ignored, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, Rect(), 64, 64, false)); + ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults)); } TEST_F(SurfaceTest, ConcreteTypeIsSurface) { @@ -662,8 +677,7 @@ public: NewFrameEventsEntry mNewFrameEntryOverride = { 0, 0, 0, nullptr }; }; - -class FakeSurfaceComposer : public ISurfaceComposer{ +class FakeSurfaceComposer : public ISurfaceComposer { public: ~FakeSurfaceComposer() override {} @@ -681,13 +695,17 @@ public: void destroyDisplay(const sp<IBinder>& /*display */) override {} std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; } sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; } - void setTransactionState(const Vector<ComposerState>& /*state*/, - const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, - const sp<IBinder>& /*applyToken*/, - const InputWindowCommands& /*inputWindowCommands*/, - int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/, - bool /*hasListenerCallbacks*/, - const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override { + status_t setTransactionState(int64_t /*frameTimelineVsyncId*/, + const Vector<ComposerState>& /*state*/, + const Vector<DisplayState>& /*displays*/, uint32_t /*flags*/, + const sp<IBinder>& /*applyToken*/, + const InputWindowCommands& /*inputWindowCommands*/, + int64_t /*desiredPresentTime*/, + const client_cache_t& /*cachedBuffer*/, + bool /*hasListenerCallbacks*/, + const std::vector<ListenerCallbacks>& /*listenerCallbacks*/, + uint64_t /*transactionId*/) override { + return NO_ERROR; } void bootFinished() override {} @@ -742,12 +760,8 @@ public: } status_t setActiveColorMode(const sp<IBinder>& /*display*/, ColorMode /*colorMode*/) override { return NO_ERROR; } - status_t captureScreen(const sp<IBinder>& /*display*/, sp<GraphicBuffer>* /*outBuffer*/, - bool& /*outCapturedSecureLayers*/, ui::Dataspace /*reqDataspace*/, - ui::PixelFormat /*reqPixelFormat*/, const Rect& /*sourceCrop*/, - uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, - bool /*useIdentityTransform*/, ui::Rotation, - bool /*captureSecureLayers*/) override { + status_t captureDisplay(const DisplayCaptureArgs& /* captureArgs */, + const sp<IScreenCaptureListener>& /* captureListener */) override { return NO_ERROR; } status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/, @@ -760,17 +774,13 @@ public: return NO_ERROR; } void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {} - status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/, - sp<GraphicBuffer>* /*outBuffer*/) override { + status_t captureDisplay(uint64_t /*displayOrLayerStack*/, + const sp<IScreenCaptureListener>& /* captureListener */) override { return NO_ERROR; } virtual status_t captureLayers( - const sp<IBinder>& /*parentHandle*/, sp<GraphicBuffer>* /*outBuffer*/, - ui::Dataspace /*reqDataspace*/, ui::PixelFormat /*reqPixelFormat*/, - const Rect& /*sourceCrop*/, - const std::unordered_set<sp<IBinder>, - ISurfaceComposer::SpHash<IBinder>>& /*excludeHandles*/, - float /*frameScale*/, bool /*childrenOnly*/) override { + const LayerCaptureArgs& /* captureArgs */, + const sp<IScreenCaptureListener>& /* captureListener */) override { return NO_ERROR; } status_t clearAnimationFrameStats() override { return NO_ERROR; } @@ -834,7 +844,7 @@ public: return NO_ERROR; } status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/, - int32_t /*defaultConfig*/, + int32_t /*defaultConfig*/, bool /*allowGroupSwitching*/, float /*primaryRefreshRateMin*/, float /*primaryRefreshRateMax*/, float /*appRequestRefreshRateMin*/, @@ -843,13 +853,14 @@ public: } status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/, int32_t* /*outDefaultConfig*/, + bool* /*outAllowGroupSwitching*/, float* /*outPrimaryRefreshRateMin*/, float* /*outPrimaryRefreshRateMax*/, float* /*outAppRequestRefreshRateMin*/, float* /*outAppRequestRefreshRateMax*/) override { return NO_ERROR; }; - status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; } + status_t notifyPowerBoost(int32_t /*boostId*/) override { return NO_ERROR; } status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/, float /*lightPosY*/, float /*lightPosZ*/, @@ -862,7 +873,19 @@ public: return NO_ERROR; } - status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) { return NO_ERROR; } + status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override { + return NO_ERROR; + } + + status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& /*surface*/, + int64_t /*frameTimelineVsyncId*/) override { + return NO_ERROR; + } + + status_t addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& /*listener*/) override { + return NO_ERROR; + } protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp index d98ffc6618..1bfe4624f1 100644 --- a/libs/gui/view/Surface.cpp +++ b/libs/gui/view/Surface.cpp @@ -45,7 +45,9 @@ status_t Surface::writeToParcel(Parcel* parcel, bool nameAlreadyWritten) const { if (res != OK) return res; } - return IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel); + res = IGraphicBufferProducer::exportToParcel(graphicBufferProducer, parcel); + if (res != OK) return res; + return parcel->writeStrongBinder(surfaceControlHandle); } status_t Surface::readFromParcel(const Parcel* parcel) { @@ -68,6 +70,7 @@ status_t Surface::readFromParcel(const Parcel* parcel, bool nameAlreadyRead) { } graphicBufferProducer = IGraphicBufferProducer::createFromParcel(parcel); + surfaceControlHandle = parcel->readStrongBinder(); return OK; } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 6a5d434515..fce3000a0f 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -14,6 +14,17 @@ // libinput is partially built for the host (used by build time keymap validation tool) +filegroup { + name: "inputconstants_aidl", + srcs: [ + "android/os/BlockUntrustedTouchesMode.aidl", + "android/os/IInputConstants.aidl", + "android/os/InputEventInjectionResult.aidl", + "android/os/InputEventInjectionSync.aidl", + "android/os/TouchOcclusionMode.aidl", + ], +} + cc_library { name: "libinput", host_supported: true, @@ -25,11 +36,15 @@ cc_library { srcs: [ "Input.cpp", "InputDevice.cpp", + "InputEventLabels.cpp", "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", + "LatencyStatistics.cpp", "PropertyMap.cpp", "TouchVideoFrame.cpp", + "VelocityControl.cpp", + "VelocityTracker.cpp", "VirtualKeyMap.cpp", ], @@ -44,19 +59,32 @@ cc_library { "libcutils", ], + static_libs: [ + "libui-types", + ], + + export_static_lib_headers: [ + "libui-types", + ], + target: { android: { srcs: [ - "IInputFlinger.cpp", - "InputApplication.cpp", "InputTransport.cpp", "InputWindow.cpp", - "ISetInputWindowsListener.cpp", - "LatencyStatistics.cpp", - "VelocityControl.cpp", - "VelocityTracker.cpp", + "android/FocusRequest.aidl", + "android/InputApplicationInfo.aidl", + "android/os/IInputConstants.aidl", + "android/os/InputEventInjectionResult.aidl", + "android/os/InputEventInjectionSync.aidl", + "android/os/TouchOcclusionMode.aidl", + "android/os/BlockUntrustedTouchesMode.aidl", + "android/os/IInputFlinger.aidl", + "android/os/ISetInputWindowsListener.aidl", ], + export_shared_lib_headers: ["libbinder"], + shared_libs: [ "libutils", "libbinder", @@ -71,7 +99,33 @@ cc_library { shared: { enabled: false, }, + include_dirs: [ + "frameworks/native/libs/arect/include", + ], }, + linux_glibc: { + srcs: [ + "InputTransport.cpp", + "InputWindow.cpp", + "android/FocusRequest.aidl", + "android/InputApplicationInfo.aidl", + "android/os/IInputConstants.aidl", + "android/os/IInputFlinger.aidl", + "android/os/ISetInputWindowsListener.aidl", + "android/os/TouchOcclusionMode.aidl", + ], + static_libs: [ + "libhostgraphics", + ], + shared_libs: [ + "libbinder", + ], + }, + }, + + aidl: { + local_include_dirs: ["."], + export_aidl_headers: true, }, } diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp deleted file mode 100644 index 8ec51653a8..0000000000 --- a/libs/input/IInputFlinger.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> -#include <binder/IPCThreadState.h> -#include <binder/IServiceManager.h> - -#include <input/IInputFlinger.h> - -namespace android { - -class BpInputFlinger : public BpInterface<IInputFlinger> { -public: - explicit BpInputFlinger(const sp<IBinder>& impl) : - BpInterface<IInputFlinger>(impl) { } - - virtual void setInputWindows(const std::vector<InputWindowInfo>& inputInfo, - const sp<ISetInputWindowsListener>& setInputWindowsListener) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - - data.writeUint32(static_cast<uint32_t>(inputInfo.size())); - for (const auto& info : inputInfo) { - info.write(data); - } - data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener)); - - remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply, - IBinder::FLAG_ONEWAY); - } - - virtual void registerInputChannel(const sp<InputChannel>& channel) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - channel->write(data); - remote()->transact(BnInputFlinger::REGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); - } - - virtual void unregisterInputChannel(const sp<InputChannel>& channel) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - channel->write(data); - remote()->transact(BnInputFlinger::UNREGISTER_INPUT_CHANNEL_TRANSACTION, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger"); - -status_t BnInputFlinger::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - switch(code) { - case SET_INPUT_WINDOWS_TRANSACTION: { - CHECK_INTERFACE(IInputFlinger, data, reply); - size_t count = data.readUint32(); - if (count > data.dataSize()) { - return BAD_VALUE; - } - std::vector<InputWindowInfo> handles; - for (size_t i = 0; i < count; i++) { - handles.push_back(InputWindowInfo::read(data)); - } - const sp<ISetInputWindowsListener> setInputWindowsListener = - ISetInputWindowsListener::asInterface(data.readStrongBinder()); - setInputWindows(handles, setInputWindowsListener); - break; - } - case REGISTER_INPUT_CHANNEL_TRANSACTION: { - CHECK_INTERFACE(IInputFlinger, data, reply); - sp<InputChannel> channel = InputChannel::read(data); - registerInputChannel(channel); - break; - } - case UNREGISTER_INPUT_CHANNEL_TRANSACTION: { - CHECK_INTERFACE(IInputFlinger, data, reply); - sp<InputChannel> channel = InputChannel::read(data); - unregisterInputChannel(channel); - break; - } - default: - return BBinder::onTransact(code, data, reply, flags); - } - return NO_ERROR; -} - -}; diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp deleted file mode 100644 index a0330da89e..0000000000 --- a/libs/input/ISetInputWindowsListener.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <input/ISetInputWindowsListener.h> - -namespace android { - -class BpSetInputWindowsListener : public BpInterface<ISetInputWindowsListener> { -public: - explicit BpSetInputWindowsListener(const sp<IBinder>& impl) - : BpInterface<ISetInputWindowsListener>(impl) { - } - - virtual ~BpSetInputWindowsListener() = default; - - virtual void onSetInputWindowsFinished() { - Parcel data, reply; - data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor()); - remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply, - IBinder::FLAG_ONEWAY); - } -}; - -IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener"); - -status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - switch(code) { - case ON_SET_INPUT_WINDOWS_FINISHED: { - CHECK_INTERFACE(ISetInputWindowsListener, data, reply); - onSetInputWindowsFinished(); - return NO_ERROR; - } - default: { - return BBinder::onTransact(code, data, reply, flags); - } - } -} - -} // namespace android diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 31aa685391..0ea3889338 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -17,19 +17,26 @@ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 +#include <attestation/HmacKeyManager.h> #include <cutils/compiler.h> +#include <inttypes.h> #include <limits.h> #include <string.h> +#include <android-base/stringprintf.h> #include <input/Input.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> -#ifdef __ANDROID__ +#ifdef __linux__ #include <binder/Parcel.h> +#endif +#ifdef __ANDROID__ #include <sys/random.h> #endif +using android::base::StringPrintf; + namespace android { const char* motionClassificationToString(MotionClassification classification) { @@ -135,11 +142,11 @@ int32_t InputEvent::nextId() { // --- KeyEvent --- const char* KeyEvent::getLabel(int32_t keyCode) { - return getLabelByKeyCode(keyCode); + return InputEventLookup::getLabelByKeyCode(keyCode); } int32_t KeyEvent::getKeyCodeFromLabel(const char* label) { - return getKeyCodeByLabel(label); + return InputEventLookup::getKeyCodeByLabel(label); } void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, @@ -249,7 +256,7 @@ void PointerCoords::applyOffset(float xOffset, float yOffset) { setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); } -#ifdef __ANDROID__ +#ifdef __linux__ status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); @@ -301,6 +308,11 @@ void PointerCoords::copyFrom(const PointerCoords& other) { } } +void PointerCoords::transform(const ui::Transform& transform) { + vec2 newCoords = transform.transform(getX(), getY()); + setAxisValue(AMOTION_EVENT_AXIS_X, newCoords.x); + setAxisValue(AMOTION_EVENT_AXIS_Y, newCoords.y); +} // --- PointerProperties --- @@ -320,10 +332,10 @@ void PointerProperties::copyFrom(const PointerProperties& other) { void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xScale, - float yScale, float xOffset, float yOffset, float xPrecision, - float yPrecision, float rawXCursorPosition, float rawYCursorPosition, - nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + int32_t buttonState, MotionClassification classification, + const ui::Transform& transform, float xPrecision, float yPrecision, + float rawXCursorPosition, float rawYCursorPosition, nsecs_t downTime, + nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(id, deviceId, source, displayId, hmac); @@ -334,10 +346,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 mMetaState = metaState; mButtonState = buttonState; mClassification = classification; - mXScale = xScale; - mYScale = yScale; - mXOffset = xOffset; - mYOffset = yOffset; + mTransform = transform; mXPrecision = xPrecision; mYPrecision = yPrecision; mRawXCursorPosition = rawXCursorPosition; @@ -360,10 +369,7 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mMetaState = other->mMetaState; mButtonState = other->mButtonState; mClassification = other->mClassification; - mXScale = other->mXScale; - mYScale = other->mYScale; - mXOffset = other->mXOffset; - mYOffset = other->mYOffset; + mTransform = other->mTransform; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; mRawXCursorPosition = other->mRawXCursorPosition; @@ -376,7 +382,7 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mSamplePointerCoords = other->mSamplePointerCoords; } else { mSampleEventTimes.clear(); - mSampleEventTimes.push(other->getEventTime()); + mSampleEventTimes.push_back(other->getEventTime()); mSamplePointerCoords.clear(); size_t pointerCount = other->getPointerCount(); size_t historySize = other->getHistorySize(); @@ -388,23 +394,25 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { - mSampleEventTimes.push(eventTime); + mSampleEventTimes.push_back(eventTime); mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } float MotionEvent::getXCursorPosition() const { - const float rawX = getRawXCursorPosition(); - return rawX * mXScale + mXOffset; + vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); + return vals.x; } float MotionEvent::getYCursorPosition() const { - const float rawY = getRawYCursorPosition(); - return rawY * mYScale + mYOffset; + vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); + return vals.y; } void MotionEvent::setCursorPosition(float x, float y) { - mRawXCursorPosition = (x - mXOffset) / mXScale; - mRawYCursorPosition = (y - mYOffset) / mYScale; + ui::Transform inverse = mTransform.inverse(); + vec2 vals = inverse.transform(x, y); + mRawXCursorPosition = vals.x; + mRawYCursorPosition = vals.y; } const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { @@ -416,14 +424,7 @@ float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { } float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { - float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); - switch (axis) { - case AMOTION_EVENT_AXIS_X: - return value * mXScale + mXOffset; - case AMOTION_EVENT_AXIS_Y: - return value * mYScale + mYOffset; - } - return value; + return getHistoricalAxisValue(axis, pointerIndex, getHistorySize()); } const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( @@ -438,14 +439,23 @@ float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { - float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + if (axis != AMOTION_EVENT_AXIS_X && axis != AMOTION_EVENT_AXIS_Y) { + return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + } + + float rawX = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getX(); + float rawY = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getY(); + vec2 vals = mTransform.transform(rawX, rawY); + switch (axis) { case AMOTION_EVENT_AXIS_X: - return value * mXScale + mXOffset; + return vals.x; case AMOTION_EVENT_AXIS_Y: - return value * mYScale + mYOffset; + return vals.y; } - return value; + + // This should never happen + return 0; } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { @@ -459,23 +469,24 @@ ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { } void MotionEvent::offsetLocation(float xOffset, float yOffset) { - mXOffset += xOffset; - mYOffset += yOffset; + float currXOffset = mTransform.tx(); + float currYOffset = mTransform.ty(); + mTransform.set(currXOffset + xOffset, currYOffset + yOffset); } void MotionEvent::scale(float globalScaleFactor) { - mXOffset *= globalScaleFactor; - mYOffset *= globalScaleFactor; + mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor); mXPrecision *= globalScaleFactor; mYPrecision *= globalScaleFactor; size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { - mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor); + mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor, + globalScaleFactor); } } -static void transformPoint(const float matrix[9], float x, float y, float *outX, float *outY) { +static vec2 transformPoint(const std::array<float, 9>& matrix, float x, float y) { // Apply perspective transform like Skia. float newX = matrix[0] * x + matrix[1] * y + matrix[2]; float newY = matrix[3] * x + matrix[4] * y + matrix[5]; @@ -483,22 +494,25 @@ static void transformPoint(const float matrix[9], float x, float y, float *outX, if (newZ) { newZ = 1.0f / newZ; } - *outX = newX * newZ; - *outY = newY * newZ; + vec2 transformedPoint; + transformedPoint.x = newX * newZ; + transformedPoint.y = newY * newZ; + return transformedPoint; } -static float transformAngle(const float matrix[9], float angleRadians, - float originX, float originY) { +static float transformAngle(const std::array<float, 9>& matrix, float angleRadians, float originX, + float originY) { // Construct and transform a vector oriented at the specified clockwise angle from vertical. // Coordinate system: down is increasing Y, right is increasing X. float x = sinf(angleRadians); float y = -cosf(angleRadians); - transformPoint(matrix, x, y, &x, &y); - x -= originX; - y -= originY; + vec2 transformedPoint = transformPoint(matrix, x, y); + + transformedPoint.x -= originX; + transformedPoint.y -= originY; // Derive the transformed vector's clockwise angle from vertical. - float result = atan2f(x, -y); + float result = atan2f(transformedPoint.x, -transformedPoint.y); if (result < - M_PI_2) { result += M_PI; } else if (result > M_PI_2) { @@ -507,51 +521,51 @@ static float transformAngle(const float matrix[9], float angleRadians, return result; } -void MotionEvent::transform(const float matrix[9]) { - // The tricky part of this implementation is to preserve the value of - // rawX and rawY. So we apply the transformation to the first point - // then derive an appropriate new X/Y offset that will preserve rawX - // and rawY for that point. - float oldXOffset = mXOffset; - float oldYOffset = mYOffset; - float newX, newY; - float scaledRawX = getRawX(0) * mXScale; - float scaledRawY = getRawY(0) * mYScale; - transformPoint(matrix, scaledRawX + oldXOffset, scaledRawY + oldYOffset, &newX, &newY); - mXOffset = newX - scaledRawX; - mYOffset = newY - scaledRawY; +void MotionEvent::transform(const std::array<float, 9>& matrix) { + // We want to preserve the rawX and rawY so we just update the transform + // using the values of the transform passed in + ui::Transform newTransform; + newTransform.set(matrix); + mTransform = newTransform * mTransform; // Determine how the origin is transformed by the matrix so that we // can transform orientation vectors. - float originX, originY; - transformPoint(matrix, 0, 0, &originX, &originY); - - // Apply the transformation to cursor position. - if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { - float x = mRawXCursorPosition * mXScale + oldXOffset; - float y = mRawYCursorPosition * mYScale + oldYOffset; - transformPoint(matrix, x, y, &x, &y); - mRawXCursorPosition = (x - mXOffset) / mXScale; - mRawYCursorPosition = (y - mYOffset) / mYScale; - } + vec2 origin = transformPoint(matrix, 0, 0); // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { PointerCoords& c = mSamplePointerCoords.editItemAt(i); - float x = c.getAxisValue(AMOTION_EVENT_AXIS_X) * mXScale + oldXOffset; - float y = c.getAxisValue(AMOTION_EVENT_AXIS_Y) * mYScale + oldYOffset; - transformPoint(matrix, x, y, &x, &y); - c.setAxisValue(AMOTION_EVENT_AXIS_X, (x - mXOffset) / mXScale); - c.setAxisValue(AMOTION_EVENT_AXIS_Y, (y - mYOffset) / mYScale); - float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, - transformAngle(matrix, orientation, originX, originY)); + transformAngle(matrix, orientation, origin.x, origin.y)); } } -#ifdef __ANDROID__ +#ifdef __linux__ +static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) { + float dsdx, dtdx, tx, dtdy, dsdy, ty; + status_t status = parcel.readFloat(&dsdx); + status |= parcel.readFloat(&dtdx); + status |= parcel.readFloat(&tx); + status |= parcel.readFloat(&dtdy); + status |= parcel.readFloat(&dsdy); + status |= parcel.readFloat(&ty); + + transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); + return status; +} + +static status_t writeToParcel(const ui::Transform& transform, Parcel& parcel) { + status_t status = parcel.writeFloat(transform.dsdx()); + status |= parcel.writeFloat(transform.dtdx()); + status |= parcel.writeFloat(transform.tx()); + status |= parcel.writeFloat(transform.dtdy()); + status |= parcel.writeFloat(transform.dsdy()); + status |= parcel.writeFloat(transform.ty()); + return status; +} + status_t MotionEvent::readFromParcel(Parcel* parcel) { size_t pointerCount = parcel->readInt32(); size_t sampleCount = parcel->readInt32(); @@ -577,10 +591,11 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mMetaState = parcel->readInt32(); mButtonState = parcel->readInt32(); mClassification = static_cast<MotionClassification>(parcel->readByte()); - mXScale = parcel->readFloat(); - mYScale = parcel->readFloat(); - mXOffset = parcel->readFloat(); - mYOffset = parcel->readFloat(); + + result = android::readFromParcel(mTransform, *parcel); + if (result != OK) { + return result; + } mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); mRawXCursorPosition = parcel->readFloat(); @@ -590,7 +605,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mPointerProperties.clear(); mPointerProperties.setCapacity(pointerCount); mSampleEventTimes.clear(); - mSampleEventTimes.setCapacity(sampleCount); + mSampleEventTimes.reserve(sampleCount); mSamplePointerCoords.clear(); mSamplePointerCoords.setCapacity(sampleCount * pointerCount); @@ -603,7 +618,7 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { while (sampleCount > 0) { sampleCount--; - mSampleEventTimes.push(parcel->readInt64()); + mSampleEventTimes.push_back(parcel->readInt64()); for (size_t i = 0; i < pointerCount; i++) { mSamplePointerCoords.push(); status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); @@ -635,10 +650,11 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt32(mMetaState); parcel->writeInt32(mButtonState); parcel->writeByte(static_cast<int8_t>(mClassification)); - parcel->writeFloat(mXScale); - parcel->writeFloat(mYScale); - parcel->writeFloat(mXOffset); - parcel->writeFloat(mYOffset); + + status_t result = android::writeToParcel(mTransform, *parcel); + if (result != OK) { + return result; + } parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); parcel->writeFloat(mRawXCursorPosition); @@ -653,7 +669,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { const PointerCoords* pc = mSamplePointerCoords.array(); for (size_t h = 0; h < sampleCount; h++) { - parcel->writeInt64(mSampleEventTimes.itemAt(h)); + parcel->writeInt64(mSampleEventTimes[h]); for (size_t i = 0; i < pointerCount; i++) { status_t status = (pc++)->writeToParcel(parcel); if (status) { @@ -683,30 +699,44 @@ bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) { } const char* MotionEvent::getLabel(int32_t axis) { - return getAxisLabel(axis); + return InputEventLookup::getAxisLabel(axis); } int32_t MotionEvent::getAxisFromLabel(const char* label) { - return getAxisByLabel(label); + return InputEventLookup::getAxisByLabel(label); } -const char* MotionEvent::actionToString(int32_t action) { +std::string MotionEvent::actionToString(int32_t action) { // Convert MotionEvent action to string switch (action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: return "DOWN"; - case AMOTION_EVENT_ACTION_MOVE: - return "MOVE"; case AMOTION_EVENT_ACTION_UP: return "UP"; + case AMOTION_EVENT_ACTION_MOVE: + return "MOVE"; case AMOTION_EVENT_ACTION_CANCEL: return "CANCEL"; + case AMOTION_EVENT_ACTION_OUTSIDE: + return "OUTSIDE"; case AMOTION_EVENT_ACTION_POINTER_DOWN: return "POINTER_DOWN"; case AMOTION_EVENT_ACTION_POINTER_UP: return "POINTER_UP"; - } - return "UNKNOWN"; + case AMOTION_EVENT_ACTION_HOVER_MOVE: + return "HOVER_MOVE"; + case AMOTION_EVENT_ACTION_SCROLL: + return "SCROLL"; + case AMOTION_EVENT_ACTION_HOVER_ENTER: + return "HOVER_ENTER"; + case AMOTION_EVENT_ACTION_HOVER_EXIT: + return "HOVER_EXIT"; + case AMOTION_EVENT_ACTION_BUTTON_PRESS: + return "BUTTON_PRESS"; + case AMOTION_EVENT_ACTION_BUTTON_RELEASE: + return "BUTTON_RELEASE"; + } + return android::base::StringPrintf("%" PRId32, action); } // --- FocusEvent --- diff --git a/libs/input/InputApplication.cpp b/libs/input/InputApplication.cpp deleted file mode 100644 index 1d9f8a7091..0000000000 --- a/libs/input/InputApplication.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "InputApplication" - -#include <input/InputApplication.h> - -#include <android/log.h> - -namespace android { - -// --- InputApplicationHandle --- - -InputApplicationHandle::InputApplicationHandle() { -} - -InputApplicationHandle::~InputApplicationHandle() { -} - -InputApplicationInfo InputApplicationInfo::read(const Parcel& from) { - InputApplicationInfo ret; - ret.token = from.readStrongBinder(); - ret.name = from.readString8().c_str(); - ret.dispatchingTimeout = from.readInt64(); - - return ret; -} - -status_t InputApplicationInfo::write(Parcel& output) const { - output.writeStrongBinder(token); - output.writeString8(String8(name.c_str())); - output.writeInt64(dispatchingTimeout); - - return OK; -} - -} // namespace android diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 4db9e06d8d..34eba5be7f 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -46,9 +46,9 @@ static bool isValidNameChar(char ch) { static void appendInputDeviceConfigurationFileRelativePath(std::string& path, const std::string& name, InputDeviceConfigurationFileType type) { - path += CONFIGURATION_FILE_DIR[type]; + path += CONFIGURATION_FILE_DIR[static_cast<int32_t>(type)]; path += name; - path += CONFIGURATION_FILE_EXTENSION[type]; + path += CONFIGURATION_FILE_EXTENSION[static_cast<int32_t>(type)]; } std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( @@ -153,14 +153,20 @@ InputDeviceInfo::InputDeviceInfo() { initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false); } -InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : - mId(other.mId), mGeneration(other.mGeneration), mControllerNumber(other.mControllerNumber), - mIdentifier(other.mIdentifier), mAlias(other.mAlias), mIsExternal(other.mIsExternal), - mHasMic(other.mHasMic), mSources(other.mSources), - mKeyboardType(other.mKeyboardType), mKeyCharacterMap(other.mKeyCharacterMap), - mHasVibrator(other.mHasVibrator), mHasButtonUnderPad(other.mHasButtonUnderPad), - mMotionRanges(other.mMotionRanges) { -} +InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) + : mId(other.mId), + mGeneration(other.mGeneration), + mControllerNumber(other.mControllerNumber), + mIdentifier(other.mIdentifier), + mAlias(other.mAlias), + mIsExternal(other.mIsExternal), + mHasMic(other.mHasMic), + mSources(other.mSources), + mKeyboardType(other.mKeyboardType), + mKeyCharacterMap(other.mKeyCharacterMap), + mHasVibrator(other.mHasVibrator), + mHasButtonUnderPad(other.mHasButtonUnderPad), + mMotionRanges(other.mMotionRanges) {} InputDeviceInfo::~InputDeviceInfo() { } diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp new file mode 100644 index 0000000000..c0aa2e26a2 --- /dev/null +++ b/libs/input/InputEventLabels.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/InputEventLabels.h> + +#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key } +#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis } +#define DEFINE_LED(led) { #led, ALED_##led } +#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag } + +namespace android { + +// NOTE: If you add a new keycode here you must also add it to several other files. +// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. +#define KEYCODES_SEQUENCE \ + DEFINE_KEYCODE(UNKNOWN), \ + DEFINE_KEYCODE(SOFT_LEFT), \ + DEFINE_KEYCODE(SOFT_RIGHT), \ + DEFINE_KEYCODE(HOME), \ + DEFINE_KEYCODE(BACK), \ + DEFINE_KEYCODE(CALL), \ + DEFINE_KEYCODE(ENDCALL), \ + DEFINE_KEYCODE(0), \ + DEFINE_KEYCODE(1), \ + DEFINE_KEYCODE(2), \ + DEFINE_KEYCODE(3), \ + DEFINE_KEYCODE(4), \ + DEFINE_KEYCODE(5), \ + DEFINE_KEYCODE(6), \ + DEFINE_KEYCODE(7), \ + DEFINE_KEYCODE(8), \ + DEFINE_KEYCODE(9), \ + DEFINE_KEYCODE(STAR), \ + DEFINE_KEYCODE(POUND), \ + DEFINE_KEYCODE(DPAD_UP), \ + DEFINE_KEYCODE(DPAD_DOWN), \ + DEFINE_KEYCODE(DPAD_LEFT), \ + DEFINE_KEYCODE(DPAD_RIGHT), \ + DEFINE_KEYCODE(DPAD_CENTER), \ + DEFINE_KEYCODE(VOLUME_UP), \ + DEFINE_KEYCODE(VOLUME_DOWN), \ + DEFINE_KEYCODE(POWER), \ + DEFINE_KEYCODE(CAMERA), \ + DEFINE_KEYCODE(CLEAR), \ + DEFINE_KEYCODE(A), \ + DEFINE_KEYCODE(B), \ + DEFINE_KEYCODE(C), \ + DEFINE_KEYCODE(D), \ + DEFINE_KEYCODE(E), \ + DEFINE_KEYCODE(F), \ + DEFINE_KEYCODE(G), \ + DEFINE_KEYCODE(H), \ + DEFINE_KEYCODE(I), \ + DEFINE_KEYCODE(J), \ + DEFINE_KEYCODE(K), \ + DEFINE_KEYCODE(L), \ + DEFINE_KEYCODE(M), \ + DEFINE_KEYCODE(N), \ + DEFINE_KEYCODE(O), \ + DEFINE_KEYCODE(P), \ + DEFINE_KEYCODE(Q), \ + DEFINE_KEYCODE(R), \ + DEFINE_KEYCODE(S), \ + DEFINE_KEYCODE(T), \ + DEFINE_KEYCODE(U), \ + DEFINE_KEYCODE(V), \ + DEFINE_KEYCODE(W), \ + DEFINE_KEYCODE(X), \ + DEFINE_KEYCODE(Y), \ + DEFINE_KEYCODE(Z), \ + DEFINE_KEYCODE(COMMA), \ + DEFINE_KEYCODE(PERIOD), \ + DEFINE_KEYCODE(ALT_LEFT), \ + DEFINE_KEYCODE(ALT_RIGHT), \ + DEFINE_KEYCODE(SHIFT_LEFT), \ + DEFINE_KEYCODE(SHIFT_RIGHT), \ + DEFINE_KEYCODE(TAB), \ + DEFINE_KEYCODE(SPACE), \ + DEFINE_KEYCODE(SYM), \ + DEFINE_KEYCODE(EXPLORER), \ + DEFINE_KEYCODE(ENVELOPE), \ + DEFINE_KEYCODE(ENTER), \ + DEFINE_KEYCODE(DEL), \ + DEFINE_KEYCODE(GRAVE), \ + DEFINE_KEYCODE(MINUS), \ + DEFINE_KEYCODE(EQUALS), \ + DEFINE_KEYCODE(LEFT_BRACKET), \ + DEFINE_KEYCODE(RIGHT_BRACKET), \ + DEFINE_KEYCODE(BACKSLASH), \ + DEFINE_KEYCODE(SEMICOLON), \ + DEFINE_KEYCODE(APOSTROPHE), \ + DEFINE_KEYCODE(SLASH), \ + DEFINE_KEYCODE(AT), \ + DEFINE_KEYCODE(NUM), \ + DEFINE_KEYCODE(HEADSETHOOK), \ + DEFINE_KEYCODE(FOCUS), \ + DEFINE_KEYCODE(PLUS), \ + DEFINE_KEYCODE(MENU), \ + DEFINE_KEYCODE(NOTIFICATION), \ + DEFINE_KEYCODE(SEARCH), \ + DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \ + DEFINE_KEYCODE(MEDIA_STOP), \ + DEFINE_KEYCODE(MEDIA_NEXT), \ + DEFINE_KEYCODE(MEDIA_PREVIOUS), \ + DEFINE_KEYCODE(MEDIA_REWIND), \ + DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \ + DEFINE_KEYCODE(MUTE), \ + DEFINE_KEYCODE(PAGE_UP), \ + DEFINE_KEYCODE(PAGE_DOWN), \ + DEFINE_KEYCODE(PICTSYMBOLS), \ + DEFINE_KEYCODE(SWITCH_CHARSET), \ + DEFINE_KEYCODE(BUTTON_A), \ + DEFINE_KEYCODE(BUTTON_B), \ + DEFINE_KEYCODE(BUTTON_C), \ + DEFINE_KEYCODE(BUTTON_X), \ + DEFINE_KEYCODE(BUTTON_Y), \ + DEFINE_KEYCODE(BUTTON_Z), \ + DEFINE_KEYCODE(BUTTON_L1), \ + DEFINE_KEYCODE(BUTTON_R1), \ + DEFINE_KEYCODE(BUTTON_L2), \ + DEFINE_KEYCODE(BUTTON_R2), \ + DEFINE_KEYCODE(BUTTON_THUMBL), \ + DEFINE_KEYCODE(BUTTON_THUMBR), \ + DEFINE_KEYCODE(BUTTON_START), \ + DEFINE_KEYCODE(BUTTON_SELECT), \ + DEFINE_KEYCODE(BUTTON_MODE), \ + DEFINE_KEYCODE(ESCAPE), \ + DEFINE_KEYCODE(FORWARD_DEL), \ + DEFINE_KEYCODE(CTRL_LEFT), \ + DEFINE_KEYCODE(CTRL_RIGHT), \ + DEFINE_KEYCODE(CAPS_LOCK), \ + DEFINE_KEYCODE(SCROLL_LOCK), \ + DEFINE_KEYCODE(META_LEFT), \ + DEFINE_KEYCODE(META_RIGHT), \ + DEFINE_KEYCODE(FUNCTION), \ + DEFINE_KEYCODE(SYSRQ), \ + DEFINE_KEYCODE(BREAK), \ + DEFINE_KEYCODE(MOVE_HOME), \ + DEFINE_KEYCODE(MOVE_END), \ + DEFINE_KEYCODE(INSERT), \ + DEFINE_KEYCODE(FORWARD), \ + DEFINE_KEYCODE(MEDIA_PLAY), \ + DEFINE_KEYCODE(MEDIA_PAUSE), \ + DEFINE_KEYCODE(MEDIA_CLOSE), \ + DEFINE_KEYCODE(MEDIA_EJECT), \ + DEFINE_KEYCODE(MEDIA_RECORD), \ + DEFINE_KEYCODE(F1), \ + DEFINE_KEYCODE(F2), \ + DEFINE_KEYCODE(F3), \ + DEFINE_KEYCODE(F4), \ + DEFINE_KEYCODE(F5), \ + DEFINE_KEYCODE(F6), \ + DEFINE_KEYCODE(F7), \ + DEFINE_KEYCODE(F8), \ + DEFINE_KEYCODE(F9), \ + DEFINE_KEYCODE(F10), \ + DEFINE_KEYCODE(F11), \ + DEFINE_KEYCODE(F12), \ + DEFINE_KEYCODE(NUM_LOCK), \ + DEFINE_KEYCODE(NUMPAD_0), \ + DEFINE_KEYCODE(NUMPAD_1), \ + DEFINE_KEYCODE(NUMPAD_2), \ + DEFINE_KEYCODE(NUMPAD_3), \ + DEFINE_KEYCODE(NUMPAD_4), \ + DEFINE_KEYCODE(NUMPAD_5), \ + DEFINE_KEYCODE(NUMPAD_6), \ + DEFINE_KEYCODE(NUMPAD_7), \ + DEFINE_KEYCODE(NUMPAD_8), \ + DEFINE_KEYCODE(NUMPAD_9), \ + DEFINE_KEYCODE(NUMPAD_DIVIDE), \ + DEFINE_KEYCODE(NUMPAD_MULTIPLY), \ + DEFINE_KEYCODE(NUMPAD_SUBTRACT), \ + DEFINE_KEYCODE(NUMPAD_ADD), \ + DEFINE_KEYCODE(NUMPAD_DOT), \ + DEFINE_KEYCODE(NUMPAD_COMMA), \ + DEFINE_KEYCODE(NUMPAD_ENTER), \ + DEFINE_KEYCODE(NUMPAD_EQUALS), \ + DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \ + DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \ + DEFINE_KEYCODE(VOLUME_MUTE), \ + DEFINE_KEYCODE(INFO), \ + DEFINE_KEYCODE(CHANNEL_UP), \ + DEFINE_KEYCODE(CHANNEL_DOWN), \ + DEFINE_KEYCODE(ZOOM_IN), \ + DEFINE_KEYCODE(ZOOM_OUT), \ + DEFINE_KEYCODE(TV), \ + DEFINE_KEYCODE(WINDOW), \ + DEFINE_KEYCODE(GUIDE), \ + DEFINE_KEYCODE(DVR), \ + DEFINE_KEYCODE(BOOKMARK), \ + DEFINE_KEYCODE(CAPTIONS), \ + DEFINE_KEYCODE(SETTINGS), \ + DEFINE_KEYCODE(TV_POWER), \ + DEFINE_KEYCODE(TV_INPUT), \ + DEFINE_KEYCODE(STB_POWER), \ + DEFINE_KEYCODE(STB_INPUT), \ + DEFINE_KEYCODE(AVR_POWER), \ + DEFINE_KEYCODE(AVR_INPUT), \ + DEFINE_KEYCODE(PROG_RED), \ + DEFINE_KEYCODE(PROG_GREEN), \ + DEFINE_KEYCODE(PROG_YELLOW), \ + DEFINE_KEYCODE(PROG_BLUE), \ + DEFINE_KEYCODE(APP_SWITCH), \ + DEFINE_KEYCODE(BUTTON_1), \ + DEFINE_KEYCODE(BUTTON_2), \ + DEFINE_KEYCODE(BUTTON_3), \ + DEFINE_KEYCODE(BUTTON_4), \ + DEFINE_KEYCODE(BUTTON_5), \ + DEFINE_KEYCODE(BUTTON_6), \ + DEFINE_KEYCODE(BUTTON_7), \ + DEFINE_KEYCODE(BUTTON_8), \ + DEFINE_KEYCODE(BUTTON_9), \ + DEFINE_KEYCODE(BUTTON_10), \ + DEFINE_KEYCODE(BUTTON_11), \ + DEFINE_KEYCODE(BUTTON_12), \ + DEFINE_KEYCODE(BUTTON_13), \ + DEFINE_KEYCODE(BUTTON_14), \ + DEFINE_KEYCODE(BUTTON_15), \ + DEFINE_KEYCODE(BUTTON_16), \ + DEFINE_KEYCODE(LANGUAGE_SWITCH), \ + DEFINE_KEYCODE(MANNER_MODE), \ + DEFINE_KEYCODE(3D_MODE), \ + DEFINE_KEYCODE(CONTACTS), \ + DEFINE_KEYCODE(CALENDAR), \ + DEFINE_KEYCODE(MUSIC), \ + DEFINE_KEYCODE(CALCULATOR), \ + DEFINE_KEYCODE(ZENKAKU_HANKAKU), \ + DEFINE_KEYCODE(EISU), \ + DEFINE_KEYCODE(MUHENKAN), \ + DEFINE_KEYCODE(HENKAN), \ + DEFINE_KEYCODE(KATAKANA_HIRAGANA), \ + DEFINE_KEYCODE(YEN), \ + DEFINE_KEYCODE(RO), \ + DEFINE_KEYCODE(KANA), \ + DEFINE_KEYCODE(ASSIST), \ + DEFINE_KEYCODE(BRIGHTNESS_DOWN), \ + DEFINE_KEYCODE(BRIGHTNESS_UP), \ + DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \ + DEFINE_KEYCODE(SLEEP), \ + DEFINE_KEYCODE(WAKEUP), \ + DEFINE_KEYCODE(PAIRING), \ + DEFINE_KEYCODE(MEDIA_TOP_MENU), \ + DEFINE_KEYCODE(11), \ + DEFINE_KEYCODE(12), \ + DEFINE_KEYCODE(LAST_CHANNEL), \ + DEFINE_KEYCODE(TV_DATA_SERVICE), \ + DEFINE_KEYCODE(VOICE_ASSIST), \ + DEFINE_KEYCODE(TV_RADIO_SERVICE), \ + DEFINE_KEYCODE(TV_TELETEXT), \ + DEFINE_KEYCODE(TV_NUMBER_ENTRY), \ + DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \ + DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \ + DEFINE_KEYCODE(TV_SATELLITE), \ + DEFINE_KEYCODE(TV_SATELLITE_BS), \ + DEFINE_KEYCODE(TV_SATELLITE_CS), \ + DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \ + DEFINE_KEYCODE(TV_NETWORK), \ + DEFINE_KEYCODE(TV_ANTENNA_CABLE), \ + DEFINE_KEYCODE(TV_INPUT_HDMI_1), \ + DEFINE_KEYCODE(TV_INPUT_HDMI_2), \ + DEFINE_KEYCODE(TV_INPUT_HDMI_3), \ + DEFINE_KEYCODE(TV_INPUT_HDMI_4), \ + DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \ + DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \ + DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \ + DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \ + DEFINE_KEYCODE(TV_INPUT_VGA_1), \ + DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \ + DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \ + DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \ + DEFINE_KEYCODE(TV_ZOOM_MODE), \ + DEFINE_KEYCODE(TV_CONTENTS_MENU), \ + DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \ + DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \ + DEFINE_KEYCODE(HELP), \ + DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \ + DEFINE_KEYCODE(NAVIGATE_NEXT), \ + DEFINE_KEYCODE(NAVIGATE_IN), \ + DEFINE_KEYCODE(NAVIGATE_OUT), \ + DEFINE_KEYCODE(STEM_PRIMARY), \ + DEFINE_KEYCODE(STEM_1), \ + DEFINE_KEYCODE(STEM_2), \ + DEFINE_KEYCODE(STEM_3), \ + DEFINE_KEYCODE(DPAD_UP_LEFT), \ + DEFINE_KEYCODE(DPAD_DOWN_LEFT), \ + DEFINE_KEYCODE(DPAD_UP_RIGHT), \ + DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \ + DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \ + DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \ + DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \ + DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \ + DEFINE_KEYCODE(SOFT_SLEEP), \ + DEFINE_KEYCODE(CUT), \ + DEFINE_KEYCODE(COPY), \ + DEFINE_KEYCODE(PASTE), \ + DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \ + DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \ + DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \ + DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \ + DEFINE_KEYCODE(ALL_APPS), \ + DEFINE_KEYCODE(REFRESH), \ + DEFINE_KEYCODE(THUMBS_UP), \ + DEFINE_KEYCODE(THUMBS_DOWN), \ + DEFINE_KEYCODE(PROFILE_SWITCH) + +// NOTE: If you add a new axis here you must also add it to several other files. +// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. +#define AXES_SEQUENCE \ + DEFINE_AXIS(X), \ + DEFINE_AXIS(Y), \ + DEFINE_AXIS(PRESSURE), \ + DEFINE_AXIS(SIZE), \ + DEFINE_AXIS(TOUCH_MAJOR), \ + DEFINE_AXIS(TOUCH_MINOR), \ + DEFINE_AXIS(TOOL_MAJOR), \ + DEFINE_AXIS(TOOL_MINOR), \ + DEFINE_AXIS(ORIENTATION), \ + DEFINE_AXIS(VSCROLL), \ + DEFINE_AXIS(HSCROLL), \ + DEFINE_AXIS(Z), \ + DEFINE_AXIS(RX), \ + DEFINE_AXIS(RY), \ + DEFINE_AXIS(RZ), \ + DEFINE_AXIS(HAT_X), \ + DEFINE_AXIS(HAT_Y), \ + DEFINE_AXIS(LTRIGGER), \ + DEFINE_AXIS(RTRIGGER), \ + DEFINE_AXIS(THROTTLE), \ + DEFINE_AXIS(RUDDER), \ + DEFINE_AXIS(WHEEL), \ + DEFINE_AXIS(GAS), \ + DEFINE_AXIS(BRAKE), \ + DEFINE_AXIS(DISTANCE), \ + DEFINE_AXIS(TILT), \ + DEFINE_AXIS(SCROLL), \ + DEFINE_AXIS(RELATIVE_X), \ + DEFINE_AXIS(RELATIVE_Y), \ + {"RESERVED_29", 29}, \ + {"RESERVED_30", 30}, \ + {"RESERVED_31", 31}, \ + DEFINE_AXIS(GENERIC_1), \ + DEFINE_AXIS(GENERIC_2), \ + DEFINE_AXIS(GENERIC_3), \ + DEFINE_AXIS(GENERIC_4), \ + DEFINE_AXIS(GENERIC_5), \ + DEFINE_AXIS(GENERIC_6), \ + DEFINE_AXIS(GENERIC_7), \ + DEFINE_AXIS(GENERIC_8), \ + DEFINE_AXIS(GENERIC_9), \ + DEFINE_AXIS(GENERIC_10), \ + DEFINE_AXIS(GENERIC_11), \ + DEFINE_AXIS(GENERIC_12), \ + DEFINE_AXIS(GENERIC_13), \ + DEFINE_AXIS(GENERIC_14), \ + DEFINE_AXIS(GENERIC_15), \ + DEFINE_AXIS(GENERIC_16) + + +// NOTE: If you add new LEDs here, you must also add them to Input.h +#define LEDS_SEQUENCE \ + DEFINE_LED(NUM_LOCK), \ + DEFINE_LED(CAPS_LOCK), \ + DEFINE_LED(SCROLL_LOCK), \ + DEFINE_LED(COMPOSE), \ + DEFINE_LED(KANA), \ + DEFINE_LED(SLEEP), \ + DEFINE_LED(SUSPEND), \ + DEFINE_LED(MUTE), \ + DEFINE_LED(MISC), \ + DEFINE_LED(MAIL), \ + DEFINE_LED(CHARGING), \ + DEFINE_LED(CONTROLLER_1), \ + DEFINE_LED(CONTROLLER_2), \ + DEFINE_LED(CONTROLLER_3), \ + DEFINE_LED(CONTROLLER_4) + +#define FLAGS_SEQUENCE \ + DEFINE_FLAG(VIRTUAL), \ + DEFINE_FLAG(FUNCTION), \ + DEFINE_FLAG(GESTURE), \ + DEFINE_FLAG(WAKE) + +// --- InputEventLookup --- +const std::unordered_map<std::string, int> InputEventLookup::KEYCODES = {KEYCODES_SEQUENCE}; + +const std::vector<InputEventLabel> InputEventLookup::KEY_NAMES = {KEYCODES_SEQUENCE}; + +const std::unordered_map<std::string, int> InputEventLookup::AXES = {AXES_SEQUENCE}; + +const std::vector<InputEventLabel> InputEventLookup::AXES_NAMES = {AXES_SEQUENCE}; + +const std::unordered_map<std::string, int> InputEventLookup::LEDS = {LEDS_SEQUENCE}; + +const std::unordered_map<std::string, int> InputEventLookup::FLAGS = {FLAGS_SEQUENCE}; + +int InputEventLookup::lookupValueByLabel(const std::unordered_map<std::string, int>& map, + const char* literal) { + std::string str(literal); + auto it = map.find(str); + return it != map.end() ? it->second : 0; +} + +const char* InputEventLookup::lookupLabelByValue(const std::vector<InputEventLabel>& vec, + int value) { + if (static_cast<size_t>(value) < vec.size()) { + return vec[value].literal; + } + return nullptr; +} + +int32_t InputEventLookup::getKeyCodeByLabel(const char* label) { + return int32_t(lookupValueByLabel(KEYCODES, label)); +} + +const char* InputEventLookup::getLabelByKeyCode(int32_t keyCode) { + if (keyCode >= 0 && static_cast<size_t>(keyCode) < KEYCODES.size()) { + return lookupLabelByValue(KEY_NAMES, keyCode); + } + return nullptr; +} + +uint32_t InputEventLookup::getKeyFlagByLabel(const char* label) { + return uint32_t(lookupValueByLabel(FLAGS, label)); +} + +int32_t InputEventLookup::getAxisByLabel(const char* label) { + return int32_t(lookupValueByLabel(AXES, label)); +} + +const char* InputEventLookup::getAxisLabel(int32_t axisId) { + return lookupLabelByValue(AXES_NAMES, axisId); +} + +int32_t InputEventLookup::getLedByLabel(const char* label) { + return int32_t(lookupValueByLabel(LEDS, label)); +} + +} // namespace android diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 11af23e1a2..85df405f98 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -133,12 +133,11 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // Write the header msg->header.type = header.type; + msg->header.seq = header.seq; // Write the body switch(header.type) { case InputMessage::Type::KEY: { - // uint32_t seq - msg->body.key.seq = body.key.seq; // int32_t eventId msg->body.key.eventId = body.key.eventId; // nsecs_t eventTime @@ -168,8 +167,6 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { break; } case InputMessage::Type::MOTION: { - // uint32_t seq - msg->body.motion.seq = body.motion.seq; // int32_t eventId msg->body.motion.eventId = body.motion.eventId; // nsecs_t eventTime @@ -198,14 +195,14 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.edgeFlags = body.motion.edgeFlags; // nsecs_t downTime msg->body.motion.downTime = body.motion.downTime; - // float xScale - msg->body.motion.xScale = body.motion.xScale; - // float yScale - msg->body.motion.yScale = body.motion.yScale; - // float xOffset - msg->body.motion.xOffset = body.motion.xOffset; - // float yOffset - msg->body.motion.yOffset = body.motion.yOffset; + + msg->body.motion.dsdx = body.motion.dsdx; + msg->body.motion.dtdx = body.motion.dtdx; + msg->body.motion.dtdy = body.motion.dtdy; + msg->body.motion.dsdy = body.motion.dsdy; + msg->body.motion.tx = body.motion.tx; + msg->body.motion.ty = body.motion.ty; + // float xPrecision msg->body.motion.xPrecision = body.motion.xPrecision; // float yPrecision @@ -232,12 +229,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { break; } case InputMessage::Type::FINISHED: { - msg->body.finished.seq = body.finished.seq; msg->body.finished.handled = body.finished.handled; break; } case InputMessage::Type::FOCUS: { - msg->body.focus.seq = body.focus.seq; msg->body.focus.eventId = body.focus.eventId; msg->body.focus.hasFocus = body.focus.hasFocus; msg->body.focus.inTouchMode = body.focus.inTouchMode; @@ -248,39 +243,40 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // --- InputChannel --- -sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd, - sp<IBinder> token) { +std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, + android::base::unique_fd fd, sp<IBinder> token) { const int result = fcntl(fd, F_SETFL, O_NONBLOCK); if (result != 0) { LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(), strerror(errno)); return nullptr; } - return new InputChannel(name, std::move(fd), token); + // using 'new' to access a non-public constructor + return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token)); } -InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token) - : mName(name), mFd(std::move(fd)), mToken(token) { +InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) + : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) { if (DEBUG_CHANNEL_LIFECYCLE) { - ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get()); + ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get()); } } InputChannel::~InputChannel() { if (DEBUG_CHANNEL_LIFECYCLE) { - ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); + ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get()); } } status_t InputChannel::openInputChannelPair(const std::string& name, - sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { + std::unique_ptr<InputChannel>& outServerChannel, + std::unique_ptr<InputChannel>& outClientChannel) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_t result = -errno; - ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", - name.c_str(), errno); - outServerChannel.clear(); - outClientChannel.clear(); + ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.c_str(), errno); + outServerChannel.reset(); + outClientChannel.reset(); return result; } @@ -308,7 +304,7 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { @@ -343,7 +339,7 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { @@ -380,59 +376,59 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { return OK; } -sp<InputChannel> InputChannel::dup() const { - android::base::unique_fd newFd(::dup(getFd())); - if (!newFd.ok()) { - ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(), - strerror(errno)); - const bool hitFdLimit = errno == EMFILE || errno == ENFILE; - // If this process is out of file descriptors, then throwing that might end up exploding - // on the other side of a binder call, which isn't really helpful. - // Better to just crash here and hope that the FD leak is slow. - // Other failures could be client errors, so we still propagate those back to the caller. - LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", - getName().c_str()); - return nullptr; - } - return InputChannel::create(mName, std::move(newFd), mToken); +std::unique_ptr<InputChannel> InputChannel::dup() const { + base::unique_fd newFd(dupFd()); + return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); } -status_t InputChannel::write(Parcel& out) const { - status_t s = out.writeCString(getName().c_str()); - if (s != OK) { - return s; - } +void InputChannel::copyTo(InputChannel& outChannel) const { + outChannel.mName = getName(); + outChannel.mFd = dupFd(); + outChannel.mToken = getConnectionToken(); +} - s = out.writeStrongBinder(mToken); - if (s != OK) { - return s; +status_t InputChannel::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; } - - s = out.writeUniqueFileDescriptor(mFd); - return s; + return parcel->writeStrongBinder(mToken) + ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd); } -sp<InputChannel> InputChannel::read(const Parcel& from) { - std::string name = from.readCString(); - sp<IBinder> token = from.readStrongBinder(); - android::base::unique_fd rawFd; - status_t fdResult = from.readUniqueFileDescriptor(&rawFd); - if (fdResult != OK) { - return nullptr; +status_t InputChannel::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; } - - return InputChannel::create(name, std::move(rawFd), token); + mToken = parcel->readStrongBinder(); + return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd); } sp<IBinder> InputChannel::getConnectionToken() const { return mToken; } +base::unique_fd InputChannel::dupFd() const { + android::base::unique_fd newFd(::dup(getFd())); + if (!newFd.ok()) { + ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(), + strerror(errno)); + const bool hitFdLimit = errno == EMFILE || errno == ENFILE; + // If this process is out of file descriptors, then throwing that might end up exploding + // on the other side of a binder call, which isn't really helpful. + // Better to just crash here and hope that the FD leak is slow. + // Other failures could be client errors, so we still propagate those back to the caller. + LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", + getName().c_str()); + return {}; + } + return newFd; +} + // --- InputPublisher --- -InputPublisher::InputPublisher(const sp<InputChannel>& channel) : - mChannel(channel) { -} +InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {} InputPublisher::~InputPublisher() { } @@ -463,7 +459,7 @@ status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t InputMessage msg; msg.header.type = InputMessage::Type::KEY; - msg.body.key.seq = seq; + msg.header.seq = seq; msg.body.key.eventId = eventId; msg.body.key.deviceId = deviceId; msg.body.key.source = source; @@ -484,10 +480,10 @@ status_t InputPublisher::publishMotionEvent( uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, - MotionClassification classification, float xScale, float yScale, float xOffset, - float yOffset, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { + MotionClassification classification, const ui::Transform& transform, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, + nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", @@ -495,17 +491,18 @@ status_t InputPublisher::publishMotionEvent( ATRACE_NAME(message.c_str()); } if (DEBUG_TRANSPORT_ACTIONS) { + std::string transformString; + transform.dump(transformString, "transform", " "); ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "displayId=%" PRId32 ", " "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " - "metaState=0x%x, buttonState=0x%x, classification=%s, xScale=%.1f, yScale=%.1f, " - "xOffset=%.1f, yOffset=%.1f, " + "metaState=0x%x, buttonState=0x%x, classification=%s," "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " - "pointerCount=%" PRIu32, + "pointerCount=%" PRIu32 " \n%s", mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState, buttonState, - motionClassificationToString(classification), xScale, yScale, xOffset, yOffset, - xPrecision, yPrecision, downTime, eventTime, pointerCount); + motionClassificationToString(classification), xPrecision, yPrecision, downTime, + eventTime, pointerCount, transformString.c_str()); } if (!seq) { @@ -521,7 +518,7 @@ status_t InputPublisher::publishMotionEvent( InputMessage msg; msg.header.type = InputMessage::Type::MOTION; - msg.body.motion.seq = seq; + msg.header.seq = seq; msg.body.motion.eventId = eventId; msg.body.motion.deviceId = deviceId; msg.body.motion.source = source; @@ -534,10 +531,12 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.metaState = metaState; msg.body.motion.buttonState = buttonState; msg.body.motion.classification = classification; - msg.body.motion.xScale = xScale; - msg.body.motion.yScale = yScale; - msg.body.motion.xOffset = xOffset; - msg.body.motion.yOffset = yOffset; + msg.body.motion.dsdx = transform.dsdx(); + msg.body.motion.dtdx = transform.dtdx(); + msg.body.motion.dtdy = transform.dtdy(); + msg.body.motion.dsdy = transform.dsdy(); + msg.body.motion.tx = transform.tx(); + msg.body.motion.ty = transform.ty(); msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; msg.body.motion.xCursorPosition = xCursorPosition; @@ -565,7 +564,7 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool h InputMessage msg; msg.header.type = InputMessage::Type::FOCUS; - msg.body.focus.seq = seq; + msg.header.seq = seq; msg.body.focus.eventId = eventId; msg.body.focus.hasFocus = hasFocus ? 1 : 0; msg.body.focus.inTouchMode = inTouchMode ? 1 : 0; @@ -589,17 +588,15 @@ status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandle mChannel->getName().c_str(), msg.header.type); return UNKNOWN_ERROR; } - *outSeq = msg.body.finished.seq; + *outSeq = msg.header.seq; *outHandled = msg.body.finished.handled == 1; return OK; } // --- InputConsumer --- -InputConsumer::InputConsumer(const sp<InputChannel>& channel) : - mResampleTouch(isTouchResamplingEnabled()), - mChannel(channel), mMsgDeferred(false) { -} +InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel) + : mResampleTouch(isTouchResamplingEnabled()), mChannel(channel), mMsgDeferred(false) {} InputConsumer::~InputConsumer() { } @@ -650,7 +647,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum if (!keyEvent) return NO_MEMORY; initializeKeyEvent(keyEvent, &mMsg); - *outSeq = mMsg.body.key.seq; + *outSeq = mMsg.header.seq; *outEvent = keyEvent; if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", @@ -662,9 +659,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum case InputMessage::Type::MOTION: { ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); if (batchIndex >= 0) { - Batch& batch = mBatches.editItemAt(batchIndex); + Batch& batch = mBatches[batchIndex]; if (canAddSample(batch, &mMsg)) { - batch.samples.push(mMsg); + batch.samples.push_back(mMsg); if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ appended to batch event", mChannel->getName().c_str()); @@ -675,18 +672,18 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum // No need to process events that we are going to cancel anyways const size_t count = batch.samples.size(); for (size_t i = 0; i < count; i++) { - const InputMessage& msg = batch.samples.itemAt(i); - sendFinishedSignal(msg.body.motion.seq, false); + const InputMessage& msg = batch.samples[i]; + sendFinishedSignal(msg.header.seq, false); } - batch.samples.removeItemsAt(0, count); - mBatches.removeAt(batchIndex); + batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); + mBatches.erase(mBatches.begin() + batchIndex); } else { // We cannot append to the batch in progress, so we need to consume // the previous batch right now and defer the new message until later. mMsgDeferred = true; status_t result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); - mBatches.removeAt(batchIndex); + mBatches.erase(mBatches.begin() + batchIndex); if (result) { return result; } @@ -702,9 +699,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum // Start a new batch if needed. if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mBatches.push(); - Batch& batch = mBatches.editTop(); - batch.samples.push(mMsg); + Batch batch; + batch.samples.push_back(mMsg); + mBatches.push_back(batch); if (DEBUG_TRANSPORT_ACTIONS) { ALOGD("channel '%s' consumer ~ started batch event", mChannel->getName().c_str()); @@ -717,7 +714,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum updateTouchState(mMsg); initializeMotionEvent(motionEvent, &mMsg); - *outSeq = mMsg.body.motion.seq; + *outSeq = mMsg.header.seq; *outEvent = motionEvent; if (DEBUG_TRANSPORT_ACTIONS) { @@ -738,7 +735,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum if (!focusEvent) return NO_MEMORY; initializeFocusEvent(focusEvent, &mMsg); - *outSeq = mMsg.body.focus.seq; + *outSeq = mMsg.header.seq; *outEvent = focusEvent; break; } @@ -752,10 +749,10 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, status_t result; for (size_t i = mBatches.size(); i > 0; ) { i--; - Batch& batch = mBatches.editItemAt(i); + Batch& batch = mBatches[i]; if (frameTime < 0) { result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent); - mBatches.removeAt(i); + mBatches.erase(mBatches.begin() + i); return result; } @@ -770,11 +767,11 @@ status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); const InputMessage* next; - if (batch.samples.isEmpty()) { - mBatches.removeAt(i); + if (batch.samples.empty()) { + mBatches.erase(mBatches.begin() + i); next = nullptr; } else { - next = &batch.samples.itemAt(0); + next = &batch.samples[0]; } if (!result && mResampleTouch) { resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next); @@ -792,20 +789,20 @@ status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, uint32_t chain = 0; for (size_t i = 0; i < count; i++) { - InputMessage& msg = batch.samples.editItemAt(i); + InputMessage& msg = batch.samples[i]; updateTouchState(msg); if (i) { SeqChain seqChain; - seqChain.seq = msg.body.motion.seq; + seqChain.seq = msg.header.seq; seqChain.chain = chain; - mSeqChains.push(seqChain); + mSeqChains.push_back(seqChain); addSample(motionEvent, &msg); } else { initializeMotionEvent(motionEvent, &msg); } - chain = msg.body.motion.seq; + chain = msg.header.seq; } - batch.samples.removeItemsAt(0, count); + batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count); *outSeq = chain; *outEvent = motionEvent; @@ -827,10 +824,10 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_DOWN: { ssize_t index = findTouchState(deviceId, source); if (index < 0) { - mTouchStates.push(); + mTouchStates.push_back({}); index = mTouchStates.size() - 1; } - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; touchState.initialize(deviceId, source); touchState.addHistory(msg); break; @@ -839,7 +836,7 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_MOVE: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; touchState.addHistory(msg); rewriteMessage(touchState, msg); } @@ -849,7 +846,7 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_POINTER_DOWN: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); rewriteMessage(touchState, msg); } @@ -859,7 +856,7 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_POINTER_UP: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; rewriteMessage(touchState, msg); touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId()); } @@ -869,7 +866,7 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_SCROLL: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; rewriteMessage(touchState, msg); } break; @@ -879,9 +876,9 @@ void InputConsumer::updateTouchState(InputMessage& msg) { case AMOTION_EVENT_ACTION_CANCEL: { ssize_t index = findTouchState(deviceId, source); if (index >= 0) { - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; rewriteMessage(touchState, msg); - mTouchStates.removeAt(index); + mTouchStates.erase(mTouchStates.begin() + index); } break; } @@ -938,7 +935,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, return; } - TouchState& touchState = mTouchStates.editItemAt(index); + TouchState& touchState = mTouchStates[index]; if (touchState.historySize < 1) { #if DEBUG_RESAMPLING ALOGD("Not resampled, no history for device."); @@ -1084,11 +1081,11 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { size_t chainIndex = 0; for (size_t i = seqChainCount; i > 0; ) { i--; - const SeqChain& seqChain = mSeqChains.itemAt(i); + const SeqChain& seqChain = mSeqChains[i]; if (seqChain.seq == currentSeq) { currentSeq = seqChain.chain; chainSeqs[chainIndex++] = currentSeq; - mSeqChains.removeAt(i); + mSeqChains.erase(mSeqChains.begin() + i); } } status_t status = OK; @@ -1102,7 +1099,7 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { SeqChain seqChain; seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; seqChain.chain = chainSeqs[chainIndex]; - mSeqChains.push(seqChain); + mSeqChains.push_back(seqChain); if (!chainIndex) break; chainIndex--; } @@ -1117,7 +1114,7 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { InputMessage msg; msg.header.type = InputMessage::Type::FINISHED; - msg.body.finished.seq = seq; + msg.header.seq = seq; msg.body.finished.handled = handled ? 1 : 0; return mChannel->sendMessage(&msg); } @@ -1127,23 +1124,23 @@ bool InputConsumer::hasDeferredEvent() const { } bool InputConsumer::hasPendingBatch() const { - return !mBatches.isEmpty(); + return !mBatches.empty(); } int32_t InputConsumer::getPendingBatchSource() const { - if (mBatches.isEmpty()) { + if (mBatches.empty()) { return AINPUT_SOURCE_CLASS_NONE; } - const Batch& batch = mBatches.itemAt(0); - const InputMessage& head = batch.samples.itemAt(0); + const Batch& batch = mBatches[0]; + const InputMessage& head = batch.samples[0]; return head.body.motion.source; } ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mBatches.size(); i++) { - const Batch& batch = mBatches.itemAt(i); - const InputMessage& head = batch.samples.itemAt(0); + const Batch& batch = mBatches[i]; + const InputMessage& head = batch.samples[0]; if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { return i; } @@ -1153,7 +1150,7 @@ ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mTouchStates.size(); i++) { - const TouchState& touchState = mTouchStates.itemAt(i); + const TouchState& touchState = mTouchStates[i]; if (touchState.deviceId == deviceId && touchState.source == source) { return i; } @@ -1183,16 +1180,18 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } + ui::Transform transform; + transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx, + msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1}); event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, msg->body.motion.edgeFlags, msg->body.motion.metaState, - msg->body.motion.buttonState, msg->body.motion.classification, - msg->body.motion.xScale, msg->body.motion.yScale, msg->body.motion.xOffset, - msg->body.motion.yOffset, msg->body.motion.xPrecision, - msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, - msg->body.motion.yCursorPosition, msg->body.motion.downTime, - msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); + msg->body.motion.buttonState, msg->body.motion.classification, transform, + msg->body.motion.xPrecision, msg->body.motion.yPrecision, + msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, + pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { @@ -1207,7 +1206,7 @@ void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { } bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { - const InputMessage& head = batch.samples.itemAt(0); + const InputMessage& head = batch.samples[0]; uint32_t pointerCount = msg->body.motion.pointerCount; if (head.body.motion.pointerCount != pointerCount || head.body.motion.action != msg->body.motion.action) { @@ -1225,11 +1224,72 @@ bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { size_t numSamples = batch.samples.size(); size_t index = 0; - while (index < numSamples - && batch.samples.itemAt(index).body.motion.eventTime <= time) { + while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) { index += 1; } return ssize_t(index) - 1; } +std::string InputConsumer::dump() const { + std::string out; + out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n"; + out = out + "mChannel = " + mChannel->getName() + "\n"; + out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n"; + if (mMsgDeferred) { + out = out + "mMsg : " + InputMessage::typeToString(mMsg.header.type) + "\n"; + } + out += "Batches:\n"; + for (const Batch& batch : mBatches) { + out += " Batch:\n"; + for (const InputMessage& msg : batch.samples) { + out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq, + InputMessage::typeToString(msg.header.type)); + switch (msg.header.type) { + case InputMessage::Type::KEY: { + out += android::base::StringPrintf("action=%s keycode=%" PRId32, + KeyEvent::actionToString( + msg.body.key.action), + msg.body.key.keyCode); + break; + } + case InputMessage::Type::MOTION: { + out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action); + for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) { + const float x = msg.body.motion.pointers[i].coords.getX(); + const float y = msg.body.motion.pointers[i].coords.getY(); + out += android::base::StringPrintf("\n Pointer %" PRIu32 + " : x=%.1f y=%.1f", + i, x, y); + } + break; + } + case InputMessage::Type::FINISHED: { + out += android::base::StringPrintf("handled=%s", + toString(msg.body.finished.handled)); + break; + } + case InputMessage::Type::FOCUS: { + out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s", + toString(msg.body.focus.hasFocus), + toString(msg.body.focus.inTouchMode)); + break; + } + } + out += "\n"; + } + } + if (mBatches.empty()) { + out += " <empty>\n"; + } + out += "mSeqChains:\n"; + for (const SeqChain& chain : mSeqChains) { + out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq, + chain.chain); + } + if (mSeqChains.empty()) { + out += " <empty>\n"; + } + return out; +} + } // namespace android diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 85a2015e43..8546bbbb43 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -14,20 +14,20 @@ * limitations under the License. */ +#include <type_traits> #define LOG_TAG "InputWindow" #define LOG_NDEBUG 0 +#include <android-base/stringprintf.h> #include <binder/Parcel.h> -#include <input/InputWindow.h> #include <input/InputTransport.h> +#include <input/InputWindow.h> #include <log/log.h> -#include <ui/Rect.h> -#include <ui/Region.h> - namespace android { + // --- InputWindowInfo --- void InputWindowInfo::addTouchableRegion(const Rect& region) { touchableRegion.orSelf(region); @@ -42,22 +42,8 @@ bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { && y >= frameTop && y < frameBottom; } -// TODO(b/155781676): Remove and replace call points with trustedOverlay when that is ready. -bool InputWindowInfo::isTrustedOverlay() const { - return layoutParamsType == TYPE_INPUT_METHOD || layoutParamsType == TYPE_INPUT_METHOD_DIALOG || - layoutParamsType == TYPE_MAGNIFICATION_OVERLAY || layoutParamsType == TYPE_STATUS_BAR || - layoutParamsType == TYPE_NOTIFICATION_SHADE || - layoutParamsType == TYPE_NAVIGATION_BAR || - layoutParamsType == TYPE_NAVIGATION_BAR_PANEL || - layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY || - layoutParamsType == TYPE_DOCK_DIVIDER || - layoutParamsType == TYPE_ACCESSIBILITY_OVERLAY || - layoutParamsType == TYPE_INPUT_CONSUMER || - layoutParamsType == TYPE_TRUSTED_APPLICATION_OVERLAY; -} - bool InputWindowInfo::supportsSplitTouch() const { - return layoutParamsFlags & FLAG_SPLIT_TOUCH; + return flags.test(Flag::SPLIT_TOUCH); } bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { @@ -65,94 +51,158 @@ bool InputWindowInfo::overlaps(const InputWindowInfo* other) const { && frameTop < other->frameBottom && frameBottom > other->frameTop; } -status_t InputWindowInfo::write(Parcel& output) const { +bool InputWindowInfo::operator==(const InputWindowInfo& info) const { + return info.token == token && info.id == id && info.name == name && info.flags == flags && + info.type == type && info.dispatchingTimeout == dispatchingTimeout && + info.frameLeft == frameLeft && info.frameTop == frameTop && + info.frameRight == frameRight && info.frameBottom == frameBottom && + info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor && + info.transform == transform && info.touchableRegion.hasSameRects(touchableRegion) && + info.visible == visible && info.trustedOverlay == trustedOverlay && + info.focusable == focusable && info.touchOcclusionMode == touchOcclusionMode && + info.hasWallpaper == hasWallpaper && info.paused == paused && + info.ownerPid == ownerPid && info.ownerUid == ownerUid && + info.packageName == packageName && info.inputFeatures == inputFeatures && + info.displayId == displayId && info.portalToDisplayId == portalToDisplayId && + info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && + info.applicationInfo == applicationInfo; +} + +status_t InputWindowInfo::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } if (name.empty()) { - output.writeInt32(0); + parcel->writeInt32(0); return OK; } - output.writeInt32(1); - status_t s = output.writeStrongBinder(token); - if (s != OK) return s; - - output.writeInt32(id); - output.writeString8(String8(name.c_str())); - output.writeInt32(layoutParamsFlags); - output.writeInt32(layoutParamsType); - output.writeInt64(dispatchingTimeout); - output.writeInt32(frameLeft); - output.writeInt32(frameTop); - output.writeInt32(frameRight); - output.writeInt32(frameBottom); - output.writeInt32(surfaceInset); - output.writeFloat(globalScaleFactor); - output.writeFloat(windowXScale); - output.writeFloat(windowYScale); - output.writeBool(visible); - output.writeBool(canReceiveKeys); - output.writeBool(hasFocus); - output.writeBool(hasWallpaper); - output.writeBool(paused); - output.writeInt32(ownerPid); - output.writeInt32(ownerUid); - output.writeInt32(inputFeatures); - output.writeInt32(displayId); - output.writeInt32(portalToDisplayId); - applicationInfo.write(output); - output.write(touchableRegion); - output.writeBool(replaceTouchableRegionWithCrop); - output.writeStrongBinder(touchableRegionCropHandle.promote()); - return OK; + parcel->writeInt32(1); + + // clang-format off + status_t status = parcel->writeStrongBinder(token) ?: + parcel->writeInt64(dispatchingTimeout.count()) ?: + parcel->writeInt32(id) ?: + parcel->writeUtf8AsUtf16(name) ?: + parcel->writeInt32(flags.get()) ?: + parcel->writeInt32(static_cast<std::underlying_type_t<InputWindowInfo::Type>>(type)) ?: + parcel->writeInt32(frameLeft) ?: + parcel->writeInt32(frameTop) ?: + parcel->writeInt32(frameRight) ?: + parcel->writeInt32(frameBottom) ?: + parcel->writeInt32(surfaceInset) ?: + parcel->writeFloat(globalScaleFactor) ?: + parcel->writeFloat(alpha) ?: + parcel->writeFloat(transform.dsdx()) ?: + parcel->writeFloat(transform.dtdx()) ?: + parcel->writeFloat(transform.tx()) ?: + parcel->writeFloat(transform.dtdy()) ?: + parcel->writeFloat(transform.dsdy()) ?: + parcel->writeFloat(transform.ty()) ?: + parcel->writeBool(visible) ?: + parcel->writeBool(focusable) ?: + parcel->writeBool(hasWallpaper) ?: + parcel->writeBool(paused) ?: + parcel->writeBool(trustedOverlay) ?: + parcel->writeInt32(static_cast<int32_t>(touchOcclusionMode)) ?: + parcel->writeInt32(ownerPid) ?: + parcel->writeInt32(ownerUid) ?: + parcel->writeUtf8AsUtf16(packageName) ?: + parcel->writeInt32(inputFeatures.get()) ?: + parcel->writeInt32(displayId) ?: + parcel->writeInt32(portalToDisplayId) ?: + applicationInfo.writeToParcel(parcel) ?: + parcel->write(touchableRegion) ?: + parcel->writeBool(replaceTouchableRegionWithCrop) ?: + parcel->writeStrongBinder(touchableRegionCropHandle.promote()); + // clang-format on + return status; } -InputWindowInfo InputWindowInfo::read(const Parcel& from) { - InputWindowInfo ret; +status_t InputWindowInfo::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + if (parcel->readInt32() == 0) { + return OK; + } + + token = parcel->readStrongBinder(); + dispatchingTimeout = static_cast<decltype(dispatchingTimeout)>(parcel->readInt64()); + status_t status = parcel->readInt32(&id) ?: parcel->readUtf8FromUtf16(&name); + if (status != OK) { + return status; + } - if (from.readInt32() == 0) { - return ret; + flags = Flags<Flag>(parcel->readInt32()); + type = static_cast<Type>(parcel->readInt32()); + float dsdx, dtdx, tx, dtdy, dsdy, ty; + int32_t touchOcclusionModeInt; + // clang-format off + status = parcel->readInt32(&frameLeft) ?: + parcel->readInt32(&frameTop) ?: + parcel->readInt32(&frameRight) ?: + parcel->readInt32(&frameBottom) ?: + parcel->readInt32(&surfaceInset) ?: + parcel->readFloat(&globalScaleFactor) ?: + parcel->readFloat(&alpha) ?: + parcel->readFloat(&dsdx) ?: + parcel->readFloat(&dtdx) ?: + parcel->readFloat(&tx) ?: + parcel->readFloat(&dtdy) ?: + parcel->readFloat(&dsdy) ?: + parcel->readFloat(&ty) ?: + parcel->readBool(&visible) ?: + parcel->readBool(&focusable) ?: + parcel->readBool(&hasWallpaper) ?: + parcel->readBool(&paused) ?: + parcel->readBool(&trustedOverlay) ?: + parcel->readInt32(&touchOcclusionModeInt) ?: + parcel->readInt32(&ownerPid) ?: + parcel->readInt32(&ownerUid) ?: + parcel->readUtf8FromUtf16(&packageName); + // clang-format on + + if (status != OK) { + return status; } - ret.token = from.readStrongBinder(); - ret.id = from.readInt32(); - ret.name = from.readString8().c_str(); - ret.layoutParamsFlags = from.readInt32(); - ret.layoutParamsType = from.readInt32(); - ret.dispatchingTimeout = from.readInt64(); - ret.frameLeft = from.readInt32(); - ret.frameTop = from.readInt32(); - ret.frameRight = from.readInt32(); - ret.frameBottom = from.readInt32(); - ret.surfaceInset = from.readInt32(); - ret.globalScaleFactor = from.readFloat(); - ret.windowXScale = from.readFloat(); - ret.windowYScale = from.readFloat(); - ret.visible = from.readBool(); - ret.canReceiveKeys = from.readBool(); - ret.hasFocus = from.readBool(); - ret.hasWallpaper = from.readBool(); - ret.paused = from.readBool(); - ret.ownerPid = from.readInt32(); - ret.ownerUid = from.readInt32(); - ret.inputFeatures = from.readInt32(); - ret.displayId = from.readInt32(); - ret.portalToDisplayId = from.readInt32(); - ret.applicationInfo = InputApplicationInfo::read(from); - from.read(ret.touchableRegion); - ret.replaceTouchableRegionWithCrop = from.readBool(); - ret.touchableRegionCropHandle = from.readStrongBinder(); - - return ret; -} + touchOcclusionMode = static_cast<TouchOcclusionMode>(touchOcclusionModeInt); + + inputFeatures = Flags<Feature>(parcel->readInt32()); + status = parcel->readInt32(&displayId) ?: + parcel->readInt32(&portalToDisplayId) ?: + applicationInfo.readFromParcel(parcel) ?: + parcel->read(touchableRegion) ?: + parcel->readBool(&replaceTouchableRegionWithCrop); + + if (status != OK) { + return status; + } + + touchableRegionCropHandle = parcel->readStrongBinder(); + transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); -InputWindowInfo::InputWindowInfo(const Parcel& from) { - *this = read(from); + return OK; } // --- InputWindowHandle --- -InputWindowHandle::InputWindowHandle() { +InputWindowHandle::InputWindowHandle() {} + +InputWindowHandle::~InputWindowHandle() {} + +InputWindowHandle::InputWindowHandle(const InputWindowHandle& other) : mInfo(other.mInfo) {} + +InputWindowHandle::InputWindowHandle(const InputWindowInfo& other) : mInfo(other) {} + +status_t InputWindowHandle::writeToParcel(android::Parcel* parcel) const { + return mInfo.writeToParcel(parcel); } -InputWindowHandle::~InputWindowHandle() { +status_t InputWindowHandle::readFromParcel(const android::Parcel* parcel) { + return mInfo.readFromParcel(parcel); } void InputWindowHandle::releaseChannel() { @@ -166,5 +216,4 @@ sp<IBinder> InputWindowHandle::getToken() const { void InputWindowHandle::updateFrom(sp<InputWindowHandle> handle) { mInfo = handle->mInfo; } - } // namespace android diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index cb68165433..f5432ad3ee 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -19,14 +19,14 @@ #include <stdlib.h> #include <string.h> -#ifdef __ANDROID__ +#ifdef __linux__ #include <binder/Parcel.h> #endif - #include <android/keycodes.h> +#include <attestation/HmacKeyManager.h> #include <input/InputEventLabels.h> -#include <input/Keyboard.h> #include <input/KeyCharacterMap.h> +#include <input/Keyboard.h> #include <utils/Log.h> #include <utils/Errors.h> @@ -85,15 +85,12 @@ static String8 toString(const char16_t* chars, size_t numChars) { // --- KeyCharacterMap --- -sp<KeyCharacterMap> KeyCharacterMap::sEmpty = new KeyCharacterMap(); +KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {} -KeyCharacterMap::KeyCharacterMap() : - mType(KEYBOARD_TYPE_UNKNOWN) { -} - -KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : - RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode), - mKeysByUsageCode(other.mKeysByUsageCode) { +KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) + : mType(other.mType), + mKeysByScanCode(other.mKeysByScanCode), + mKeysByUsageCode(other.mKeysByUsageCode) { for (size_t i = 0; i < other.mKeys.size(); i++) { mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i))); } @@ -106,102 +103,93 @@ KeyCharacterMap::~KeyCharacterMap() { } } -status_t KeyCharacterMap::load(const std::string& filename, - Format format, sp<KeyCharacterMap>* outMap) { - outMap->clear(); - +base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(const std::string& filename, + Format format) { Tokenizer* tokenizer; status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { - ALOGE("Error %d opening key character map file %s.", status, filename.c_str()); - } else { - status = load(tokenizer, format, outMap); - delete tokenizer; + return Errorf("Error {} opening key character map file {}.", status, filename.c_str()); } - return status; + std::unique_ptr<Tokenizer> t(tokenizer); + auto ret = load(t.get(), format); + if (ret) { + (*ret)->mLoadFileName = filename; + } + return ret; } -status_t KeyCharacterMap::loadContents(const std::string& filename, const char* contents, - Format format, sp<KeyCharacterMap>* outMap) { - outMap->clear(); - +base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::loadContents( + const std::string& filename, const char* contents, Format format) { Tokenizer* tokenizer; status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); if (status) { ALOGE("Error %d opening key character map.", status); - } else { - status = load(tokenizer, format, outMap); - delete tokenizer; + return Errorf("Error {} opening key character map.", status); } - return status; + std::unique_ptr<Tokenizer> t(tokenizer); + auto ret = load(t.get(), format); + if (ret) { + (*ret)->mLoadFileName = filename; + } + return ret; } -status_t KeyCharacterMap::load(Tokenizer* tokenizer, - Format format, sp<KeyCharacterMap>* outMap) { +base::Result<std::shared_ptr<KeyCharacterMap>> KeyCharacterMap::load(Tokenizer* tokenizer, + Format format) { status_t status = OK; - sp<KeyCharacterMap> map = new KeyCharacterMap(); + std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap()); if (!map.get()) { ALOGE("Error allocating key character map."); - status = NO_MEMORY; - } else { + return Errorf("Error allocating key character map."); + } #if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map.get(), tokenizer, format); - status = parser.parse(); + Parser parser(map.get(), tokenizer, format); + status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif - if (!status) { - *outMap = map; - } + if (status == OK) { + return map; } - return status; -} -sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base, - const sp<KeyCharacterMap>& overlay) { - if (overlay == nullptr) { - return base; - } - if (base == nullptr) { - return overlay; - } + return Errorf("Load KeyCharacterMap failed {}.", status); +} - sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get()); - for (size_t i = 0; i < overlay->mKeys.size(); i++) { - int32_t keyCode = overlay->mKeys.keyAt(i); - Key* key = overlay->mKeys.valueAt(i); - ssize_t oldIndex = map->mKeys.indexOfKey(keyCode); +void KeyCharacterMap::combine(const KeyCharacterMap& overlay) { + for (size_t i = 0; i < overlay.mKeys.size(); i++) { + int32_t keyCode = overlay.mKeys.keyAt(i); + Key* key = overlay.mKeys.valueAt(i); + ssize_t oldIndex = mKeys.indexOfKey(keyCode); if (oldIndex >= 0) { - delete map->mKeys.valueAt(oldIndex); - map->mKeys.editValueAt(oldIndex) = new Key(*key); + delete mKeys.valueAt(oldIndex); + mKeys.editValueAt(oldIndex) = new Key(*key); } else { - map->mKeys.add(keyCode, new Key(*key)); + mKeys.add(keyCode, new Key(*key)); } } - for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) { - map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i), - overlay->mKeysByScanCode.valueAt(i)); + for (size_t i = 0; i < overlay.mKeysByScanCode.size(); i++) { + mKeysByScanCode.replaceValueFor(overlay.mKeysByScanCode.keyAt(i), + overlay.mKeysByScanCode.valueAt(i)); } - for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) { - map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i), - overlay->mKeysByUsageCode.valueAt(i)); + for (size_t i = 0; i < overlay.mKeysByUsageCode.size(); i++) { + mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i), + overlay.mKeysByUsageCode.valueAt(i)); } - return map; + mLoadFileName = overlay.mLoadFileName; } -sp<KeyCharacterMap> KeyCharacterMap::empty() { - return sEmpty; +KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const { + return mType; } -int32_t KeyCharacterMap::getKeyboardType() const { - return mType; +const std::string KeyCharacterMap::getLoadFileName() const { + return mLoadFileName; } char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { @@ -599,10 +587,14 @@ void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents, } } -#ifdef __ANDROID__ -sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { - sp<KeyCharacterMap> map = new KeyCharacterMap(); - map->mType = parcel->readInt32(); +#ifdef __linux__ +std::shared_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return nullptr; + } + std::shared_ptr<KeyCharacterMap> map = std::shared_ptr<KeyCharacterMap>(new KeyCharacterMap()); + map->mType = static_cast<KeyCharacterMap::KeyboardType>(parcel->readInt32()); size_t numKeys = parcel->readInt32(); if (parcel->errorCheck()) { return nullptr; @@ -656,7 +648,11 @@ sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) { } void KeyCharacterMap::writeToParcel(Parcel* parcel) const { - parcel->writeInt32(mType); + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return; + } + parcel->writeInt32(static_cast<int32_t>(mType)); size_t numKeys = mKeys.size(); parcel->writeInt32(numKeys); @@ -677,8 +673,7 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { parcel->writeInt32(0); } } -#endif - +#endif // __linux__ // --- KeyCharacterMap::Key --- @@ -782,20 +777,20 @@ status_t KeyCharacterMap::Parser::parse() { return BAD_VALUE; } - if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { + if (mMap->mType == KeyboardType::UNKNOWN) { ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", mTokenizer->getLocation().string()); return BAD_VALUE; } - if (mFormat == FORMAT_BASE) { - if (mMap->mType == KEYBOARD_TYPE_OVERLAY) { + if (mFormat == Format::BASE) { + if (mMap->mType == KeyboardType::OVERLAY) { ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", mTokenizer->getLocation().string()); return BAD_VALUE; } - } else if (mFormat == FORMAT_OVERLAY) { - if (mMap->mType != KEYBOARD_TYPE_OVERLAY) { + } else if (mFormat == Format::OVERLAY) { + if (mMap->mType != KeyboardType::OVERLAY) { ALOGE("%s: Overlay keyboard layout missing required keyboard " "'type OVERLAY' declaration.", mTokenizer->getLocation().string()); @@ -807,7 +802,7 @@ status_t KeyCharacterMap::Parser::parse() { } status_t KeyCharacterMap::Parser::parseType() { - if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { + if (mMap->mType != KeyboardType::UNKNOWN) { ALOGE("%s: Duplicate keyboard 'type' declaration.", mTokenizer->getLocation().string()); return BAD_VALUE; @@ -816,20 +811,20 @@ status_t KeyCharacterMap::Parser::parseType() { KeyboardType type; String8 typeToken = mTokenizer->nextToken(WHITESPACE); if (typeToken == "NUMERIC") { - type = KEYBOARD_TYPE_NUMERIC; + type = KeyboardType::NUMERIC; } else if (typeToken == "PREDICTIVE") { - type = KEYBOARD_TYPE_PREDICTIVE; + type = KeyboardType::PREDICTIVE; } else if (typeToken == "ALPHA") { - type = KEYBOARD_TYPE_ALPHA; + type = KeyboardType::ALPHA; } else if (typeToken == "FULL") { - type = KEYBOARD_TYPE_FULL; + type = KeyboardType::FULL; } else if (typeToken == "SPECIAL_FUNCTION") { ALOGW("The SPECIAL_FUNCTION type is now declared in the device's IDC file, please set " "the property 'keyboard.specialFunction' to '1' there instead."); // TODO: return BAD_VALUE here in Q - type = KEYBOARD_TYPE_SPECIAL_FUNCTION; + type = KeyboardType::SPECIAL_FUNCTION; } else if (typeToken == "OVERLAY") { - type = KEYBOARD_TYPE_OVERLAY; + type = KeyboardType::OVERLAY; } else { ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), typeToken.string()); @@ -880,7 +875,7 @@ status_t KeyCharacterMap::Parser::parseMapKey() { mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); @@ -897,7 +892,7 @@ status_t KeyCharacterMap::Parser::parseMapKey() { status_t KeyCharacterMap::Parser::parseKey() { String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); @@ -1017,7 +1012,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } else if (token == "fallback") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(token.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string()); if (!keyCode) { ALOGE("%s: Invalid key code label for fallback behavior, got '%s'.", mTokenizer->getLocation().string(), @@ -1034,7 +1029,7 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } else if (token == "replace") { mTokenizer->skipDelimiters(WHITESPACE); token = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(token.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(token.string()); if (!keyCode) { ALOGE("%s: Invalid key code label for replace, got '%s'.", mTokenizer->getLocation().string(), diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index efca68d171..16ce48aca1 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -49,37 +49,60 @@ KeyLayoutMap::KeyLayoutMap() { KeyLayoutMap::~KeyLayoutMap() { } -status_t KeyLayoutMap::load(const std::string& filename, sp<KeyLayoutMap>* outMap) { - outMap->clear(); +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename, + const char* contents) { + Tokenizer* tokenizer; + status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); + if (status) { + ALOGE("Error %d opening key layout map.", status); + return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); + } + std::unique_ptr<Tokenizer> t(tokenizer); + auto ret = load(t.get()); + if (ret) { + (*ret)->mLoadFileName = filename; + } + return ret; +} +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) { Tokenizer* tokenizer; status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); + return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); + } + std::unique_ptr<Tokenizer> t(tokenizer); + auto ret = load(t.get()); + if (ret) { + (*ret)->mLoadFileName = filename; + } + return ret; +} + +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(Tokenizer* tokenizer) { + std::shared_ptr<KeyLayoutMap> map = std::shared_ptr<KeyLayoutMap>(new KeyLayoutMap()); + status_t status = OK; + if (!map.get()) { + ALOGE("Error allocating key layout map."); + return Errorf("Error allocating key layout map."); } else { - sp<KeyLayoutMap> map = new KeyLayoutMap(); - if (!map.get()) { - ALOGE("Error allocating key layout map."); - status = NO_MEMORY; - } else { #if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map.get(), tokenizer); - status = parser.parse(); + Parser parser(map.get(), tokenizer); + status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); #endif - if (!status) { - *outMap = map; - } + if (!status) { + return std::move(map); } - delete tokenizer; } - return status; + return Errorf("Load KeyLayoutMap failed {}.", status); } status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, @@ -264,7 +287,7 @@ status_t KeyLayoutMap::Parser::parseKey() { mTokenizer->skipDelimiters(WHITESPACE); String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + int32_t keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.string()); if (!keyCode) { ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), keyCodeToken.string()); @@ -277,7 +300,7 @@ status_t KeyLayoutMap::Parser::parseKey() { if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; String8 flagToken = mTokenizer->nextToken(WHITESPACE); - uint32_t flag = getKeyFlagByLabel(flagToken.string()); + uint32_t flag = InputEventLookup::getKeyFlagByLabel(flagToken.string()); if (!flag) { ALOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), flagToken.string()); @@ -326,7 +349,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 axisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.axis = getAxisByLabel(axisToken.string()); + axisInfo.axis = InputEventLookup::getAxisByLabel(axisToken.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected inverted axis label, got '%s'.", mTokenizer->getLocation().string(), axisToken.string()); @@ -346,7 +369,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.axis = getAxisByLabel(lowAxisToken.string()); + axisInfo.axis = InputEventLookup::getAxisByLabel(lowAxisToken.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected low axis label, got '%s'.", mTokenizer->getLocation().string(), lowAxisToken.string()); @@ -355,14 +378,14 @@ status_t KeyLayoutMap::Parser::parseAxis() { mTokenizer->skipDelimiters(WHITESPACE); String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); - axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); + axisInfo.highAxis = InputEventLookup::getAxisByLabel(highAxisToken.string()); if (axisInfo.highAxis < 0) { ALOGE("%s: Expected high axis label, got '%s'.", mTokenizer->getLocation().string(), highAxisToken.string()); return BAD_VALUE; } } else { - axisInfo.axis = getAxisByLabel(token.string()); + axisInfo.axis = InputEventLookup::getAxisByLabel(token.string()); if (axisInfo.axis < 0) { ALOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", mTokenizer->getLocation().string(), token.string()); @@ -428,7 +451,7 @@ status_t KeyLayoutMap::Parser::parseLed() { mTokenizer->skipDelimiters(WHITESPACE); String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE); - int32_t ledCode = getLedByLabel(ledCodeToken.string()); + int32_t ledCode = InputEventLookup::getLedByLabel(ledCodeToken.string()); if (ledCode < 0) { ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(), ledCodeToken.string()); diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index 56900c129e..14dc9e59fb 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -105,35 +105,34 @@ bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name) { - std::string path(getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); + std::string path(getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_LAYOUT)); if (path.empty()) { return NAME_NOT_FOUND; } - status_t status = KeyLayoutMap::load(path, &keyLayoutMap); - if (status) { - return status; + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path); + if (!ret) { + return ret.error().code(); } - + keyLayoutMap = *ret; keyLayoutFile = path; return OK; } status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name) { - std::string path = getPath(deviceIdentifier, name, - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP); + std::string path = + getPath(deviceIdentifier, name, InputDeviceConfigurationFileType::KEY_CHARACTER_MAP); if (path.empty()) { return NAME_NOT_FOUND; } - status_t status = KeyCharacterMap::load(path, - KeyCharacterMap::FORMAT_BASE, &keyCharacterMap); - if (status) { - return status; + base::Result<std::shared_ptr<KeyCharacterMap>> ret = + KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE); + if (!ret) { + return ret.error().code(); } - + keyCharacterMap = *ret; keyCharacterMapFile = path; return OK; } @@ -160,9 +159,9 @@ bool isKeyboardSpecialFunction(const PropertyMap* config) { bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { // TODO: remove the third OR statement (SPECIAL_FUNCTION) in Q - if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) - || keyMap->keyCharacterMap->getKeyboardType() - == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) { + if (!keyMap->haveKeyCharacterMap() || isKeyboardSpecialFunction(deviceConfiguration) || + keyMap->keyCharacterMap->getKeyboardType() == + KeyCharacterMap::KeyboardType::SPECIAL_FUNCTION) { return false; } diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp index 4833eb9c05..a842166761 100644 --- a/libs/input/PropertyMap.cpp +++ b/libs/input/PropertyMap.cpp @@ -107,23 +107,22 @@ void PropertyMap::addAll(const PropertyMap* map) { } } -status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { - *outMap = nullptr; +android::base::Result<std::unique_ptr<PropertyMap>> PropertyMap::load(const char* filename) { + std::unique_ptr<PropertyMap> outMap = std::make_unique<PropertyMap>(); + if (outMap == nullptr) { + return android::base::Error(NO_MEMORY) << "Error allocating property map."; + } - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); + Tokenizer* rawTokenizer; + status_t status = Tokenizer::open(String8(filename), &rawTokenizer); + std::unique_ptr<Tokenizer> tokenizer(rawTokenizer); if (status) { - ALOGE("Error %d opening property file %s.", status, filename.string()); + ALOGE("Error %d opening property file %s.", status, filename); } else { - PropertyMap* map = new PropertyMap(); - if (!map) { - ALOGE("Error allocating property map."); - status = NO_MEMORY; - } else { #if DEBUG_PARSER_PERFORMANCE nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map, tokenizer); + Parser parser(outMap.get(), tokenizer.get()); status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; @@ -132,14 +131,10 @@ status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { elapsedTime / 1000000.0); #endif if (status) { - delete map; - } else { - *outMap = map; + return android::base::Error(BAD_VALUE) << "Could not parse " << filename; } - } - delete tokenizer; } - return status; + return std::move(outMap); } // --- PropertyMap::Parser --- diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp index 23ead0e469..afb97a1d47 100755 --- a/libs/input/PropertyMap_fuzz.cpp +++ b/libs/input/PropertyMap_fuzz.cpp @@ -42,7 +42,7 @@ static const std::vector<std::function<void(FuzzedDataProvider*, android::Proper android::String8 out; propertyMap.tryGetProperty(key, out); }, - [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { + [](FuzzedDataProvider* dataProvider, android::PropertyMap /*unused*/) -> void { TemporaryFile tf; // Generate file contents std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE); @@ -52,8 +52,7 @@ static const std::vector<std::function<void(FuzzedDataProvider*, android::Proper const char* bytes = contents.c_str(); android::base::WriteStringToFd(bytes, tf.fd); } - android::PropertyMap* mapPtr = &propertyMap; - android::PropertyMap::load(android::String8(tf.path), &mapPtr); + android::PropertyMap::load(tf.path); }, [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); @@ -65,12 +64,12 @@ static const std::vector<std::function<void(FuzzedDataProvider*, android::Proper }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { FuzzedDataProvider dataProvider(data, size); - android::PropertyMap proprtyMap = android::PropertyMap(); + android::PropertyMap propertyMap = android::PropertyMap(); int opsRun = 0; while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) { uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1); - operations[op](&dataProvider, proprtyMap); + operations[op](&dataProvider, propertyMap); } return 0; } diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING new file mode 100644 index 0000000000..9626d8dac7 --- /dev/null +++ b/libs/input/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/native/services/inputflinger" + } + ] +} diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp index bcf55b0ea3..2c04d420f4 100644 --- a/libs/input/VelocityControl.cpp +++ b/libs/input/VelocityControl.cpp @@ -66,7 +66,7 @@ void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { if (deltaY) { mRawPosition.y += *deltaY; } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition); + mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition}); float vx, vy; float scale = mParameters.scale; diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index c6cc4fc374..a44f0b7fe0 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -104,107 +104,73 @@ static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool r // --- VelocityTracker --- -// The default velocity tracker strategy. -// Although other strategies are available for testing and comparison purposes, -// this is the strategy that applications will actually use. Be very careful -// when adjusting the default strategy because it can dramatically affect -// (often in a bad way) the user experience. -const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2"; - -VelocityTracker::VelocityTracker(const char* strategy) : - mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { - char value[PROPERTY_VALUE_MAX]; - - // Allow the default strategy to be overridden using a system property for debugging. - if (!strategy) { - int length = property_get("persist.input.velocitytracker.strategy", value, nullptr); - if (length > 0) { - strategy = value; - } else { - strategy = DEFAULT_STRATEGY; - } - } - +VelocityTracker::VelocityTracker(const Strategy strategy) + : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { // Configure the strategy. if (!configureStrategy(strategy)) { - ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy); - if (!configureStrategy(DEFAULT_STRATEGY)) { - LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!", - strategy); + ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy); + if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) { + LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32 + "'!", + strategy); } } } VelocityTracker::~VelocityTracker() { - delete mStrategy; } -bool VelocityTracker::configureStrategy(const char* strategy) { - mStrategy = createStrategy(strategy); +bool VelocityTracker::configureStrategy(Strategy strategy) { + if (strategy == VelocityTracker::Strategy::DEFAULT) { + mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY); + } else { + mStrategy = createStrategy(strategy); + } return mStrategy != nullptr; } -VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { - if (!strcmp("impulse", strategy)) { - // Physical model of pushing an object. Quality: VERY GOOD. - // Works with duplicate coordinates, unclean finger liftoff. - return new ImpulseVelocityTrackerStrategy(); - } - if (!strcmp("lsq1", strategy)) { - // 1st order least squares. Quality: POOR. - // Frequently underfits the touch data especially when the finger accelerates - // or changes direction. Often underestimates velocity. The direction - // is overly influenced by historical touch points. - return new LeastSquaresVelocityTrackerStrategy(1); - } - if (!strcmp("lsq2", strategy)) { - // 2nd order least squares. Quality: VERY GOOD. - // Pretty much ideal, but can be confused by certain kinds of touch data, - // particularly if the panel has a tendency to generate delayed, - // duplicate or jittery touch coordinates when the finger is released. - return new LeastSquaresVelocityTrackerStrategy(2); - } - if (!strcmp("lsq3", strategy)) { - // 3rd order least squares. Quality: UNUSABLE. - // Frequently overfits the touch data yielding wildly divergent estimates - // of the velocity when the finger is released. - return new LeastSquaresVelocityTrackerStrategy(3); - } - if (!strcmp("wlsq2-delta", strategy)) { - // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA); - } - if (!strcmp("wlsq2-central", strategy)) { - // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL); - } - if (!strcmp("wlsq2-recent", strategy)) { - // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL - return new LeastSquaresVelocityTrackerStrategy(2, - LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT); - } - if (!strcmp("int1", strategy)) { - // 1st order integrating filter. Quality: GOOD. - // Not as good as 'lsq2' because it cannot estimate acceleration but it is - // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate - // the velocity of a fling but this strategy tends to respond to changes in - // direction more quickly and accurately. - return new IntegratingVelocityTrackerStrategy(1); - } - if (!strcmp("int2", strategy)) { - // 2nd order integrating filter. Quality: EXPERIMENTAL. - // For comparison purposes only. Unlike 'int1' this strategy can compensate - // for acceleration but it typically overestimates the effect. - return new IntegratingVelocityTrackerStrategy(2); - } - if (!strcmp("legacy", strategy)) { - // Legacy velocity tracker algorithm. Quality: POOR. - // For comparison purposes only. This algorithm is strongly influenced by - // old data points, consistently underestimates velocity and takes a very long - // time to adjust to changes in direction. - return new LegacyVelocityTrackerStrategy(); +std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy( + VelocityTracker::Strategy strategy) { + switch (strategy) { + case VelocityTracker::Strategy::IMPULSE: + return std::make_unique<ImpulseVelocityTrackerStrategy>(); + + case VelocityTracker::Strategy::LSQ1: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1); + + case VelocityTracker::Strategy::LSQ2: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2); + + case VelocityTracker::Strategy::LSQ3: + return std::make_unique<LeastSquaresVelocityTrackerStrategy>(3); + + case VelocityTracker::Strategy::WLSQ2_DELTA: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_DELTA); + case VelocityTracker::Strategy::WLSQ2_CENTRAL: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_CENTRAL); + case VelocityTracker::Strategy::WLSQ2_RECENT: + return std::make_unique< + LeastSquaresVelocityTrackerStrategy>(2, + LeastSquaresVelocityTrackerStrategy:: + WEIGHTING_RECENT); + + case VelocityTracker::Strategy::INT1: + return std::make_unique<IntegratingVelocityTrackerStrategy>(1); + + case VelocityTracker::Strategy::INT2: + return std::make_unique<IntegratingVelocityTrackerStrategy>(2); + + case VelocityTracker::Strategy::LEGACY: + return std::make_unique<LegacyVelocityTrackerStrategy>(); + + default: + break; } return nullptr; } @@ -227,7 +193,11 @@ void VelocityTracker::clearPointers(BitSet32 idBits) { mStrategy->clearPointers(idBits); } -void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) { +void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { + LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(), + "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu", + idBits.count(), positions.size()); while (idBits.count() > MAX_POINTERS) { idBits.clearLastMarkedBit(); } @@ -319,12 +289,12 @@ void VelocityTracker::addMovement(const MotionEvent* event) { pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); } - nsecs_t eventTime; - Position positions[pointerCount]; + std::vector<Position> positions; + positions.resize(pointerCount); size_t historySize = event->getHistorySize(); - for (size_t h = 0; h < historySize; h++) { - eventTime = event->getHistoricalEventTime(h); + for (size_t h = 0; h <= historySize; h++) { + nsecs_t eventTime = event->getHistoricalEventTime(h); for (size_t i = 0; i < pointerCount; i++) { uint32_t index = pointerIndex[i]; positions[index].x = event->getHistoricalX(i, h); @@ -332,14 +302,6 @@ void VelocityTracker::addMovement(const MotionEvent* event) { } addMovement(eventTime, idBits, positions); } - - eventTime = event->getEventTime(); - for (size_t i = 0; i < pointerCount; i++) { - uint32_t index = pointerIndex[i]; - positions[index].x = event->getX(i); - positions[index].y = event->getY(i); - } - addMovement(eventTime, idBits, positions); } bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { @@ -380,8 +342,9 @@ void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { +void LeastSquaresVelocityTrackerStrategy::addMovement( + nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include @@ -453,13 +416,15 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet3 * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares * http://en.wikipedia.org/wiki/Gram-Schmidt */ -static bool solveLeastSquares(const float* x, const float* y, - const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) { +static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y, + const std::vector<float>& w, uint32_t n, float* outB, float* outDet) { + const size_t m = x.size(); #if DEBUG_STRATEGY ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), vectorToString(x, m).c_str(), vectorToString(y, m).c_str(), vectorToString(w, m).c_str()); #endif + LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes"); // Expand the X vector to a matrix A, pre-multiplied by the weights. float a[n][m]; // column-major order @@ -576,7 +541,9 @@ static bool solveLeastSquares(const float* x, const float* y, * the default implementation */ static std::optional<std::array<float, 3>> solveUnweightedLeastSquaresDeg2( - const float* x, const float* y, size_t count) { + const std::vector<float>& x, const std::vector<float>& y) { + const size_t count = x.size(); + LOG_ALWAYS_FATAL_IF(count != y.size(), "Mismatching array sizes"); // Solving y = a*x^2 + b*x + c float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0; @@ -628,11 +595,11 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, outEstimator->clear(); // Iterate over movement samples in reverse time order and collect samples. - float x[HISTORY_SIZE]; - float y[HISTORY_SIZE]; - float w[HISTORY_SIZE]; - float time[HISTORY_SIZE]; - uint32_t m = 0; + std::vector<float> x; + std::vector<float> y; + std::vector<float> w; + std::vector<float> time; + uint32_t index = mIndex; const Movement& newestMovement = mMovements[mIndex]; do { @@ -647,13 +614,14 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, } const VelocityTracker::Position& position = movement.getPosition(id); - x[m] = position.x; - y[m] = position.y; - w[m] = chooseWeight(index); - time[m] = -age * 0.000000001f; + x.push_back(position.x); + y.push_back(position.y); + w.push_back(chooseWeight(index)); + time.push_back(-age * 0.000000001f); index = (index == 0 ? HISTORY_SIZE : index) - 1; - } while (++m < HISTORY_SIZE); + } while (x.size() < HISTORY_SIZE); + const size_t m = x.size(); if (m == 0) { return false; // no data } @@ -666,8 +634,8 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, if (degree == 2 && mWeighting == WEIGHTING_NONE) { // Optimize unweighted, quadratic polynomial fit - std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x, m); - std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y, m); + std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x); + std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y); if (xCoeff && yCoeff) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = 2; @@ -682,8 +650,8 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, // General case for an Nth degree polynomial fit float xdet, ydet; uint32_t n = degree + 1; - if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) - && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) { + if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) && + solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) { outEstimator->time = newestMovement.eventTime; outEstimator->degree = degree; outEstimator->confidence = xdet * ydet; @@ -792,8 +760,9 @@ void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mPointerIdBits.value &= ~idBits.value; } -void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { +void IntegratingVelocityTrackerStrategy::addMovement( + nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { uint32_t index = 0; for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { uint32_t id = iterIdBits.clearFirstMarkedBit(); @@ -910,8 +879,9 @@ void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { +void LegacyVelocityTrackerStrategy::addMovement( + nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { if (++mIndex == HISTORY_SIZE) { mIndex = 0; } @@ -1024,8 +994,9 @@ void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { mMovements[mIndex].idBits = remainingIdBits; } -void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, - const VelocityTracker::Position* positions) { +void ImpulseVelocityTrackerStrategy::addMovement( + nsecs_t eventTime, BitSet32 idBits, + const std::vector<VelocityTracker::Position>& positions) { if (mMovements[mIndex].eventTime != eventTime) { // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include diff --git a/libs/input/android/FocusRequest.aidl b/libs/input/android/FocusRequest.aidl new file mode 100644 index 0000000000..303dd1c349 --- /dev/null +++ b/libs/input/android/FocusRequest.aidl @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + +/** @hide */ +parcelable FocusRequest { + /** + * Input channel token used to identify the window that should gain focus. + */ + IBinder token; + /** + * The token that the caller expects currently to be focused. If the + * specified token does not match the currently focused window, this request will be dropped. + * If the specified focused token matches the currently focused window, the call will succeed. + * Set this to "null" if this call should succeed no matter what the currently focused token + * is. + */ + @nullable IBinder focusedToken; + /** + * SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) when requesting the focus + * change. This determines which request gets precedence if there is a focus change request + * from another source such as pointer down. + */ + long timestamp; + /** + * Display id associated with this request. + */ + int displayId; +} diff --git a/libs/input/android/InputApplicationInfo.aidl b/libs/input/android/InputApplicationInfo.aidl new file mode 100644 index 0000000000..933603916d --- /dev/null +++ b/libs/input/android/InputApplicationInfo.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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; + +parcelable InputApplicationInfo { + @nullable IBinder token; + @utf8InCpp String name; + long dispatchingTimeoutMillis; +} diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/InputChannel.aidl new file mode 100644 index 0000000000..c2d1112dd3 --- /dev/null +++ b/libs/input/android/InputChannel.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/view/InputChannel.aidl +** +** Copyright 2020, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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; + +parcelable InputChannel cpp_header "input/InputTransport.h"; diff --git a/libs/input/android/InputWindowInfo.aidl b/libs/input/android/InputWindowInfo.aidl new file mode 100644 index 0000000000..eeaf400227 --- /dev/null +++ b/libs/input/android/InputWindowInfo.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/view/InputChannel.aidl +** +** Copyright 2020, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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; + +parcelable InputWindowInfo cpp_header "input/InputWindow.h"; diff --git a/libs/gui/GuiConfig.cpp b/libs/input/android/os/BlockUntrustedTouchesMode.aidl index 3ec20ee0c0..9504e993f8 100644 --- a/libs/gui/GuiConfig.cpp +++ b/libs/input/android/os/BlockUntrustedTouchesMode.aidl @@ -1,11 +1,11 @@ -/* - * Copyright (C) 2012 The Android Open Source Project +/** + * Copyright (c) 2020, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -14,18 +14,22 @@ * limitations under the License. */ -#include <gui/GuiConfig.h> +package android.os; -namespace android { -void appendGuiConfigString(std::string& configStr) { - static const char* config = - " [libgui" -#ifdef DONT_USE_FENCE_SYNC - " DONT_USE_FENCE_SYNC" -#endif - "]"; - configStr.append(config); -} +/** + * Block untrusted touches feature mode. + * + * @hide + */ +@Backing(type="int") +enum BlockUntrustedTouchesMode { + /** Feature is off. */ + DISABLED, + + /** Untrusted touches are flagged but not blocked. */ + PERMISSIVE, -}; // namespace android + /** Untrusted touches are blocked. */ + BLOCK +} diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl new file mode 100644 index 0000000000..82c220fcc2 --- /dev/null +++ b/libs/input/android/os/IInputConstants.aidl @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + + +/** @hide */ +interface IInputConstants +{ + const int DEFAULT_DISPATCHING_TIMEOUT_MILLIS = 5000; // 5 seconds +} diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl new file mode 100644 index 0000000000..1771d192af --- /dev/null +++ b/libs/input/android/os/IInputFlinger.aidl @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.FocusRequest; +import android.InputChannel; +import android.InputWindowInfo; +import android.os.ISetInputWindowsListener; + +/** @hide */ +interface IInputFlinger +{ + // SurfaceFlinger is the caller of this method, it uses the listener callback to ensure the + // ordering when needed. + // SurfaceFlinger calls this only every VSync, so overflow of binder's oneway buffer + // shouldn't be a concern. + oneway void setInputWindows(in InputWindowInfo[] inputHandles, + in @nullable ISetInputWindowsListener setInputWindowsListener); + InputChannel createInputChannel(in @utf8InCpp String name); + void removeInputChannel(in IBinder connectionToken); + /** + * Sets focus to the window identified by the token. This must be called + * after updating any input window handles. + */ + oneway void setFocusedWindow(in FocusRequest request); +} diff --git a/libs/input/android/os/ISetInputWindowsListener.aidl b/libs/input/android/os/ISetInputWindowsListener.aidl new file mode 100644 index 0000000000..bb58fb671b --- /dev/null +++ b/libs/input/android/os/ISetInputWindowsListener.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** @hide */ +oneway interface ISetInputWindowsListener +{ + void onSetInputWindowsFinished(); +} diff --git a/libs/input/android/os/InputEventInjectionResult.aidl b/libs/input/android/os/InputEventInjectionResult.aidl new file mode 100644 index 0000000000..34f10ec00e --- /dev/null +++ b/libs/input/android/os/InputEventInjectionResult.aidl @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * Constants used to report the outcome of input event injection. + * + * @hide + */ +@Backing(type="int") +enum InputEventInjectionResult { + /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ + PENDING = -1, + + /* Injection succeeded. */ + SUCCEEDED = 0, + + /* Injection failed because the injector did not have permission to inject + * into the application with input focus. */ + PERMISSION_DENIED = 1, + + /* Injection failed because there were no available input targets. */ + FAILED = 2, + + /* Injection failed due to a timeout. */ + TIMED_OUT = 3, +} diff --git a/libs/input/android/os/InputEventInjectionSync.aidl b/libs/input/android/os/InputEventInjectionSync.aidl new file mode 100644 index 0000000000..95d24cb443 --- /dev/null +++ b/libs/input/android/os/InputEventInjectionSync.aidl @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * Constants used to specify the input event injection synchronization mode. + * + * @hide + */ +@Backing(type="int") +enum InputEventInjectionSync { + /* Injection is asynchronous and is assumed always to be successful. */ + NONE = 0, + + /* Waits for previous events to be dispatched so that the input dispatcher can determine + * whether input event injection willbe permitted based on the current input focus. + * Does not wait for the input event to finish processing. */ + WAIT_FOR_RESULT = 1, + + /* Waits for the input event to be completely processed. */ + WAIT_FOR_FINISHED = 2, +} diff --git a/libs/input/android/os/TouchOcclusionMode.aidl b/libs/input/android/os/TouchOcclusionMode.aidl new file mode 100644 index 0000000000..106f159a50 --- /dev/null +++ b/libs/input/android/os/TouchOcclusionMode.aidl @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + + +/** + * Touch occlusion modes: These modes represent how windows are taken into + * consideration in order to decide whether to block obscured touches or + * not. + * + * @hide + */ +@Backing(type="int") +enum TouchOcclusionMode { + /** + * Touches that pass through this window will be blocked if they are + * consumed by a different UID and this window is not trusted. + */ + BLOCK_UNTRUSTED, + + /** + * The window's opacity will be taken into consideration for touch + * occlusion rules if the touch passes through it and the window is not + * trusted. + */ + USE_OPACITY, + + /** + * The window won't count for touch occlusion rules if the touch passes + * through it. + */ + ALLOW +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 3b57146461..b23aaded78 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -2,6 +2,8 @@ cc_test { name: "libinput_tests", srcs: [ + "NamedEnum_test.cpp", + "Flags_test.cpp", "IdGenerator_test.cpp", "InputChannel_test.cpp", "InputDevice_test.cpp", @@ -18,14 +20,18 @@ cc_test { "-Wextra", "-Werror", ], - shared_libs: [ + static_libs: [ "libinput", - "libcutils", - "libutils", + ], + shared_libs: [ + "libbase", "libbinder", + "libcutils", + "liblog", "libui", - "libbase", - ] + "libutils", + ], + test_suites: ["device-tests"], } // NOTE: This is a compile time test, and does not need to be diff --git a/libs/input/tests/Flags_test.cpp b/libs/input/tests/Flags_test.cpp new file mode 100644 index 0000000000..0dbb4cfe32 --- /dev/null +++ b/libs/input/tests/Flags_test.cpp @@ -0,0 +1,222 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <input/Flags.h> + +#include <type_traits> + +namespace android::test { + +using namespace android::flag_operators; + +enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 }; + +TEST(Flags, Test) { + Flags<TestFlags> flags = TestFlags::ONE; + ASSERT_TRUE(flags.test(TestFlags::ONE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); + ASSERT_FALSE(flags.test(TestFlags::THREE)); +} + +TEST(Flags, Any) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_TRUE(flags.any(TestFlags::ONE)); + ASSERT_TRUE(flags.any(TestFlags::TWO)); + ASSERT_FALSE(flags.any(TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO)); + ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, All) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_TRUE(flags.all(TestFlags::ONE)); + ASSERT_TRUE(flags.all(TestFlags::TWO)); + ASSERT_FALSE(flags.all(TestFlags::THREE)); + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO)); + ASSERT_FALSE(flags.all(TestFlags::TWO | TestFlags::THREE)); + ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, DefaultConstructor_hasNoFlagsSet) { + Flags<TestFlags> flags; + ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, NotOperator_onEmptyFlagsSetsAllFlags) { + Flags<TestFlags> flags; + flags = ~flags; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, NotOperator_onNonEmptyFlagsInvertsFlags) { + Flags<TestFlags> flags = TestFlags::TWO; + flags = ~flags; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); +} + +TEST(Flags, OrOperator_withNewFlag) { + Flags<TestFlags> flags = TestFlags::ONE; + Flags<TestFlags> flags2 = flags | TestFlags::TWO; + ASSERT_FALSE(flags2.test(TestFlags::THREE)); + ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, OrOperator_withExistingFlag) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + Flags<TestFlags> flags2 = flags | TestFlags::THREE; + ASSERT_FALSE(flags2.test(TestFlags::TWO)); + ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::THREE)); +} + +TEST(Flags, OrEqualsOperator_withNewFlag) { + Flags<TestFlags> flags; + flags |= TestFlags::THREE; + ASSERT_TRUE(flags.test(TestFlags::THREE)); + ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, OrEqualsOperator_withExistingFlag) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + flags |= TestFlags::THREE; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withOneSetFlag) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + Flags<TestFlags> andFlags = flags & TestFlags::THREE; + ASSERT_TRUE(andFlags.test(TestFlags::THREE)); + ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withMultipleSetFlags) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + Flags<TestFlags> andFlags = flags & (TestFlags::ONE | TestFlags::THREE); + ASSERT_TRUE(andFlags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(andFlags.test(TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withNoSetFlags) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::THREE; + Flags<TestFlags> andFlags = flags & TestFlags::TWO; + ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, Equality) { + Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO; + Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::TWO; + ASSERT_EQ(flags1, flags2); +} + +TEST(Flags, Inequality) { + Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO; + Flags<TestFlags> flags2 = TestFlags::ONE | TestFlags::THREE; + ASSERT_NE(flags1, flags2); +} + +TEST(Flags, EqualsOperator) { + Flags<TestFlags> flags; + flags = TestFlags::ONE; + ASSERT_TRUE(flags.test(TestFlags::ONE)); + ASSERT_FALSE(flags.any(TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, EqualsOperator_DontShareState) { + Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO; + Flags<TestFlags> flags2 = flags1; + ASSERT_EQ(flags1, flags2); + + flags1 &= TestFlags::TWO; + ASSERT_NE(flags1, flags2); +} + +TEST(Flags, String_NoFlags) { + Flags<TestFlags> flags; + ASSERT_EQ(flags.string(), "0x0"); +} + +TEST(Flags, String_KnownValues) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_EQ(flags.string(), "ONE | TWO"); +} + +TEST(Flags, String_UnknownValues) { + auto flags = Flags<TestFlags>(0b1011); + ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008"); +} + +TEST(FlagsIterator, IteratesOverAllFlags) { + Flags<TestFlags> flags1 = TestFlags::ONE | TestFlags::TWO; + Flags<TestFlags> flags2; + for (TestFlags f : flags1) { + flags2 |= f; + } + ASSERT_EQ(flags2, flags1); +} + +TEST(FlagsIterator, IteratesInExpectedOrder) { + const std::vector<TestFlags> flagOrder = {TestFlags::ONE, TestFlags::TWO}; + Flags<TestFlags> flags; + for (TestFlags f : flagOrder) { + flags |= f; + } + + size_t idx = 0; + auto iter = flags.begin(); + while (iter != flags.end() && idx < flagOrder.size()) { + // Make sure the order is what we expect + ASSERT_EQ(*iter, flagOrder[idx]); + iter++; + idx++; + } + ASSERT_EQ(iter, flags.end()); +} +TEST(FlagsIterator, PostFixIncrement) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + auto iter = flags.begin(); + ASSERT_EQ(*(iter++), TestFlags::ONE); + ASSERT_EQ(*iter, TestFlags::TWO); + ASSERT_EQ(*(iter++), TestFlags::TWO); + ASSERT_EQ(iter, flags.end()); +} + +TEST(FlagsIterator, PreFixIncrement) { + Flags<TestFlags> flags = TestFlags::ONE | TestFlags::TWO; + auto iter = flags.begin(); + ASSERT_EQ(*++iter, TestFlags::TWO); + ASSERT_EQ(++iter, flags.end()); +} + +TEST(FlagNames, RuntimeFlagName) { + TestFlags f = TestFlags::ONE; + ASSERT_EQ(flag_name(f), "ONE"); +} + +TEST(FlagNames, RuntimeUnknownFlagName) { + TestFlags f = static_cast<TestFlags>(0x8); + ASSERT_EQ(flag_name(f), std::nullopt); +} + +TEST(FlagNames, CompileTimeFlagName) { + static_assert(flag_name<TestFlags::TWO>() == "TWO"); +} + +} // namespace android::test
\ No newline at end of file diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index ada275d014..0661261003 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -23,6 +23,7 @@ #include <errno.h> #include <binder/Binder.h> +#include <binder/Parcel.h> #include <gtest/gtest.h> #include <input/InputTransport.h> #include <utils/StopWatch.h> @@ -32,9 +33,6 @@ namespace android { class InputChannelTest : public testing::Test { -protected: - virtual void SetUp() { } - virtual void TearDown() { } }; @@ -46,7 +44,7 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor android::base::unique_fd sendFd(pipe.sendFd); - sp<InputChannel> inputChannel = + std::unique_ptr<InputChannel> inputChannel = InputChannel::create("channel name", std::move(sendFd), new BBinder()); EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; @@ -61,14 +59,14 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor TEST_F(InputChannelTest, SetAndGetToken) { Pipe pipe; sp<IBinder> token = new BBinder(); - sp<InputChannel> channel = + std::unique_ptr<InputChannel> channel = InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token); EXPECT_EQ(token, channel->getConnectionToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); @@ -102,7 +100,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { InputMessage clientReply; memset(&clientReply, 0, sizeof(InputMessage)); clientReply.header.type = InputMessage::Type::FINISHED; - clientReply.body.finished.seq = 0x11223344; + clientReply.header.seq = 0x11223344; clientReply.body.finished.handled = true; EXPECT_EQ(OK, clientChannel->sendMessage(&clientReply)) << "client channel should be able to send message to server channel"; @@ -112,14 +110,14 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "server channel should be able to receive message from client channel"; EXPECT_EQ(clientReply.header.type, serverReply.header.type) << "server channel should receive the correct message from client channel"; - EXPECT_EQ(clientReply.body.finished.seq, serverReply.body.finished.seq) + EXPECT_EQ(clientReply.header.seq, serverReply.header.seq) << "server channel should receive the correct message from client channel"; EXPECT_EQ(clientReply.body.finished.handled, serverReply.body.finished.handled) << "server channel should receive the correct message from client channel"; } TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); @@ -133,7 +131,7 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { } TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); @@ -141,7 +139,7 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - serverChannel.clear(); // close server channel + serverChannel.reset(); // close server channel InputMessage msg; EXPECT_EQ(DEAD_OBJECT, clientChannel->receiveMessage(&msg)) @@ -149,7 +147,7 @@ TEST_F(InputChannelTest, ReceiveSignal_WhenPeerClosed_ReturnsAnError) { } TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); @@ -157,7 +155,7 @@ TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - serverChannel.clear(); // close server channel + serverChannel.reset(); // close server channel InputMessage msg; msg.header.type = InputMessage::Type::KEY; @@ -166,7 +164,7 @@ TEST_F(InputChannelTest, SendSignal_WhenPeerClosed_ReturnsAnError) { } TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { - sp<InputChannel> serverChannel, clientChannel; + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result) @@ -180,7 +178,7 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { InputMessage serverMsg = {}, clientMsg; serverMsg.header.type = InputMessage::Type::MOTION; - serverMsg.body.motion.seq = 1; + serverMsg.header.seq = 1; serverMsg.body.motion.pointerCount = 1; for (MotionClassification classification : classifications) { @@ -197,5 +195,36 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { } } +TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; + + status_t result = + InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel); + + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + InputChannel chan; + Parcel parcel; + ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel)); + parcel.setDataPosition(0); + chan.readFromParcel(&parcel); + + EXPECT_EQ(chan == *serverChannel, true) + << "inputchannel should be equal after parceling and unparceling.\n" + << "name " << chan.getName() << " name " << serverChannel->getName(); +} + +TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; + + status_t result = + InputChannel::openInputChannelPair("channel dup", serverChannel, clientChannel); + + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + std::unique_ptr<InputChannel> dupChan = serverChannel->dup(); + + EXPECT_EQ(*serverChannel == *dupChan, true) << "inputchannel should be equal after duplication"; +} } // namespace android diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 553dc4c068..601d8dabf7 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -17,6 +17,7 @@ #include <array> #include <math.h> +#include <attestation/HmacKeyManager.h> #include <binder/Parcel.h> #include <gtest/gtest.h> #include <input/Input.h> @@ -225,6 +226,7 @@ protected: static constexpr float Y_OFFSET = 1.1; int32_t mId; + ui::Transform mTransform; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); @@ -233,6 +235,7 @@ protected: void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { mId = InputEvent::nextId(); + mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1}); PointerProperties pointerProperties[2]; pointerProperties[0].clear(); @@ -266,7 +269,7 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, - MotionClassification::NONE, X_SCALE, Y_SCALE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + MotionClassification::NONE, mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); @@ -326,8 +329,7 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); ASSERT_EQ(MotionClassification::NONE, event->getClassification()); - EXPECT_EQ(X_SCALE, event->getXScale()); - EXPECT_EQ(Y_SCALE, event->getYScale()); + EXPECT_EQ(mTransform, event->getTransform()); ASSERT_EQ(X_OFFSET, event->getXOffset()); ASSERT_EQ(Y_OFFSET, event->getYOffset()); ASSERT_EQ(2.0f, event->getXPrecision()); @@ -545,7 +547,7 @@ TEST_F(MotionEventTest, Parcel) { ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent)); } -static void setRotationMatrix(float matrix[9], float angle) { +static void setRotationMatrix(std::array<float, 9>& matrix, float angle) { float sin = sinf(angle); float cos = cosf(angle); matrix[0] = cos; @@ -584,13 +586,14 @@ TEST_F(MotionEventTest, Transform) { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); } MotionEvent event; + ui::Transform identityTransform; event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, - 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, 0 /*downTime*/, - 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, + 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, + 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, + pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -606,7 +609,7 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); // Apply a rotation about the origin by ROTATION degrees clockwise. - float matrix[9]; + std::array<float, 9> matrix; setRotationMatrix(matrix, ROTATION * PI_180); event.transform(matrix); @@ -648,11 +651,12 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { pointerCoords[i].clear(); } + ui::Transform identityTransform; for (MotionClassification classification : classifications) { event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, - AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, 1 /*xScale*/, - 1 /*yScale*/, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, + identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); @@ -670,10 +674,11 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { pointerCoords[i].clear(); } + ui::Transform identityTransform; event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, - AMETA_NONE, 0, MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0, 0, 0, - 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 8e2eec85ed..4f53dc9ec4 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -20,43 +20,32 @@ #include <sys/mman.h> #include <time.h> +#include <attestation/HmacKeyManager.h> #include <cutils/ashmem.h> #include <gtest/gtest.h> #include <input/InputTransport.h> -#include <utils/Timers.h> #include <utils/StopWatch.h> +#include <utils/Timers.h> namespace android { class InputPublisherAndConsumerTest : public testing::Test { protected: - sp<InputChannel> serverChannel, clientChannel; - InputPublisher* mPublisher; - InputConsumer* mConsumer; + std::shared_ptr<InputChannel> mServerChannel, mClientChannel; + std::unique_ptr<InputPublisher> mPublisher; + std::unique_ptr<InputConsumer> mConsumer; PreallocatedInputEventFactory mEventFactory; - virtual void SetUp() { + void SetUp() override { + std::unique_ptr<InputChannel> serverChannel, clientChannel; status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result); + mServerChannel = std::move(serverChannel); + mClientChannel = std::move(clientChannel); - mPublisher = new InputPublisher(serverChannel); - mConsumer = new InputConsumer(clientChannel); - } - - virtual void TearDown() { - if (mPublisher) { - delete mPublisher; - mPublisher = nullptr; - } - - if (mConsumer) { - delete mConsumer; - mConsumer = nullptr; - } - - serverChannel.clear(); - clientChannel.clear(); + mPublisher = std::make_unique<InputPublisher>(mServerChannel); + mConsumer = std::make_unique<InputConsumer>(mClientChannel); } void PublishAndConsumeKeyEvent(); @@ -65,8 +54,12 @@ protected: }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { - EXPECT_EQ(serverChannel.get(), mPublisher->getChannel().get()); - EXPECT_EQ(clientChannel.get(), mConsumer->getChannel().get()); + ASSERT_NE(nullptr, mPublisher->getChannel()); + ASSERT_NE(nullptr, mConsumer->getChannel()); + EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get()); + EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get()); + ASSERT_EQ(mPublisher->getChannel()->getConnectionToken(), + mConsumer->getChannel()->getConnectionToken()); } void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { @@ -185,12 +178,13 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } + ui::Transform transform; + transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, actionButton, flags, edgeFlags, metaState, buttonState, - classification, xScale, yScale, xOffset, yOffset, - xPrecision, yPrecision, xCursorPosition, - yCursorPosition, downTime, eventTime, pointerCount, - pointerProperties, pointerCoords); + classification, transform, xPrecision, yPrecision, + xCursorPosition, yCursorPosition, downTime, eventTime, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -218,8 +212,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(metaState, motionEvent->getMetaState()); EXPECT_EQ(buttonState, motionEvent->getButtonState()); EXPECT_EQ(classification, motionEvent->getClassification()); - EXPECT_EQ(xScale, motionEvent->getXScale()); - EXPECT_EQ(yScale, motionEvent->getYScale()); + EXPECT_EQ(transform, motionEvent->getTransform()); EXPECT_EQ(xOffset, motionEvent->getXOffset()); EXPECT_EQ(yOffset, motionEvent->getYOffset()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); @@ -338,10 +331,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } + ui::Transform identityTransform; status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, - 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) @@ -354,10 +347,10 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; + ui::Transform identityTransform; status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, - 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) @@ -375,10 +368,10 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } + ui::Transform identityTransform; status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, 1 /* xScale */, - 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index d1cb527a57..c18a17f1ae 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -22,17 +22,19 @@ #include <input/InputWindow.h> #include <input/InputTransport.h> +using std::chrono_literals::operator""s; + namespace android { namespace test { TEST(InputWindowInfo, ParcellingWithoutToken) { - InputWindowInfo i; + InputWindowInfo i, i2; i.token = nullptr; Parcel p; - ASSERT_EQ(OK, i.write(p)); + ASSERT_EQ(OK, i.writeToParcel(&p)); p.setDataPosition(0); - InputWindowInfo i2 = InputWindowInfo::read(p); + i2.readFromParcel(&p); ASSERT_TRUE(i2.token == nullptr); } @@ -42,40 +44,44 @@ TEST(InputWindowInfo, Parcelling) { i.token = new BBinder(); i.id = 1; i.name = "Foobar"; - i.layoutParamsFlags = 7; - i.layoutParamsType = 39; - i.dispatchingTimeout = 12; + i.flags = InputWindowInfo::Flag::SLIPPERY; + i.type = InputWindowInfo::Type::INPUT_METHOD; + i.dispatchingTimeout = 12s; i.frameLeft = 93; i.frameTop = 34; i.frameRight = 16; i.frameBottom = 19; i.surfaceInset = 17; i.globalScaleFactor = 0.3; - i.windowXScale = 0.4; - i.windowYScale = 0.5; + i.alpha = 0.7; + i.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1}); i.visible = false; - i.canReceiveKeys = false; - i.hasFocus = false; + i.focusable = false; i.hasWallpaper = false; i.paused = false; + i.touchOcclusionMode = TouchOcclusionMode::ALLOW; i.ownerPid = 19; i.ownerUid = 24; - i.inputFeatures = 29; + i.packageName = "com.example.package"; + i.inputFeatures = InputWindowInfo::Feature::DISABLE_USER_ACTIVITY; i.displayId = 34; i.portalToDisplayId = 2; i.replaceTouchableRegionWithCrop = true; i.touchableRegionCropHandle = touchableRegionCropHandle; + i.applicationInfo.name = "ApplicationFooBar"; + i.applicationInfo.token = new BBinder(); + i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD; Parcel p; - i.write(p); - + i.writeToParcel(&p); p.setDataPosition(0); - InputWindowInfo i2 = InputWindowInfo::read(p); + InputWindowInfo i2; + i2.readFromParcel(&p); ASSERT_EQ(i.token, i2.token); ASSERT_EQ(i.id, i2.id); ASSERT_EQ(i.name, i2.name); - ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags); - ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType); + ASSERT_EQ(i.flags, i2.flags); + ASSERT_EQ(i.type, i2.type); ASSERT_EQ(i.dispatchingTimeout, i2.dispatchingTimeout); ASSERT_EQ(i.frameLeft, i2.frameLeft); ASSERT_EQ(i.frameTop, i2.frameTop); @@ -83,20 +89,36 @@ TEST(InputWindowInfo, Parcelling) { ASSERT_EQ(i.frameBottom, i2.frameBottom); ASSERT_EQ(i.surfaceInset, i2.surfaceInset); ASSERT_EQ(i.globalScaleFactor, i2.globalScaleFactor); - ASSERT_EQ(i.windowXScale, i2.windowXScale); - ASSERT_EQ(i.windowYScale, i2.windowYScale); + ASSERT_EQ(i.alpha, i2.alpha); + ASSERT_EQ(i.transform, i2.transform); ASSERT_EQ(i.visible, i2.visible); - ASSERT_EQ(i.canReceiveKeys, i2.canReceiveKeys); - ASSERT_EQ(i.hasFocus, i2.hasFocus); + ASSERT_EQ(i.focusable, i2.focusable); ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper); ASSERT_EQ(i.paused, i2.paused); + ASSERT_EQ(i.touchOcclusionMode, i2.touchOcclusionMode); ASSERT_EQ(i.ownerPid, i2.ownerPid); ASSERT_EQ(i.ownerUid, i2.ownerUid); + ASSERT_EQ(i.packageName, i2.packageName); ASSERT_EQ(i.inputFeatures, i2.inputFeatures); ASSERT_EQ(i.displayId, i2.displayId); ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId); ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); + ASSERT_EQ(i.applicationInfo, i2.applicationInfo); +} + +TEST(InputApplicationInfo, Parcelling) { + InputApplicationInfo i; + i.token = new BBinder(); + i.name = "ApplicationFooBar"; + i.dispatchingTimeoutMillis = 0x12345678ABCD; + + Parcel p; + ASSERT_EQ(i.writeToParcel(&p), OK); + p.setDataPosition(0); + InputApplicationInfo i2; + ASSERT_EQ(i2.readFromParcel(&p), OK); + ASSERT_EQ(i, i2); } } // namespace test diff --git a/libs/input/tests/NamedEnum_test.cpp b/libs/input/tests/NamedEnum_test.cpp new file mode 100644 index 0000000000..4e93f71fa0 --- /dev/null +++ b/libs/input/tests/NamedEnum_test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <input/NamedEnum.h> + +namespace android { + +// Test enum class maximum enum value smaller than default maximum of 8. +enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 }; +// Big enum contains enum values greater than default maximum of 8. +enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF }; + +// Declared to specialize the maximum enum since the enum size exceeds 8 by default. +template <> +constexpr size_t NamedEnum::max<TestBigEnums> = 16; + +namespace test { +using android::TestBigEnums; +using android::TestEnums; + +TEST(NamedEnum, RuntimeNamedEnum) { + TestEnums e = TestEnums::ZERO; + ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); + + e = TestEnums::ONE; + ASSERT_EQ(NamedEnum::enum_name(e), "ONE"); + + e = TestEnums::THREE; + ASSERT_EQ(NamedEnum::enum_name(e), "THREE"); + + e = TestEnums::SEVEN; + ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN"); +} + +// Test big enum +TEST(NamedEnum, RuntimeBigNamedEnum) { + TestBigEnums e = TestBigEnums::ZERO; + ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); + + e = TestBigEnums::FIFTEEN; + ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN"); +} + +TEST(NamedEnum, RuntimeNamedEnumAsString) { + TestEnums e = TestEnums::ZERO; + ASSERT_EQ(NamedEnum::string(e), "ZERO"); + + e = TestEnums::ONE; + ASSERT_EQ(NamedEnum::string(e), "ONE"); + + e = TestEnums::THREE; + ASSERT_EQ(NamedEnum::string(e), "THREE"); + + e = TestEnums::SEVEN; + ASSERT_EQ(NamedEnum::string(e), "SEVEN"); +} + +TEST(NamedEnum, RuntimeBigNamedEnumAsString) { + TestBigEnums e = TestBigEnums::ZERO; + ASSERT_EQ(NamedEnum::string(e), "ZERO"); + + e = TestBigEnums::FIFTEEN; + ASSERT_EQ(NamedEnum::string(e), "FIFTEEN"); +} + +TEST(NamedEnum, RuntimeUnknownNamedEnum) { + TestEnums e = static_cast<TestEnums>(0x5); + ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); + e = static_cast<TestEnums>(0x9); + ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); +} + +TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) { + TestEnums e = static_cast<TestEnums>(0x5); + ASSERT_EQ(NamedEnum::string(e), "0x00000005"); + e = static_cast<TestEnums>(0x9); + ASSERT_EQ(NamedEnum::string(e), "0x00000009"); +} + +TEST(NamedEnum, CompileTimeFlagName) { + static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO"); + static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE"); +} + +} // namespace test + +} // namespace android diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 1fe7bb90ca..3c5fb22588 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -34,8 +34,7 @@ void TestPointerCoordsAlignment() { void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage, body, 8); - CHECK_OFFSET(InputMessage::Body::Key, seq, 0); - CHECK_OFFSET(InputMessage::Body::Key, eventId, 4); + CHECK_OFFSET(InputMessage::Body::Key, eventId, 0); CHECK_OFFSET(InputMessage::Body::Key, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Key, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Key, source, 20); @@ -49,8 +48,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, repeatCount, 80); CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); - CHECK_OFFSET(InputMessage::Body::Motion, seq, 0); - CHECK_OFFSET(InputMessage::Body::Motion, eventId, 4); + CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); @@ -64,27 +62,29 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, classification, 80); CHECK_OFFSET(InputMessage::Body::Motion, edgeFlags, 84); CHECK_OFFSET(InputMessage::Body::Motion, downTime, 88); - CHECK_OFFSET(InputMessage::Body::Motion, xScale, 96); - CHECK_OFFSET(InputMessage::Body::Motion, yScale, 100); - CHECK_OFFSET(InputMessage::Body::Motion, xOffset, 104); - CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 108); - CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 112); - CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 116); - CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 120); - CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 124); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 128); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 136); - - CHECK_OFFSET(InputMessage::Body::Focus, seq, 0); - CHECK_OFFSET(InputMessage::Body::Focus, eventId, 4); - CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 12); - CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 14); - - CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); + CHECK_OFFSET(InputMessage::Body::Motion, dsdx, 96); + CHECK_OFFSET(InputMessage::Body::Motion, dtdx, 100); + CHECK_OFFSET(InputMessage::Body::Motion, dtdy, 104); + CHECK_OFFSET(InputMessage::Body::Motion, dsdy, 108); + CHECK_OFFSET(InputMessage::Body::Motion, tx, 112); + CHECK_OFFSET(InputMessage::Body::Motion, ty, 116); + CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 120); + CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124); + CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128); + CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 136); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 144); + + CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); + CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); + CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6); + CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); } void TestHeaderSize() { + CHECK_OFFSET(InputMessage::Header, type, 0); + CHECK_OFFSET(InputMessage::Header, seq, 4); static_assert(sizeof(InputMessage::Header) == 8); } @@ -98,7 +98,7 @@ void TestBodySize() { offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); static_assert(sizeof(InputMessage::Body::Finished) == 8); - static_assert(sizeof(InputMessage::Body::Focus) == 16); + static_assert(sizeof(InputMessage::Body::Focus) == 8); } // --- VerifiedInputEvent --- diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index bf452c07a3..d049d05ac5 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -21,6 +21,7 @@ #include <math.h> #include <android-base/stringprintf.h> +#include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> #include <input/VelocityTracker.h> @@ -176,12 +177,12 @@ static std::vector<MotionEvent> createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; + ui::Transform identityTransform; event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, INVALID_HMAC, action, 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, 1 /*xScale*/, 1 /*yScale*/, 0 /*xOffset*/, - 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - AMOTION_EVENT_INVALID_CURSOR_POSITION, + MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, + 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); @@ -191,8 +192,9 @@ static std::vector<MotionEvent> createMotionEventStream( return events; } -static void computeAndCheckVelocity(const char* strategy, - const std::vector<MotionEventEntry>& motions, int32_t axis, float targetVelocity) { +static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, + const std::vector<MotionEventEntry>& motions, int32_t axis, + float targetVelocity) { VelocityTracker vt(strategy); float Vx, Vy; @@ -217,7 +219,7 @@ static void computeAndCheckVelocity(const char* strategy, static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions, const std::array<float, 3>& coefficients) { - VelocityTracker vt("lsq2"); + VelocityTracker vt(VelocityTracker::Strategy::LSQ2); std::vector<MotionEvent> events = createMotionEventStream(motions); for (MotionEvent event : events) { vt.addMovement(&event); @@ -238,36 +240,34 @@ TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) { // It is difficult to determine the correct answer here, but at least the direction // of the reported velocity should be positive. std::vector<MotionEventEntry> motions = { - {0ms, {{ 273, NAN}}}, - {12585us, {{293, NAN}}}, - {14730us, {{293, NAN}}}, - {14730us, {{293, NAN}}}, // ACTION_UP + {0ms, {{273, 0}}}, + {12585us, {{293, 0}}}, + {14730us, {{293, 0}}}, + {14730us, {{293, 0}}}, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1600); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 1600); } TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { // Same coordinate is reported 3 times in a row std::vector<MotionEventEntry> motions = { - { 0ms, {{293, NAN}} }, - { 6132us, {{293, NAN}} }, - { 11283us, {{293, NAN}} }, - { 11283us, {{293, NAN}} }, // ACTION_UP + {0ms, {{293, 0}}}, + {6132us, {{293, 0}}}, + {11283us, {{293, 0}}}, + {11283us, {{293, 0}}}, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 0); } TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { // Fixed velocity at 5 points per 10 milliseconds std::vector<MotionEventEntry> motions = { - { 0ms, {{0, NAN}} }, - { 10ms, {{5, NAN}} }, - { 20ms, {{10, NAN}} }, - { 20ms, {{10, NAN}} }, // ACTION_UP + {0ms, {{0, 0}}}, {10ms, {{5, 0}}}, {20ms, {{10, 0}}}, {20ms, {{10, 0}}}, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 500); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 500); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 500); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500); } @@ -297,8 +297,10 @@ TEST_F(VelocityTrackerTest, SwordfishFlingDown) { { 96948871ns, {{274.79245, 428.113159}} }, { 96948871ns, {{274.79245, 428.113159}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 623.577637); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 5970.7309); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 623.577637); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 5970.7309); } // --------------- Recorded by hand on sailfish, generated by a script ----------------------------- @@ -339,10 +341,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { { 235089162955851ns, {{560.66, 843.82}} }, { 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 872.794617); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 951.698181); - computeAndCheckVelocity("impulse",motions, AMOTION_EVENT_AXIS_Y, -3604.819336); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3044.966064); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 872.794617); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 951.698181); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -3604.819336); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3044.966064); } @@ -368,8 +374,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { { 235110660368000ns, {{530.00, 980.00}} }, { 235110660368000ns, {{530.00, 980.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4096.583008); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3455.094238); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -4096.583008); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3455.094238); } @@ -396,10 +404,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { { 792629200000ns, {{619.00, 1115.00}} }, { 792629200000ns, {{619.00, 1115.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 574.33429); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 617.40564); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -2361.982666); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -2500.055664); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 574.33429); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 617.40564); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -2361.982666); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -2500.055664); } @@ -426,10 +438,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { { 235160520366000ns, {{679.00, 814.00}} }, { 235160520366000ns, {{679.00, 814.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 1274.141724); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 1438.53186); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -3001.4348); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -3695.859619); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 1274.141724); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 1438.53186); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -3001.4348); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -3695.859619); } @@ -452,8 +468,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { { 847237986000ns, {{610.00, 1095.00}} }, { 847237986000ns, {{610.00, 1095.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -4280.07959); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -4241.004395); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -4280.07959); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -4241.004395); } @@ -476,8 +494,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { { 235200616933000ns, {{590.00, 844.00}} }, { 235200616933000ns, {{590.00, 844.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -8715.686523); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -7639.026367); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -8715.686523); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -7639.026367); } @@ -499,10 +519,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { { 920989261000ns, {{715.00, 903.00}} }, { 920989261000ns, {{715.00, 903.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 5670.329102); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, 5991.866699); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -13021.101562); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -15093.995117); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + 5670.329102); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + 5991.866699); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -13021.101562); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -15093.995117); } @@ -522,8 +546,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { { 235247220736000ns, {{620.00, 641.00}} }, { 235247220736000ns, {{620.00, 641.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -20286.958984); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -20494.587891); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -20286.958984); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -20494.587891); } @@ -541,8 +567,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { { 235302613019881ns, {{679.26, 526.73}} }, { 235302613019881ns, {{679.26, 526.73}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, -39295.941406); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -36461.421875); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + -39295.941406); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + -36461.421875); } @@ -569,10 +597,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { { 235655842893000ns, {{563.00, 649.00}} }, { 235655842893000ns, {{563.00, 649.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -419.749695); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -398.303894); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3309.016357); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 3969.099854); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -419.749695); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -398.303894); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 3309.016357); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 3969.099854); } @@ -599,10 +631,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { { 235671246532000ns, {{470.00, 799.00}} }, { 235671246532000ns, {{470.00, 799.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -262.80426); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -243.665344); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4215.682129); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4587.986816); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -262.80426); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -243.665344); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4215.682129); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4587.986816); } @@ -622,10 +658,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { { 171051052000ns, {{536.00, 586.00}} }, { 171051052000ns, {{536.00, 586.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -723.413513); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -651.038452); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 2091.502441); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 1934.517456); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -723.413513); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -651.038452); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 2091.502441); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 1934.517456); } @@ -652,8 +692,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { { 235695373403000ns, {{564.00, 744.00}} }, { 235695373403000ns, {{564.00, 744.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4254.639648); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4698.415039); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4254.639648); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4698.415039); } @@ -677,10 +719,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { { 235709710626776ns, {{511.72, 741.85}} }, { 235709710626776ns, {{511.72, 741.85}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -430.440247); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -447.600311); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 3953.859375); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4316.155273); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -430.440247); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -447.600311); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 3953.859375); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4316.155273); } @@ -706,8 +752,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { { 235727721580000ns, {{516.00, 658.00}} }, { 235727721580000ns, {{516.00, 658.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 4484.617676); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 4927.92627); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 4484.617676); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 4927.92627); } @@ -725,8 +773,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { { 235762396429369ns, {{404.37, 680.67}} }, { 235762396429369ns, {{404.37, 680.67}} }, //ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 14227.0224); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16064.685547); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 14227.0224); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 16064.685547); } @@ -744,8 +794,10 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { { 235772537635000ns, {{484.00, 589.00}} }, { 235772537635000ns, {{484.00, 589.00}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 18660.048828); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 16918.439453); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 18660.048828); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 16918.439453); } @@ -764,10 +816,14 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { { 507703352649ns, {{443.71, 857.77}} }, { 507703352649ns, {{443.71, 857.77}} }, // ACTION_UP }; - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, -4111.8173); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -6388.48877); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 29765.908203); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, 28354.796875); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, + -4111.8173); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + -6388.48877); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, + 29765.908203); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + 28354.796875); } /** @@ -789,10 +845,10 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_ThreeFi // Velocity should actually be zero, but we expect 0.016 here instead. // This is close enough to zero, and is likely caused by division by a very small number. - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_X, -0.016); - computeAndCheckVelocity("lsq2", motions, AMOTION_EVENT_AXIS_Y, -0.016); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_X, 0); - computeAndCheckVelocity("impulse", motions, AMOTION_EVENT_AXIS_Y, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, -0.016); + computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, -0.016); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X, 0); + computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y, 0); } /** diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index 4e8e840d1c..36f87b8a6a 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <attestation/HmacKeyManager.h> #include <gtest/gtest.h> #include <input/Input.h> @@ -39,13 +40,14 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { pointerCoords[i].clear(); } + ui::Transform transform; + transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1}); event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, 2 /*xScale*/, 3 /*yScale*/, 4 /*xOffset*/, - 5 /*yOffset*/, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, 280 /*xCursorPosition*/, - 540 /*yCursorPosition*/, 100 /*downTime*/, 200 /*eventTime*/, pointerCount, - pointerProperties, pointerCoords); + MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 100 /*downTime*/, + 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); return event; } diff --git a/libs/math/Android.bp b/libs/math/Android.bp index 3b1edcb43a..22d471204a 100644 --- a/libs/math/Android.bp +++ b/libs/math/Android.bp @@ -25,6 +25,11 @@ cc_library_static { min_sdk_version: "29", export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + } + } } subdirs = ["tests"] diff --git a/libs/math/include/math/half.h b/libs/math/include/math/half.h index 76829734a4..617a0ab5d2 100644 --- a/libs/math/include/math/half.h +++ b/libs/math/include/math/half.h @@ -82,6 +82,7 @@ class half { }; public: + CONSTEXPR half() noexcept { } CONSTEXPR half(float v) noexcept : mBits(ftoh(v)) { } CONSTEXPR operator float() const noexcept { return htof(mBits); } diff --git a/libs/math/tests/half_test.cpp b/libs/math/tests/half_test.cpp index 496a7ef56d..604072e557 100644 --- a/libs/math/tests/half_test.cpp +++ b/libs/math/tests/half_test.cpp @@ -35,6 +35,7 @@ TEST_F(HalfTest, Basics) { EXPECT_EQ(2UL, sizeof(half)); // test +/- zero + EXPECT_EQ(0x0000, half().getBits()); EXPECT_EQ(0x0000, half( 0.0f).getBits()); EXPECT_EQ(0x8000, half(-0.0f).getBits()); diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp index b431cbb6c0..9e515cdc80 100644 --- a/libs/nativedisplay/AChoreographer.cpp +++ b/libs/nativedisplay/AChoreographer.cpp @@ -128,11 +128,15 @@ public: static Choreographer* getForThread(); virtual ~Choreographer() override EXCLUDES(gChoreographers.lock); + int64_t getVsyncId() const; + int64_t getFrameDeadline() const; + private: Choreographer(const Choreographer&) = delete; - void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; + void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, + VsyncEventData vsyncEventData) override; void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t vsyncPeriod) override; @@ -146,6 +150,7 @@ private: std::vector<RefreshRateCallback> mRefreshRateCallbacks; nsecs_t mLatestVsyncPeriod = -1; + VsyncEventData mLastVsyncEventData; const sp<Looper> mLooper; const std::thread::id mThreadId; @@ -350,7 +355,8 @@ void Choreographer::handleRefreshRateUpdates() { // TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the // internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for // the internal display implicitly. -void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) { +void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t, + VsyncEventData vsyncEventData) { std::vector<FrameCallback> callbacks{}; { std::lock_guard<std::mutex> _l{mLock}; @@ -360,6 +366,7 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t mFrameCallbacks.pop(); } } + mLastVsyncEventData = vsyncEventData; for (const auto& cb : callbacks) { if (cb.callback64 != nullptr) { cb.callback64(timestamp, cb.data); @@ -370,9 +377,8 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t } void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { - ALOGV("choreographer %p ~ received hotplug event (displayId=%" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.", - this, displayId, toString(connected)); + ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", + this, to_string(displayId).c_str(), toString(connected)); } // TODO(b/74619554): The PhysicalDisplayId is ignored because currently @@ -382,9 +388,8 @@ void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool c // PhysicalDisplayId should no longer be ignored. void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, int32_t configId, nsecs_t) { - ALOGV("choreographer %p ~ received config change event " - "(displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%d).", - this, displayId, configId); + ALOGV("choreographer %p ~ received config change event (displayId=%s, configId=%d).", + this, to_string(displayId).c_str(), configId); } void Choreographer::dispatchNullEvent(nsecs_t, PhysicalDisplayId) { @@ -406,6 +411,14 @@ void Choreographer::handleMessage(const Message& message) { } } +int64_t Choreographer::getVsyncId() const { + return mLastVsyncEventData.id; +} + +int64_t Choreographer::getFrameDeadline() const { + return mLastVsyncEventData.deadlineTimestamp; +} + } // namespace android using namespace android; @@ -413,6 +426,11 @@ static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* cho return reinterpret_cast<Choreographer*>(choreographer); } +static inline const Choreographer* AChoreographer_to_Choreographer( + const AChoreographer* choreographer) { + return reinterpret_cast<const Choreographer*>(choreographer); +} + // Glue for private C api namespace android { void AChoreographer_signalRefreshRateCallbacks(nsecs_t vsyncPeriod) EXCLUDES(gChoreographers.lock) { @@ -476,15 +494,18 @@ void AChoreographer_routeUnregisterRefreshRateCallback(AChoreographer* choreogra return AChoreographer_unregisterRefreshRateCallback(choreographer, callback, data); } +int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer) { + return AChoreographer_to_Choreographer(choreographer)->getVsyncId(); +} + +int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer) { + return AChoreographer_to_Choreographer(choreographer)->getFrameDeadline(); +} + } // namespace android /* Glue for the NDK interface */ -static inline const Choreographer* AChoreographer_to_Choreographer( - const AChoreographer* choreographer) { - return reinterpret_cast<const Choreographer*>(choreographer); -} - static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) { return reinterpret_cast<AChoreographer*>(choreographer); } diff --git a/libs/nativedisplay/include-private/private/android/choreographer.h b/libs/nativedisplay/include-private/private/android/choreographer.h index 21649304bf..d3a4a66c18 100644 --- a/libs/nativedisplay/include-private/private/android/choreographer.h +++ b/libs/nativedisplay/include-private/private/android/choreographer.h @@ -29,6 +29,19 @@ void AChoreographer_initJVM(JNIEnv* env); // for consumption by callbacks. void AChoreographer_signalRefreshRateCallbacks(int64_t vsyncPeriod); +// Returns the vsync id of the last frame callback. Client are expected to call +// this function from their frame callback function to get the vsyncId and pass +// it together with a buffer or transaction to the Surface Composer. Calling +// this function from anywhere else will return an undefined value. +int64_t AChoreographer_getVsyncId(const AChoreographer* choreographer); + +// Returns the deadline timestamp (in CLOCK_MONOTONIC) of the last frame callback. +// Client are expected to call this function from their frame callback function +// to get the deadline and use it to know whether a frame is likely to miss +// presentation. Calling this function from anywhere else will return an undefined +// value. +int64_t AChoreographer_getFrameDeadline(const AChoreographer* choreographer); + // Trampoline functions allowing libandroid.so to define the NDK symbols without including // the entirety of libnativedisplay as a whole static lib. As libnativedisplay // maintains global state, libnativedisplay can never be directly statically diff --git a/libs/nativedisplay/libnativedisplay.map.txt b/libs/nativedisplay/libnativedisplay.map.txt index fc59431d08..fda6a20a0b 100644 --- a/libs/nativedisplay/libnativedisplay.map.txt +++ b/libs/nativedisplay/libnativedisplay.map.txt @@ -29,6 +29,8 @@ LIBNATIVEDISPLAY_PLATFORM { android::AChoreographer_routeRegisterRefreshRateCallback*; android::AChoreographer_routeUnregisterRefreshRateCallback*; android::AChoreographer_signalRefreshRateCallbacks*; + android::AChoreographer_getVsyncId*; + android::AChoreographer_getFrameDeadline*; android::ADisplay_acquirePhysicalDisplays*; android::ADisplay_release*; android::ADisplay_getMaxSupportedFps*; diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index 16afc68b3d..365e788ea6 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -51,7 +51,15 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace } int slot = item.mSlot; + *outQueueEmpty = false; if (item.mFence->isValid()) { + // If fence is not signaled, that means frame is not ready and + // outQueueEmpty is set to true. By the time the fence is signaled, + // there may be a new buffer queued. This is a proper detection for an + // empty queue and it is needed to avoid infinite loop in + // ASurfaceTexture_dequeueBuffer (see b/159921224). + *outQueueEmpty = item.mFence->getStatus() == Fence::Status::Unsignaled; + // Wait on the producer fence for the buffer to be ready. err = fenceWait(item.mFence->get(), fencePassThroughHandle); if (err != OK) { @@ -112,7 +120,6 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace st.mCurrentFrameNumber = item.mFrameNumber; st.computeCurrentTransformMatrixLocked(); - *outQueueEmpty = false; *outDataspace = item.mDataSpace; *outSlotid = slot; return st.mSlots[slot].mGraphicBuffer; diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index b78fc5dbbc..138e08f490 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -255,6 +255,7 @@ enum { NATIVE_WINDOW_ALLOCATE_BUFFERS = 45, /* private */ NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER = 46, /* private */ NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */ + NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC = 48, /* private */ // clang-format on }; @@ -1022,6 +1023,12 @@ static inline int native_window_set_frame_rate(struct ANativeWindow* window, flo (int)compatibility); } +static inline int native_window_set_frame_timeline_vsync(struct ANativeWindow* window, + int64_t frameTimelineVsyncId) { + return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_VSYNC, + frameTimelineVsyncId); +} + // ------------------------------------------------------------------------------------------------ // Candidates for APEX visibility // These functions are planned to be made stable for APEX modules, but have not diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index eb6080fc21..eb967cec90 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -31,6 +31,7 @@ cc_defaults { "libui", "libutils", ], + whole_static_libs: ["libskia"], local_include_dirs: ["include"], export_include_dirs: ["include"], } @@ -64,13 +65,25 @@ filegroup { ], } +filegroup { + name: "librenderengine_threaded_sources", + srcs: [ + "threaded/RenderEngineThreaded.cpp", + ], +} + +filegroup { + name: "librenderengine_skia_sources", + srcs: [ + "skia/SkiaRenderEngine.cpp", + "skia/SkiaGLRenderEngine.cpp", + "skia/filters/BlurFilter.cpp", + ], +} + cc_library_static { name: "librenderengine", defaults: ["librenderengine_defaults"], - vendor_available: true, - vndk: { - enabled: true, - }, double_loadable: true, clang: true, cflags: [ @@ -80,6 +93,8 @@ cc_library_static { srcs: [ ":librenderengine_sources", ":librenderengine_gl_sources", + ":librenderengine_threaded_sources", + ":librenderengine_skia_sources", ], lto: { thin: true, diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 0fdf093b2f..3e65d9afce 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -18,39 +18,45 @@ #include <cutils/properties.h> #include <log/log.h> -#include <private/gui/SyncFeatures.h> #include "gl/GLESRenderEngine.h" +#include "threaded/RenderEngineThreaded.h" + +#include "skia/SkiaGLRenderEngine.h" namespace android { namespace renderengine { -std::unique_ptr<impl::RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { +std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { + RenderEngineType renderEngineType = args.renderEngineType; + + // Keep the ability to override by PROPERTIES: char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles"); + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); if (strcmp(prop, "gles") == 0) { - ALOGD("RenderEngine GLES Backend"); - return renderengine::gl::GLESRenderEngine::create(args); + renderEngineType = RenderEngineType::GLES; + } + if (strcmp(prop, "threaded") == 0) { + renderEngineType = RenderEngineType::THREADED; + } + if (strcmp(prop, "skiagl") == 0) { + renderEngineType = RenderEngineType::SKIA_GL; } - ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); - return renderengine::gl::GLESRenderEngine::create(args); -} - -RenderEngine::~RenderEngine() = default; - -namespace impl { - -RenderEngine::RenderEngine(const RenderEngineCreationArgs& args) : mArgs(args) {} - -RenderEngine::~RenderEngine() = default; -bool RenderEngine::useNativeFenceSync() const { - return SyncFeatures::getInstance().useNativeFenceSync(); + switch (renderEngineType) { + case RenderEngineType::THREADED: + ALOGD("Threaded RenderEngine with GLES Backend"); + return renderengine::threaded::RenderEngineThreaded::create( + [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); }); + case RenderEngineType::SKIA_GL: + return renderengine::skia::SkiaGLRenderEngine::create(args); + case RenderEngineType::GLES: + default: + ALOGD("RenderEngine with GLES Backend"); + return renderengine::gl::GLESRenderEngine::create(args); + } } -bool RenderEngine::useWaitSync() const { - return SyncFeatures::getInstance().useWaitSync(); -} +RenderEngine::~RenderEngine() = default; -} // namespace impl } // namespace renderengine } // namespace android diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index af973673d8..13577f7d83 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -51,8 +51,6 @@ #include "ProgramCache.h" #include "filters/BlurFilter.h" -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); - bool checkGlError(const char* op, int lineNumber) { bool errorFound = false; GLint error = glGetError(); @@ -116,6 +114,28 @@ namespace android { namespace renderengine { namespace gl { +class BindNativeBufferAsFramebuffer { +public: + BindNativeBufferAsFramebuffer(GLESRenderEngine& engine, ANativeWindowBuffer* buffer, + const bool useFramebufferCache) + : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) { + mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(), + useFramebufferCache) + ? mEngine.bindFrameBuffer(mFramebuffer) + : NO_MEMORY; + } + ~BindNativeBufferAsFramebuffer() { + mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true); + mEngine.unbindFrameBuffer(mFramebuffer); + } + status_t getStatus() const { return mStatus; } + +private: + GLESRenderEngine& mEngine; + Framebuffer* mFramebuffer; + status_t mStatus; +}; + using base::StringAppendF; using ui::Dataspace; @@ -208,16 +228,16 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCre LOG_ALWAYS_FATAL("failed to initialize EGL"); } - const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION); + const auto eglVersion = eglQueryString(display, EGL_VERSION); if (!eglVersion) { checkGlError(__FUNCTION__, __LINE__); - LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed"); + LOG_ALWAYS_FATAL("eglQueryString(EGL_VERSION) failed"); } - const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS); + const auto eglExtensions = eglQueryString(display, EGL_EXTENSIONS); if (!eglExtensions) { checkGlError(__FUNCTION__, __LINE__); - LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed"); + LOG_ALWAYS_FATAL("eglQueryString(EGL_EXTENSIONS) failed"); } GLExtensions& extensions = GLExtensions::getInstance(); @@ -336,8 +356,7 @@ EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface stub, EGLContext protectedContext, EGLSurface protectedStub) - : renderengine::impl::RenderEngine(args), - mEGLDisplay(display), + : mEGLDisplay(display), mEGLConfig(config), mEGLContext(ctxt), mStubSurface(stub), @@ -346,7 +365,8 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp mVpWidth(0), mVpHeight(0), mFramebufferImageCacheSize(args.imageCacheSize), - mUseColorManagement(args.useColorManagement) { + mUseColorManagement(args.useColorManagement), + mPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); @@ -431,15 +451,10 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp GLESRenderEngine::~GLESRenderEngine() { // Destroy the image manager first. mImageManager = nullptr; + cleanFramebufferCache(); std::lock_guard<std::mutex> lock(mRenderingMutex); unbindFrameBuffer(mDrawingBuffer.get()); mDrawingBuffer = nullptr; - while (!mFramebufferImageCache.empty()) { - EGLImageKHR expired = mFramebufferImageCache.front().second; - mFramebufferImageCache.pop_front(); - eglDestroyImageKHR(mEGLDisplay, expired); - DEBUG_EGL_IMAGE_TRACKER_DESTROY(); - } eglDestroyImageKHR(mEGLDisplay, mPlaceholderImage); mImageCache.clear(); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); @@ -460,8 +475,7 @@ Framebuffer* GLESRenderEngine::getFramebufferForDrawing() { void GLESRenderEngine::primeCache() const { ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext, - mArgs.useColorManagement, - mArgs.precacheToneMapperShaderOnly); + mUseColorManagement, mPrecacheToneMapperShaderOnly); } base::unique_fd GLESRenderEngine::flush() { @@ -624,13 +638,8 @@ void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& i } } -status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, - const sp<GraphicBuffer>& buffer, - const sp<Fence>& bufferFence) { - if (buffer == nullptr) { - return BAD_VALUE; - } - +void GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, + const sp<Fence>& bufferFence) { ATRACE_CALL(); bool found = false; @@ -646,7 +655,8 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, if (!found) { status_t cacheResult = mImageManager->cache(buffer); if (cacheResult != NO_ERROR) { - return cacheResult; + ALOGE("Error with caching buffer: %d", cacheResult); + return; } } @@ -663,7 +673,7 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, // We failed creating the image if we got here, so bail out. ALOGE("Failed to create an EGLImage when rendering"); bindExternalTextureImage(texName, *createImage()); - return NO_INIT; + return; } bindExternalTextureImage(texName, *cachedImage->second); @@ -676,22 +686,22 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, base::unique_fd fenceFd(bufferFence->dup()); if (fenceFd == -1) { ALOGE("error dup'ing fence fd: %d", errno); - return -errno; + return; } if (!waitFence(std::move(fenceFd))) { ALOGE("failed to wait on fence fd"); - return UNKNOWN_ERROR; + return; } } else { status_t err = bufferFence->waitForever("RenderEngine::bindExternalTextureBuffer"); if (err != NO_ERROR) { ALOGE("error waiting for fence: %d", err); - return err; + return; } } } - return NO_ERROR; + return; } void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { @@ -725,9 +735,9 @@ status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBu bool created = newImage->setNativeWindowBuffer(buffer->getNativeBuffer(), buffer->getUsage() & GRALLOC_USAGE_PROTECTED); if (!created) { - ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", - buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(), - buffer->getPixelFormat()); + ALOGE("Failed to create image. id=%" PRIx64 " size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d", + buffer->getId(), buffer->getWidth(), buffer->getHeight(), buffer->getStride(), + buffer->getUsage(), buffer->getPixelFormat()); return NO_INIT; } @@ -945,6 +955,7 @@ bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) { // Bind the texture to placeholder so that backing image data can be freed. GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing()); glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer); + // Release the cached fence here, so that we don't churn reallocations when // we could no-op repeated calls of this method instead. mLastDrawFence = nullptr; @@ -952,6 +963,20 @@ bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) { return true; } +void GLESRenderEngine::cleanFramebufferCache() { + std::lock_guard<std::mutex> lock(mFramebufferImageCacheMutex); + // Bind the texture to placeholder so that backing image data can be freed. + GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing()); + glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer); + + while (!mFramebufferImageCache.empty()) { + EGLImageKHR expired = mFramebufferImageCache.front().second; + mFramebufferImageCache.pop_front(); + eglDestroyImageKHR(mEGLDisplay, expired); + DEBUG_EGL_IMAGE_TRACKER_DESTROY(); + } +} + void GLESRenderEngine::checkErrors() const { checkErrors(nullptr); } @@ -1028,7 +1053,7 @@ EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* const buffer, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) { ATRACE_CALL(); @@ -1065,7 +1090,9 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const auto blurLayersSize = blurLayers.size(); if (blurLayersSize == 0) { - fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, useFramebufferCache); + fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, + buffer.get()->getNativeBuffer(), + useFramebufferCache); if (fbo->getStatus() != NO_ERROR) { ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", buffer->handle); @@ -1122,7 +1149,9 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, if (blurLayers.size() == 0) { // Done blurring, time to bind the native FBO and render our blur onto it. - fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, buffer, + fbo = std::make_unique<BindNativeBufferAsFramebuffer>(*this, + buffer.get() + ->getNativeBuffer(), useFramebufferCache); status = fbo->getStatus(); setViewportAndProjection(display.physicalDisplay, display.clip); @@ -1189,6 +1218,11 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, texCoords[2] = vec2(1.0, 1.0); texCoords[3] = vec2(1.0, 0.0); setupLayerTexturing(texture); + + // Do not cache protected EGLImage, protected memory is limited. + if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) { + unbindExternalTextureBuffer(gBuf->getId()); + } } const half3 solidColor = layer->source.solidColor; diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 9484011525..1779994d4a 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -48,7 +48,7 @@ namespace gl { class GLImage; class BlurFilter; -class GLESRenderEngine : public impl::RenderEngine { +class GLESRenderEngine : public RenderEngine { public: static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args); @@ -60,20 +60,15 @@ public: void primeCache() const override; void genTextures(size_t count, uint32_t* names) override; void deleteTextures(size_t count, uint32_t const* names) override; - void bindExternalTextureImage(uint32_t texName, const Image& image) override; - status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, - const sp<Fence>& fence) EXCLUDES(mRenderingMutex); void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex); - status_t bindFrameBuffer(Framebuffer* framebuffer) override; - void unbindFrameBuffer(Framebuffer* framebuffer) override; bool isProtected() const override { return mInProtectedContext; } bool supportsProtectedContent() const override; bool useProtectedContext(bool useProtectedContext) override; status_t drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* buffer, const bool useFramebufferCache, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; bool cleanupPostRender(CleanupMode mode) override; @@ -102,13 +97,15 @@ public: std::shared_ptr<ImageManager::Barrier> unbindExternalTextureBufferForTesting(uint64_t bufferId); protected: - Framebuffer* getFramebufferForDrawing() override; + Framebuffer* getFramebufferForDrawing(); void dump(std::string& result) override EXCLUDES(mRenderingMutex) EXCLUDES(mFramebufferImageCacheMutex); size_t getMaxTextureSize() const override; size_t getMaxViewportDims() const override; private: + friend class BindNativeBufferAsFramebuffer; + enum GlesVersion { GLES_VERSION_1_0 = 0x10000, GLES_VERSION_1_1 = 0x10001, @@ -133,6 +130,12 @@ private: status_t cacheExternalTextureBufferInternal(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); void unbindExternalTextureBufferInternal(uint64_t bufferId) EXCLUDES(mRenderingMutex); + status_t bindFrameBuffer(Framebuffer* framebuffer); + void unbindFrameBuffer(Framebuffer* framebuffer); + void bindExternalTextureImage(uint32_t texName, const Image& image); + void bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, + const sp<Fence>& fence) EXCLUDES(mRenderingMutex); + void cleanFramebufferCache() EXCLUDES(mFramebufferImageCacheMutex) override; // A data space is considered HDR data space if it has BT2020 color space // with PQ or HLG transfer function. @@ -228,6 +231,10 @@ private: // supports sRGB, DisplayP3 color spaces. const bool mUseColorManagement = false; + // Whether only shaders performing tone mapping from HDR to SDR will be generated on + // primeCache(). + const bool mPrecacheToneMapperShaderOnly = false; + // Cache of GL images that we'll store per GraphicBuffer ID std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex); std::unordered_map<uint32_t, std::optional<uint64_t>> mTextureView; diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h index ca16d2c727..a637796267 100644 --- a/libs/renderengine/include/renderengine/DisplaySettings.h +++ b/libs/renderengine/include/renderengine/DisplaySettings.h @@ -47,8 +47,8 @@ struct DisplaySettings { // DataSpace::UNKNOWN otherwise. ui::Dataspace outputDataspace = ui::Dataspace::UNKNOWN; - // Additional color transform to apply in linear space after transforming - // to the output dataspace. + // Additional color transform to apply after transforming to the output + // dataspace, in non-linear space. mat4 colorTransform = mat4(); // Region that will be cleared to (0, 0, 0, 1) prior to rendering. diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index 95e9367fea..d8d989e95d 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -21,6 +21,7 @@ #include <math/mat4.h> #include <math/vec3.h> #include <renderengine/Texture.h> +#include <ui/BlurRegion.h> #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> @@ -151,6 +152,8 @@ struct LayerSettings { ShadowSettings shadow; int backgroundBlurRadius = 0; + + std::vector<BlurRegion> blurRegions; }; // Keep in sync with custom comparison function in @@ -182,7 +185,29 @@ static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& r lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent; } +static inline bool operator==(const BlurRegion& lhs, const BlurRegion& rhs) { + return lhs.alpha == rhs.alpha && lhs.cornerRadiusTL == rhs.cornerRadiusTL && + lhs.cornerRadiusTR == rhs.cornerRadiusTR && lhs.cornerRadiusBL == rhs.cornerRadiusBL && + lhs.cornerRadiusBR == rhs.cornerRadiusBR && lhs.blurRadius == rhs.blurRadius && + lhs.left == rhs.left && lhs.top == rhs.top && lhs.right == rhs.right && + lhs.bottom == rhs.bottom; +} + +static inline bool operator!=(const BlurRegion& lhs, const BlurRegion& rhs) { + return !(lhs == rhs); +} + static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) { + if (lhs.blurRegions.size() != rhs.blurRegions.size()) { + return false; + } + const auto size = lhs.blurRegions.size(); + for (size_t i = 0; i < size; i++) { + if (lhs.blurRegions[i] != rhs.blurRegions[i]) { + return false; + } + } + return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha && lhs.sourceDataspace == rhs.sourceDataspace && lhs.colorTransform == rhs.colorTransform && diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 74bc44b22f..11b8e44e4f 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -44,12 +44,15 @@ class Region; namespace renderengine { -class BindNativeBufferAsFramebuffer; class Image; class Mesh; class Texture; struct RenderEngineCreationArgs; +namespace threaded { +class RenderEngineThreaded; +} + namespace impl { class RenderEngine; } @@ -67,7 +70,13 @@ public: HIGH = 3, }; - static std::unique_ptr<impl::RenderEngine> create(const RenderEngineCreationArgs& args); + enum class RenderEngineType { + GLES = 1, + THREADED = 2, + SKIA_GL = 3, + }; + + static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); virtual ~RenderEngine() = 0; @@ -80,16 +89,8 @@ public: // dump the extension strings. always call the base class. virtual void dump(std::string& result) = 0; - virtual bool useNativeFenceSync() const = 0; - virtual bool useWaitSync() const = 0; virtual void genTextures(size_t count, uint32_t* names) = 0; virtual void deleteTextures(size_t count, uint32_t const* names) = 0; - virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; - // Legacy public method used by devices that don't support native fence - // synchronization in their GPU driver, as this method provides implicit - // synchronization for latching buffers. - virtual status_t bindExternalTextureBuffer(uint32_t texName, const sp<GraphicBuffer>& buffer, - const sp<Fence>& fence) = 0; // Caches Image resources for this buffer, but does not bind the buffer to // a particular texture. // Note that work is deferred to an additional thread, i.e. this call @@ -107,10 +108,6 @@ public: // a buffer should never occur before binding the buffer if the caller // called {bind, cache}ExternalTextureBuffer before calling unbind. virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0; - // When binding a native buffer, it must be done before setViewportAndProjection - // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. - virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; - virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0; enum class CleanupMode { CLEAN_OUTPUT_RESOURCES, @@ -174,17 +171,12 @@ public: // now, this always returns NO_ERROR. virtual status_t drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, - ANativeWindowBuffer* buffer, const bool useFramebufferCache, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) = 0; + virtual void cleanFramebufferCache() = 0; protected: - // Gets a framebuffer to render to. This framebuffer may or may not be - // cached depending on the implementation. - // - // Note that this method does not transfer ownership, so the caller most not - // live longer than RenderEngine. - virtual Framebuffer* getFramebufferForDrawing() = 0; - friend class BindNativeBufferAsFramebuffer; + friend class threaded::RenderEngineThreaded; }; struct RenderEngineCreationArgs { @@ -195,26 +187,25 @@ struct RenderEngineCreationArgs { bool precacheToneMapperShaderOnly; bool supportsBackgroundBlur; RenderEngine::ContextPriority contextPriority; + RenderEngine::RenderEngineType renderEngineType; struct Builder; private: // must be created by Builder via constructor with full argument list - RenderEngineCreationArgs( - int _pixelFormat, - uint32_t _imageCacheSize, - bool _useColorManagement, - bool _enableProtectedContext, - bool _precacheToneMapperShaderOnly, - bool _supportsBackgroundBlur, - RenderEngine::ContextPriority _contextPriority) - : pixelFormat(_pixelFormat) - , imageCacheSize(_imageCacheSize) - , useColorManagement(_useColorManagement) - , enableProtectedContext(_enableProtectedContext) - , precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly) - , supportsBackgroundBlur(_supportsBackgroundBlur) - , contextPriority(_contextPriority) {} + RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize, bool _useColorManagement, + bool _enableProtectedContext, bool _precacheToneMapperShaderOnly, + bool _supportsBackgroundBlur, + RenderEngine::ContextPriority _contextPriority, + RenderEngine::RenderEngineType _renderEngineType) + : pixelFormat(_pixelFormat), + imageCacheSize(_imageCacheSize), + useColorManagement(_useColorManagement), + enableProtectedContext(_enableProtectedContext), + precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly), + supportsBackgroundBlur(_supportsBackgroundBlur), + contextPriority(_contextPriority), + renderEngineType(_renderEngineType) {} RenderEngineCreationArgs() = delete; }; @@ -249,10 +240,14 @@ struct RenderEngineCreationArgs::Builder { this->contextPriority = contextPriority; return *this; } + Builder& setRenderEngineType(RenderEngine::RenderEngineType renderEngineType) { + this->renderEngineType = renderEngineType; + return *this; + } RenderEngineCreationArgs build() const { return RenderEngineCreationArgs(pixelFormat, imageCacheSize, useColorManagement, enableProtectedContext, precacheToneMapperShaderOnly, - supportsBackgroundBlur, contextPriority); + supportsBackgroundBlur, contextPriority, renderEngineType); } private: @@ -264,46 +259,9 @@ private: bool precacheToneMapperShaderOnly = false; bool supportsBackgroundBlur = false; RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM; + RenderEngine::RenderEngineType renderEngineType = RenderEngine::RenderEngineType::GLES; }; -class BindNativeBufferAsFramebuffer { -public: - BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer, - const bool useFramebufferCache) - : mEngine(engine), mFramebuffer(mEngine.getFramebufferForDrawing()), mStatus(NO_ERROR) { - mStatus = mFramebuffer->setNativeWindowBuffer(buffer, mEngine.isProtected(), - useFramebufferCache) - ? mEngine.bindFrameBuffer(mFramebuffer) - : NO_MEMORY; - } - ~BindNativeBufferAsFramebuffer() { - mFramebuffer->setNativeWindowBuffer(nullptr, false, /*arbitrary*/ true); - mEngine.unbindFrameBuffer(mFramebuffer); - } - status_t getStatus() const { return mStatus; } - -private: - RenderEngine& mEngine; - Framebuffer* mFramebuffer; - status_t mStatus; -}; - -namespace impl { - -// impl::RenderEngine contains common implementation that is graphics back-end agnostic. -class RenderEngine : public renderengine::RenderEngine { -public: - virtual ~RenderEngine() = 0; - - bool useNativeFenceSync() const override; - bool useWaitSync() const override; - -protected: - RenderEngine(const RenderEngineCreationArgs& args); - const RenderEngineCreationArgs mArgs; -}; - -} // namespace impl } // namespace renderengine } // namespace android diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index 101cbb70fd..95ee9258a1 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -35,21 +35,12 @@ public: RenderEngine(); ~RenderEngine() override; - MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*()); MOCK_CONST_METHOD0(primeCache, void()); MOCK_METHOD1(dump, void(std::string&)); - MOCK_CONST_METHOD0(useNativeFenceSync, bool()); - MOCK_CONST_METHOD0(useWaitSync, bool()); - MOCK_CONST_METHOD0(isCurrent, bool()); MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); - MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); MOCK_METHOD1(cacheExternalTextureBuffer, void(const sp<GraphicBuffer>&)); - MOCK_METHOD3(bindExternalTextureBuffer, - status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&)); MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t)); - MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*)); - MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*)); MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&)); MOCK_CONST_METHOD0(getMaxTextureSize, size_t()); MOCK_CONST_METHOD0(getMaxViewportDims, size_t()); @@ -59,7 +50,9 @@ public: MOCK_METHOD1(cleanupPostRender, bool(CleanupMode mode)); MOCK_METHOD6(drawLayers, status_t(const DisplaySettings&, const std::vector<const LayerSettings*>&, - ANativeWindowBuffer*, const bool, base::unique_fd&&, base::unique_fd*)); + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, + base::unique_fd*)); + MOCK_METHOD0(cleanFramebufferCache, void()); }; } // namespace mock diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp new file mode 100644 index 0000000000..e5f753926f --- /dev/null +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -0,0 +1,824 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#include <cstdint> +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <sync/sync.h> +#include <ui/BlurRegion.h> +#include <ui/GraphicBuffer.h> +#include <utils/Trace.h> +#include "../gl/GLExtensions.h" +#include "SkiaGLRenderEngine.h" +#include "filters/BlurFilter.h" + +#include <GrContextOptions.h> +#include <SkCanvas.h> +#include <SkColorFilter.h> +#include <SkColorMatrix.h> +#include <SkColorSpace.h> +#include <SkImage.h> +#include <SkImageFilters.h> +#include <SkShadowUtils.h> +#include <SkSurface.h> +#include <gl/GrGLInterface.h> +#include <sync/sync.h> +#include <ui/GraphicBuffer.h> +#include <utils/Trace.h> + +#include <cmath> + +#include "../gl/GLExtensions.h" +#include "SkiaGLRenderEngine.h" +#include "filters/BlurFilter.h" + +extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); + +bool checkGlError(const char* op, int lineNumber); + +namespace android { +namespace renderengine { +namespace skia { + +static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute, + EGLint wanted, EGLConfig* outConfig) { + EGLint numConfigs = -1, n = 0; + eglGetConfigs(dpy, nullptr, 0, &numConfigs); + std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR); + eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n); + configs.resize(n); + + if (!configs.empty()) { + if (attribute != EGL_NONE) { + for (EGLConfig config : configs) { + EGLint value = 0; + eglGetConfigAttrib(dpy, config, attribute, &value); + if (wanted == value) { + *outConfig = config; + return NO_ERROR; + } + } + } else { + // just pick the first one + *outConfig = configs[0]; + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType, + EGLConfig* config) { + // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if + // it is to be used with WIFI displays + status_t err; + EGLint wantedAttribute; + EGLint wantedAttributeValue; + + std::vector<EGLint> attribs; + if (renderableType) { + const ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(format); + const bool is1010102 = pixelFormat == ui::PixelFormat::RGBA_1010102; + + // Default to 8 bits per channel. + const EGLint tmpAttribs[] = { + EGL_RENDERABLE_TYPE, + renderableType, + EGL_RECORDABLE_ANDROID, + EGL_TRUE, + EGL_SURFACE_TYPE, + EGL_WINDOW_BIT | EGL_PBUFFER_BIT, + EGL_FRAMEBUFFER_TARGET_ANDROID, + EGL_TRUE, + EGL_RED_SIZE, + is1010102 ? 10 : 8, + EGL_GREEN_SIZE, + is1010102 ? 10 : 8, + EGL_BLUE_SIZE, + is1010102 ? 10 : 8, + EGL_ALPHA_SIZE, + is1010102 ? 2 : 8, + EGL_NONE, + }; + std::copy(tmpAttribs, tmpAttribs + (sizeof(tmpAttribs) / sizeof(EGLint)), + std::back_inserter(attribs)); + wantedAttribute = EGL_NONE; + wantedAttributeValue = EGL_NONE; + } else { + // if no renderable type specified, fallback to a simplified query + wantedAttribute = EGL_NATIVE_VISUAL_ID; + wantedAttributeValue = format; + } + + err = selectConfigForAttribute(display, attribs.data(), wantedAttribute, wantedAttributeValue, + config); + if (err == NO_ERROR) { + EGLint caveat; + if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat)) + ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!"); + } + + return err; +} + +// Converts an android dataspace to a supported SkColorSpace +// Supported dataspaces are +// 1. sRGB +// 2. Display P3 +// 3. BT2020 PQ +// 4. BT2020 HLG +// Unknown primaries are mapped to BT709, and unknown transfer functions +// are mapped to sRGB. +static sk_sp<SkColorSpace> toColorSpace(ui::Dataspace dataspace) { + skcms_Matrix3x3 gamut; + switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { + case HAL_DATASPACE_STANDARD_BT709: + gamut = SkNamedGamut::kSRGB; + break; + case HAL_DATASPACE_STANDARD_BT2020: + gamut = SkNamedGamut::kRec2020; + break; + case HAL_DATASPACE_STANDARD_DCI_P3: + gamut = SkNamedGamut::kDisplayP3; + break; + default: + ALOGV("Unsupported Gamut: %d, defaulting to sRGB", dataspace); + gamut = SkNamedGamut::kSRGB; + break; + } + + switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { + case HAL_DATASPACE_TRANSFER_LINEAR: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut); + case HAL_DATASPACE_TRANSFER_SRGB: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut); + case HAL_DATASPACE_TRANSFER_ST2084: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut); + case HAL_DATASPACE_TRANSFER_HLG: + return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut); + default: + ALOGV("Unsupported Gamma: %d, defaulting to sRGB transfer", dataspace); + return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut); + } +} + +std::unique_ptr<SkiaGLRenderEngine> SkiaGLRenderEngine::create( + const RenderEngineCreationArgs& args) { + // initialize EGL for the default display + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!eglInitialize(display, nullptr, nullptr)) { + LOG_ALWAYS_FATAL("failed to initialize EGL"); + } + + const auto eglVersion = eglQueryStringImplementationANDROID(display, EGL_VERSION); + if (!eglVersion) { + checkGlError(__FUNCTION__, __LINE__); + LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_VERSION) failed"); + } + + const auto eglExtensions = eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS); + if (!eglExtensions) { + checkGlError(__FUNCTION__, __LINE__); + LOG_ALWAYS_FATAL("eglQueryStringImplementationANDROID(EGL_EXTENSIONS) failed"); + } + + auto& extensions = gl::GLExtensions::getInstance(); + extensions.initWithEGLStrings(eglVersion, eglExtensions); + + // The code assumes that ES2 or later is available if this extension is + // supported. + EGLConfig config = EGL_NO_CONFIG_KHR; + if (!extensions.hasNoConfigContext()) { + config = chooseEglConfig(display, args.pixelFormat, /*logConfig*/ true); + } + + bool useContextPriority = + extensions.hasContextPriority() && args.contextPriority == ContextPriority::HIGH; + EGLContext protectedContext = EGL_NO_CONTEXT; + if (args.enableProtectedContext && extensions.hasProtectedContent()) { + protectedContext = createEglContext(display, config, nullptr, useContextPriority, + Protection::PROTECTED); + ALOGE_IF(protectedContext == EGL_NO_CONTEXT, "Can't create protected context"); + } + + EGLContext ctxt = createEglContext(display, config, protectedContext, useContextPriority, + Protection::UNPROTECTED); + + // if can't create a GL context, we can only abort. + LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); + + EGLSurface placeholder = EGL_NO_SURFACE; + if (!extensions.hasSurfacelessContext()) { + placeholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat, + Protection::UNPROTECTED); + LOG_ALWAYS_FATAL_IF(placeholder == EGL_NO_SURFACE, "can't create placeholder pbuffer"); + } + EGLBoolean success = eglMakeCurrent(display, placeholder, placeholder, ctxt); + LOG_ALWAYS_FATAL_IF(!success, "can't make placeholder pbuffer current"); + extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), + glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); + + EGLSurface protectedPlaceholder = EGL_NO_SURFACE; + if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) { + protectedPlaceholder = createPlaceholderEglPbufferSurface(display, config, args.pixelFormat, + Protection::PROTECTED); + ALOGE_IF(protectedPlaceholder == EGL_NO_SURFACE, + "can't create protected placeholder pbuffer"); + } + + // initialize the renderer while GL is current + std::unique_ptr<SkiaGLRenderEngine> engine = + std::make_unique<SkiaGLRenderEngine>(args, display, config, ctxt, placeholder, + protectedContext, protectedPlaceholder); + + ALOGI("OpenGL ES informations:"); + ALOGI("vendor : %s", extensions.getVendor()); + ALOGI("renderer : %s", extensions.getRenderer()); + ALOGI("version : %s", extensions.getVersion()); + ALOGI("extensions: %s", extensions.getExtensions()); + ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize()); + ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims()); + + return engine; +} + +EGLConfig SkiaGLRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) { + status_t err; + EGLConfig config; + + // First try to get an ES3 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES3_BIT, &config); + if (err != NO_ERROR) { + // If ES3 fails, try to get an ES2 config + err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config); + if (err != NO_ERROR) { + // If ES2 still doesn't work, probably because we're on the emulator. + // try a simplified query + ALOGW("no suitable EGLConfig found, trying a simpler query"); + err = selectEGLConfig(display, format, 0, &config); + if (err != NO_ERROR) { + // this EGL is too lame for android + LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up"); + } + } + } + + if (logConfig) { + // print some debugging info + EGLint r, g, b, a; + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); + ALOGI("EGL information:"); + ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR)); + ALOGI("version : %s", eglQueryString(display, EGL_VERSION)); + ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS)); + ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported"); + ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); + } + + return config; +} + +SkiaGLRenderEngine::SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, + EGLConfig config, EGLContext ctxt, EGLSurface placeholder, + EGLContext protectedContext, EGLSurface protectedPlaceholder) + : mEGLDisplay(display), + mEGLConfig(config), + mEGLContext(ctxt), + mPlaceholderSurface(placeholder), + mProtectedEGLContext(protectedContext), + mProtectedPlaceholderSurface(protectedPlaceholder), + mUseColorManagement(args.useColorManagement) { + // Suppress unused field warnings for things we definitely will need/use + // These EGL fields will all be needed for toggling between protected & unprotected contexts + // Or we need different RE instances for that + (void)mEGLDisplay; + (void)mEGLConfig; + (void)mEGLContext; + (void)mPlaceholderSurface; + (void)mProtectedEGLContext; + (void)mProtectedPlaceholderSurface; + + sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); + LOG_ALWAYS_FATAL_IF(!glInterface.get()); + + GrContextOptions options; + options.fPreferExternalImagesOverES3 = true; + options.fDisableDistanceFieldPaths = true; + mGrContext = GrDirectContext::MakeGL(std::move(glInterface), options); + + if (args.supportsBackgroundBlur) { + mBlurFilter = new BlurFilter(); + } +} + +base::unique_fd SkiaGLRenderEngine::flush() { + ATRACE_CALL(); + if (!gl::GLExtensions::getInstance().hasNativeFenceSync()) { + return base::unique_fd(); + } + + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr); + if (sync == EGL_NO_SYNC_KHR) { + ALOGW("failed to create EGL native fence sync: %#x", eglGetError()); + return base::unique_fd(); + } + + // native fence fd will not be populated until flush() is done. + glFlush(); + + // get the fence fd + base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync)); + eglDestroySyncKHR(mEGLDisplay, sync); + if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + ALOGW("failed to dup EGL native fence sync: %#x", eglGetError()); + } + + return fenceFd; +} + +bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) { + if (!gl::GLExtensions::getInstance().hasNativeFenceSync() || + !gl::GLExtensions::getInstance().hasWaitSync()) { + return false; + } + + // release the fd and transfer the ownership to EGLSync + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE}; + EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); + return false; + } + + // XXX: The spec draft is inconsistent as to whether this should return an + // EGLint or void. Ignore the return value for now, as it's not strictly + // needed. + eglWaitSyncKHR(mEGLDisplay, sync, 0); + EGLint error = eglGetError(); + eglDestroySyncKHR(mEGLDisplay, sync); + if (error != EGL_SUCCESS) { + ALOGE("failed to wait for EGL native fence sync: %#x", error); + return false; + } + + return true; +} + +static bool hasUsage(const AHardwareBuffer_Desc& desc, uint64_t usage) { + return !!(desc.usage & usage); +} + +static float toDegrees(uint32_t transform) { + switch (transform) { + case ui::Transform::ROT_90: + return 90.0; + case ui::Transform::ROT_180: + return 180.0; + case ui::Transform::ROT_270: + return 270.0; + default: + return 0.0; + } +} + +static SkColorMatrix toSkColorMatrix(const mat4& matrix) { + return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1], + matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2], + matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3], + matrix[3][3], 0); +} + +void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + mImageCache.erase(bufferId); +} + +status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) { + ATRACE_NAME("SkiaGL::drawLayers"); + std::lock_guard<std::mutex> lock(mRenderingMutex); + if (layers.empty()) { + ALOGV("Drawing empty layer stack"); + return NO_ERROR; + } + + if (bufferFence.get() >= 0) { + // Duplicate the fence for passing to waitFence. + base::unique_fd bufferFenceDup(dup(bufferFence.get())); + if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) { + ATRACE_NAME("Waiting before draw"); + sync_wait(bufferFence.get(), -1); + } + } + if (buffer == nullptr) { + ALOGE("No output buffer provided. Aborting GPU composition."); + return BAD_VALUE; + } + + AHardwareBuffer_Desc bufferDesc; + AHardwareBuffer_describe(buffer->toAHardwareBuffer(), &bufferDesc); + + LOG_ALWAYS_FATAL_IF(!hasUsage(bufferDesc, AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE), + "missing usage"); + + sk_sp<SkSurface> surface; + if (useFramebufferCache) { + auto iter = mSurfaceCache.find(buffer->getId()); + if (iter != mSurfaceCache.end()) { + ALOGV("Cache hit!"); + surface = iter->second; + } + } + if (!surface) { + surface = SkSurface::MakeFromAHardwareBuffer(mGrContext.get(), buffer->toAHardwareBuffer(), + GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, + mUseColorManagement + ? toColorSpace(display.outputDataspace) + : SkColorSpace::MakeSRGB(), + nullptr); + if (useFramebufferCache && surface) { + ALOGD("Adding to cache"); + mSurfaceCache.insert({buffer->getId(), surface}); + } + } + if (!surface) { + ALOGE("Failed to make surface"); + return BAD_VALUE; + } + + auto canvas = surface->getCanvas(); + // Clear the entire canvas with a transparent black to prevent ghost images. + canvas->clear(SK_ColorTRANSPARENT); + canvas->save(); + + // 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); + for (const auto& layer : layers) { + SkPaint paint; + const auto& bounds = layer->geometry.boundaries; + const auto dest = getSkRect(bounds); + std::unordered_map<uint32_t, sk_sp<SkSurface>> cachedBlurs; + + if (mBlurFilter) { + if (layer->backgroundBlurRadius > 0) { + ATRACE_NAME("BackgroundBlur"); + auto blurredSurface = + mBlurFilter->draw(canvas, surface, layer->backgroundBlurRadius); + cachedBlurs[layer->backgroundBlurRadius] = blurredSurface; + } + if (layer->blurRegions.size() > 0) { + for (auto region : layer->blurRegions) { + if (cachedBlurs[region.blurRadius]) { + continue; + } + ATRACE_NAME("BlurRegion"); + auto blurredSurface = mBlurFilter->generate(canvas, surface, region.blurRadius); + cachedBlurs[region.blurRadius] = blurredSurface; + } + } + } + + if (layer->source.buffer.buffer) { + ATRACE_NAME("DrawImage"); + const auto& item = layer->source.buffer; + const auto bufferWidth = item.buffer->getBounds().width(); + const auto bufferHeight = item.buffer->getBounds().height(); + sk_sp<SkImage> image; + auto iter = mImageCache.find(item.buffer->getId()); + if (iter != mImageCache.end()) { + image = iter->second; + } else { + image = SkImage::MakeFromAHardwareBuffer(item.buffer->toAHardwareBuffer(), + item.usePremultipliedAlpha + ? kPremul_SkAlphaType + : kUnpremul_SkAlphaType, + mUseColorManagement + ? toColorSpace( + layer->sourceDataspace) + : SkColorSpace::MakeSRGB()); + mImageCache.insert({item.buffer->getId(), image}); + } + + SkMatrix matrix; + if (layer->geometry.roundedCornersRadius > 0) { + const auto roundedRect = getRoundedRect(layer); + matrix.setTranslate(roundedRect.getBounds().left() - dest.left(), + roundedRect.getBounds().top() - dest.top()); + } else { + matrix.setIdentity(); + } + + auto texMatrix = getSkM44(item.textureTransform).asM33(); + + // b/171404534, scale to fix the layer + matrix.postScale(bounds.getWidth() / bufferWidth, bounds.getHeight() / bufferHeight); + + // 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. + matrix.postScale(1.0f / bufferWidth, 1.0f / bufferHeight); + + auto rotatedBufferWidth = bufferWidth; + auto rotatedBufferHeight = bufferHeight; + + // Swap the buffer width and height if we're rotating, so that we + // scale back up by the correct factors post-rotation. + if (texMatrix.getSkewX() <= -0.5f || texMatrix.getSkewX() >= 0.5f) { + std::swap(rotatedBufferWidth, rotatedBufferHeight); + // TODO: clean this up. + // GLESRenderEngine specifies its texture coordinates in + // CW orientation under OpenGL conventions, when they probably should have + // been CCW instead. The net result is that orientation + // transforms are applied in the reverse + // direction to render the correct result, because SurfaceFlinger uses the inverse + // of the display transform to correct for that. But this means that + // the tex transform passed by SkiaGLRenderEngine will rotate + // individual layers in the reverse orientation. Hack around it + // by injected a 180 degree rotation, but ultimately this is + // a bug in how SurfaceFlinger invokes the RenderEngine + // interface, so the proper fix should live there, and GLESRenderEngine + // should be fixed accordingly. + matrix.postRotate(180, 0.5, 0.5); + } + + matrix.postConcat(texMatrix); + matrix.postScale(rotatedBufferWidth, rotatedBufferHeight); + paint.setShader(image->makeShader(matrix)); + } else { + ATRACE_NAME("DrawColor"); + const auto color = layer->source.solidColor; + paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r, + .fG = color.g, + .fB = color.b, + layer->alpha}, + nullptr)); + } + + paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform))); + + // Layers have a local transform matrix that should be applied to them. + canvas->save(); + canvas->concat(getSkM44(layer->geometry.positionTransform)); + + for (const auto effectRegion : layer->blurRegions) { + drawBlurRegion(canvas, effectRegion, dest, cachedBlurs[effectRegion.blurRadius]); + } + + if (layer->shadow.length > 0) { + const auto rect = layer->geometry.roundedCornersRadius > 0 + ? getSkRect(layer->geometry.roundedCornersCrop) + : dest; + drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow); + } + + if (layer->geometry.roundedCornersRadius > 0) { + canvas->drawRRect(getRoundedRect(layer), paint); + } else { + canvas->drawRect(dest, paint); + } + + canvas->restore(); + } + { + ATRACE_NAME("flush surface"); + surface->flush(); + } + canvas->restore(); + + if (drawFence != nullptr) { + *drawFence = flush(); + } + + // If flush failed or we don't support native fences, we need to force the + // gl command stream to be executed. + bool requireSync = drawFence == nullptr || drawFence->get() < 0; + if (requireSync) { + ATRACE_BEGIN("Submit(sync=true)"); + } else { + ATRACE_BEGIN("Submit(sync=false)"); + } + bool success = mGrContext->submit(requireSync); + ATRACE_END(); + if (!success) { + ALOGE("Failed to flush RenderEngine commands"); + // Chances are, something illegal happened (either the caller passed + // us bad parameters, or we messed up our shader generation). + return INVALID_OPERATION; + } + + // checkErrors(); + return NO_ERROR; +} + +inline SkRect SkiaGLRenderEngine::getSkRect(const FloatRect& rect) { + return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); +} + +inline SkRect SkiaGLRenderEngine::getSkRect(const Rect& rect) { + return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); +} + +inline SkRRect SkiaGLRenderEngine::getRoundedRect(const LayerSettings* layer) { + const auto rect = getSkRect(layer->geometry.roundedCornersCrop); + const auto cornerRadius = layer->geometry.roundedCornersRadius; + return SkRRect::MakeRectXY(rect, cornerRadius, cornerRadius); +} + +inline SkColor SkiaGLRenderEngine::getSkColor(const vec4& color) { + return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255); +} + +inline SkM44 SkiaGLRenderEngine::getSkM44(const mat4& matrix) { + return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], + matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1], + matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2], + matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]); +} + +inline SkPoint3 SkiaGLRenderEngine::getSkPoint3(const vec3& vector) { + return SkPoint3::Make(vector.x, vector.y, vector.z); +} + +size_t SkiaGLRenderEngine::getMaxTextureSize() const { + return mGrContext->maxTextureSize(); +} + +size_t SkiaGLRenderEngine::getMaxViewportDims() const { + return mGrContext->maxRenderTargetSize(); +} + +void SkiaGLRenderEngine::drawShadow(SkCanvas* canvas, const SkRect& casterRect, float cornerRadius, + const ShadowSettings& settings) { + ATRACE_CALL(); + const float casterZ = settings.length / 2.0f; + const auto shadowShape = cornerRadius > 0 + ? SkPath::RRect(SkRRect::MakeRectXY(casterRect, cornerRadius, cornerRadius)) + : SkPath::Rect(casterRect); + const auto flags = + settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag; + + SkShadowUtils::DrawShadow(canvas, shadowShape, SkPoint3::Make(0, 0, casterZ), + getSkPoint3(settings.lightPos), settings.lightRadius, + getSkColor(settings.ambientColor), getSkColor(settings.spotColor), + flags); +} + +void SkiaGLRenderEngine::drawBlurRegion(SkCanvas* canvas, const BlurRegion& effectRegion, + const SkRect& layerBoundaries, + sk_sp<SkSurface> blurredSurface) { + ATRACE_CALL(); + SkPaint paint; + paint.setAlpha(static_cast<int>(effectRegion.alpha * 255)); + const auto rect = SkRect::MakeLTRB(effectRegion.left, effectRegion.top, effectRegion.right, + effectRegion.bottom); + + const auto matrix = mBlurFilter->getShaderMatrix( + SkMatrix::MakeTrans(layerBoundaries.left(), layerBoundaries.top())); + paint.setShader(blurredSurface->makeImageSnapshot()->makeShader(matrix)); + + 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); + } +} + +EGLContext SkiaGLRenderEngine::createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, bool useContextPriority, + Protection protection) { + EGLint renderableType = 0; + if (config == EGL_NO_CONFIG_KHR) { + renderableType = EGL_OPENGL_ES3_BIT; + } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) { + LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE"); + } + EGLint contextClientVersion = 0; + if (renderableType & EGL_OPENGL_ES3_BIT) { + contextClientVersion = 3; + } else if (renderableType & EGL_OPENGL_ES2_BIT) { + contextClientVersion = 2; + } else if (renderableType & EGL_OPENGL_ES_BIT) { + contextClientVersion = 1; + } else { + LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs"); + } + + std::vector<EGLint> contextAttributes; + contextAttributes.reserve(7); + contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION); + contextAttributes.push_back(contextClientVersion); + if (useContextPriority) { + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG); + contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG); + } + if (protection == Protection::PROTECTED) { + contextAttributes.push_back(EGL_PROTECTED_CONTENT_EXT); + contextAttributes.push_back(EGL_TRUE); + } + contextAttributes.push_back(EGL_NONE); + + EGLContext context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + + if (contextClientVersion == 3 && context == EGL_NO_CONTEXT) { + // eglGetConfigAttrib indicated we can create GLES 3 context, but we failed, thus + // EGL_NO_CONTEXT so that we can abort. + if (config != EGL_NO_CONFIG_KHR) { + return context; + } + // If |config| is EGL_NO_CONFIG_KHR, we speculatively try to create GLES 3 context, so we + // should try to fall back to GLES 2. + contextAttributes[1] = 2; + context = eglCreateContext(display, config, shareContext, contextAttributes.data()); + } + + return context; +} + +EGLSurface SkiaGLRenderEngine::createPlaceholderEglPbufferSurface(EGLDisplay display, + EGLConfig config, int hwcFormat, + Protection protection) { + EGLConfig placeholderConfig = config; + if (placeholderConfig == EGL_NO_CONFIG_KHR) { + placeholderConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); + } + std::vector<EGLint> attributes; + attributes.reserve(7); + attributes.push_back(EGL_WIDTH); + attributes.push_back(1); + attributes.push_back(EGL_HEIGHT); + attributes.push_back(1); + if (protection == Protection::PROTECTED) { + attributes.push_back(EGL_PROTECTED_CONTENT_EXT); + attributes.push_back(EGL_TRUE); + } + attributes.push_back(EGL_NONE); + + return eglCreatePbufferSurface(display, placeholderConfig, attributes.data()); +} + +void SkiaGLRenderEngine::cleanFramebufferCache() { + mSurfaceCache.clear(); +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h new file mode 100644 index 0000000000..0143445251 --- /dev/null +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -0,0 +1,110 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_SKIAGLRENDERENGINE_H_ +#define SF_SKIAGLRENDERENGINE_H_ + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GrDirectContext.h> +#include <SkSurface.h> +#include <android-base/thread_annotations.h> +#include <renderengine/RenderEngine.h> +#include <sys/types.h> + +#include <mutex> +#include <unordered_map> + +#include "EGL/egl.h" +#include "SkiaRenderEngine.h" +#include "filters/BlurFilter.h" + +namespace android { +namespace renderengine { +namespace skia { + +class SkiaGLRenderEngine : public skia::SkiaRenderEngine { +public: + static std::unique_ptr<SkiaGLRenderEngine> create(const RenderEngineCreationArgs& args); + SkiaGLRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config, + EGLContext ctxt, EGLSurface placeholder, EGLContext protectedContext, + EGLSurface protectedPlaceholder); + ~SkiaGLRenderEngine() override{}; + + void unbindExternalTextureBuffer(uint64_t bufferId) override; + status_t drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; + void cleanFramebufferCache() override; + +protected: + void dump(std::string& /*result*/) override{}; + size_t getMaxTextureSize() const override; + size_t getMaxViewportDims() const override; + +private: + static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); + static EGLContext createEglContext(EGLDisplay display, EGLConfig config, + EGLContext shareContext, bool useContextPriority, + Protection protection); + static EGLSurface createPlaceholderEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection); + inline SkRect getSkRect(const FloatRect& layer); + inline SkRect getSkRect(const Rect& layer); + inline SkRRect getRoundedRect(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 drawShadow(SkCanvas* canvas, const SkRect& casterRect, float casterCornerRadius, + const ShadowSettings& shadowSettings); + void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerBounds, + sk_sp<SkSurface> blurrendSurface); + + EGLDisplay mEGLDisplay; + EGLConfig mEGLConfig; + EGLContext mEGLContext; + EGLSurface mPlaceholderSurface; + EGLContext mProtectedEGLContext; + EGLSurface mProtectedPlaceholderSurface; + BlurFilter* mBlurFilter = nullptr; + + const bool mUseColorManagement; + + // Cache of GL images that we'll store per GraphicBuffer ID + std::unordered_map<uint64_t, sk_sp<SkImage>> mImageCache GUARDED_BY(mRenderingMutex); + // Mutex guarding rendering operations, so that: + // 1. GL operations aren't interleaved, and + // 2. Internal state related to rendering that is potentially modified by + // multiple threads is guaranteed thread-safe. + std::mutex mRenderingMutex; + + sp<Fence> mLastDrawFence; + + sk_sp<GrDirectContext> mGrContext; + + std::unordered_map<uint64_t, sk_sp<SkSurface>> mSurfaceCache; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android + +#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file diff --git a/libs/ui/UiConfig.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 0ac863d718..81f0b6f970 100644 --- a/libs/ui/UiConfig.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,13 @@ * limitations under the License. */ -#include <ui/UiConfig.h> +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "RenderEngine" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS namespace android { - -void appendUiConfigString(std::string& configStr) { - static const char* config = - " [libui]"; - configStr.append(config); -} - - -}; // namespace android +namespace renderengine { +namespace skia {} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h new file mode 100644 index 0000000000..2352c7e59e --- /dev/null +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -0,0 +1,65 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SF_SKIARENDERENGINE_H_ +#define SF_SKIARENDERENGINE_H_ + +#include <renderengine/RenderEngine.h> +#include <sys/types.h> + +namespace android { + +namespace renderengine { + +class Mesh; +class Texture; + +namespace skia { + +class BlurFilter; + +// TODO: Put common skia stuff here that can be shared between the GL & Vulkan backends +// Currently mostly just handles all the no-op / missing APIs +class SkiaRenderEngine : public RenderEngine { +public: + static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args); + ~SkiaRenderEngine() override {} + + virtual void primeCache() const override{}; + virtual void genTextures(size_t /*count*/, uint32_t* /*names*/) override{}; + virtual void deleteTextures(size_t /*count*/, uint32_t const* /*names*/) override{}; + virtual void cacheExternalTextureBuffer(const sp<GraphicBuffer>& /*buffer*/){}; + virtual void unbindExternalTextureBuffer(uint64_t /*bufferId*/){}; + + virtual bool isProtected() const override { return false; } // mInProtectedContext; } + virtual bool supportsProtectedContent() const override { return false; }; + virtual bool useProtectedContext(bool /*useProtectedContext*/) override { return false; }; + virtual status_t drawLayers(const DisplaySettings& /*display*/, + const std::vector<const LayerSettings*>& /*layers*/, + const sp<GraphicBuffer>& /*buffer*/, + const bool /*useFramebufferCache*/, + base::unique_fd&& /*bufferFence*/, + base::unique_fd* /*drawFence*/) override { + return 0; + }; + virtual bool cleanupPostRender(CleanupMode) override { return true; }; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android + +#endif /* SF_GLESRENDERENGINE_H_ */
\ No newline at end of file diff --git a/libs/renderengine/skia/filters/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp new file mode 100644 index 0000000000..f6a316fa1d --- /dev/null +++ b/libs/renderengine/skia/filters/BlurFilter.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "BlurFilter.h" +#include <SkCanvas.h> +#include <SkData.h> +#include <SkPaint.h> +#include <SkRuntimeEffect.h> +#include <SkSize.h> +#include <SkString.h> +#include <SkSurface.h> +#include <log/log.h> +#include <utils/Trace.h> + +namespace android { +namespace renderengine { +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); + + float4 c = float4(sample(input, scaled_xy)); + c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, in_blurOffset.y))); + c += float4(sample(input, scaled_xy + float2( in_blurOffset.x, -in_blurOffset.y))); + c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, in_blurOffset.y))); + c += float4(sample(input, scaled_xy + float2(-in_blurOffset.x, -in_blurOffset.y))); + + return half4(c.rgb * 0.2, 1.0); + } + )"); + + auto [blurEffect, error] = SkRuntimeEffect::Make(blurString); + if (!blurEffect) { + LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str()); + } + mBlurEffect = std::move(blurEffect); +} + +sk_sp<SkSurface> BlurFilter::generate(SkCanvas* canvas, const sk_sp<SkSurface> input, + const uint32_t blurRadius) const { + ATRACE_CALL(); + + // 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. + float tmpRadius = (float)blurRadius / 6.0f; + float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius)); + float radiusByPasses = tmpRadius / (float)numberOfPasses; + + SkImageInfo scaledInfo = SkImageInfo::MakeN32Premul((float)input->width() * kInputScale, + (float)input->height() * kInputScale); + auto drawSurface = canvas->makeSurface(scaledInfo); + + const float stepX = radiusByPasses; + const float stepY = radiusByPasses; + + // start by drawing and downscaling and doing the first blur pass + SkFilterOptions linear = {SkSamplingMode::kLinear, SkMipmapMode::kNone}; + SkRuntimeShaderBuilder blurBuilder(mBlurEffect); + blurBuilder.child("input") = + input->makeImageSnapshot()->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear); + blurBuilder.uniform("in_inverseScale") = kInverseInputScale; + blurBuilder.uniform("in_blurOffset") = + SkV2{stepX * kInverseInputScale, stepY * kInverseInputScale}; + + { + // limit the lifetime of the input surface's snapshot to ensure that it goes out of + // scope before the surface is written into to avoid any copy-on-write behavior. + SkPaint paint; + paint.setShader(blurBuilder.makeShader(nullptr, false)); + paint.setFilterQuality(kLow_SkFilterQuality); + drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint); + blurBuilder.child("input") = nullptr; + } + + // And now we'll ping pong between our surfaces, to accumulate the result of various offsets. + auto lastDrawTarget = drawSurface; + if (numberOfPasses > 1) { + auto readSurface = drawSurface; + drawSurface = canvas->makeSurface(scaledInfo); + + for (auto i = 1; i < numberOfPasses; i++) { + const float stepScale = (float)i * kInputScale; + + blurBuilder.child("input") = + readSurface->makeImageSnapshot()->makeShader(SkTileMode::kClamp, + SkTileMode::kClamp, linear); + blurBuilder.uniform("in_inverseScale") = 1.0f; + blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale}; + + SkPaint paint; + paint.setShader(blurBuilder.makeShader(nullptr, false)); + paint.setFilterQuality(kLow_SkFilterQuality); + drawSurface->getCanvas()->drawIRect(scaledInfo.bounds(), paint); + + // Swap buffers for next iteration + const auto tmp = drawSurface; + drawSurface = readSurface; + readSurface = tmp; + blurBuilder.child("input") = nullptr; + } + lastDrawTarget = readSurface; + } + lastDrawTarget->flushAndSubmit(); + return lastDrawTarget; +} + +sk_sp<SkSurface> BlurFilter::draw(SkCanvas* canvas, const sk_sp<SkSurface> input, + const uint32_t blurRadius) const { + ATRACE_CALL(); + auto surface = generate(canvas, input, blurRadius); + + SkPaint paint; + const auto image = surface->makeImageSnapshot(); + paint.setShader(image->makeShader(SkMatrix::MakeScale(kInverseInputScale))); + paint.setFilterQuality(kLow_SkFilterQuality); + paint.setAlpha(std::min(1.0f, (float)blurRadius / kMaxCrossFadeRadius) * 255); + canvas->drawIRect(SkIRect::MakeWH(input->width(), input->height()), paint); + return surface; +} + +SkMatrix BlurFilter::getShaderMatrix(const SkMatrix& transformMatrix) const { + SkMatrix matrix; + matrix.setConcat(transformMatrix, SkMatrix::MakeScale(kInverseInputScale)); + return matrix; +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h new file mode 100644 index 0000000000..6f973d790e --- /dev/null +++ b/libs/renderengine/skia/filters/BlurFilter.h @@ -0,0 +1,65 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <SkCanvas.h> +#include <SkImage.h> +#include <SkRuntimeEffect.h> +#include <SkSurface.h> + +using namespace std; + +namespace android { +namespace renderengine { +namespace skia { + +/** + * This is an implementation of a Kawase blur, as described in here: + * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/ + * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf + */ +class BlurFilter { +public: + // Downsample FBO to improve performance + static constexpr float kInputScale = 0.25f; + // Downsample scale factor used to improve performance + static constexpr float kInverseInputScale = 1.0f / kInputScale; + // Maximum number of render passes + static constexpr uint32_t kMaxPasses = 4; + // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited + // image, up to this radius. + static constexpr float kMaxCrossFadeRadius = 30.0f; + + explicit BlurFilter(); + virtual ~BlurFilter(){}; + + // Execute blur, saving it to a texture + sk_sp<SkSurface> generate(SkCanvas* canvas, const sk_sp<SkSurface> input, + const uint32_t radius) const; + // Same as generate but also drawing to the screen + sk_sp<SkSurface> draw(SkCanvas* canvas, const sk_sp<SkSurface> input, + const uint32_t radius) const; + // Returns a matrix that should be applied to the blur shader + SkMatrix getShaderMatrix(const SkMatrix& transformMatrix) const; + +private: + sk_sp<SkRuntimeEffect> mBlurEffect; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index e98babc30c..bcf389b2c2 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -18,10 +18,12 @@ cc_test { test_suites: ["device-tests"], srcs: [ "RenderEngineTest.cpp", + "RenderEngineThreadedTest.cpp", ], static_libs: [ "libgmock", "librenderengine", + "librenderengine_mocks", ], shared_libs: [ "libbase", diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 0fec99b9b6..d795616b48 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -22,12 +22,13 @@ #include <condition_variable> #include <fstream> -#include <gtest/gtest.h> #include <cutils/properties.h> +#include <gtest/gtest.h> #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <ui/PixelFormat.h> #include "../gl/GLESRenderEngine.h" +#include "../threaded/RenderEngineThreaded.h" constexpr int DEFAULT_DISPLAY_WIDTH = 128; constexpr int DEFAULT_DISPLAY_HEIGHT = 256; @@ -40,14 +41,15 @@ struct RenderEngineTest : public ::testing::Test { static void SetUpTestSuite() { sRE = renderengine::gl::GLESRenderEngine::create( renderengine::RenderEngineCreationArgs::Builder() - .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) - .setImageCacheSize(1) - .setUseColorManagerment(false) - .setEnableProtectedContext(false) - .setPrecacheToneMapperShaderOnly(false) - .setSupportsBackgroundBlur(true) - .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) - .build()); + .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888)) + .setImageCacheSize(1) + .setUseColorManagerment(false) + .setEnableProtectedContext(false) + .setPrecacheToneMapperShaderOnly(false) + .setSupportsBackgroundBlur(true) + .setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM) + .setRenderEngineType(renderengine::RenderEngine::RenderEngineType::GLES) + .build()); } static void TearDownTestSuite() { @@ -253,8 +255,8 @@ struct RenderEngineTest : public ::testing::Test { std::vector<const renderengine::LayerSettings*> layers, sp<GraphicBuffer> buffer) { base::unique_fd fence; - status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), true, - base::unique_fd(), &fence); + status_t status = + sRE->drawLayers(settings, layers, buffer, true, base::unique_fd(), &fence); sCurrentBuffer = buffer; int fd = fence.release(); @@ -1005,8 +1007,7 @@ TEST_F(RenderEngineTest, drawLayers_nullOutputFence) { layer.alpha = 1.0; layers.push_back(&layer); - status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, - base::unique_fd(), nullptr); + status_t status = sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), nullptr); sCurrentBuffer = mBuffer; ASSERT_EQ(NO_ERROR, status); expectBufferColor(fullscreenRect(), 255, 0, 0, 255); @@ -1024,8 +1025,7 @@ TEST_F(RenderEngineTest, drawLayers_doesNotCacheFramebuffer) { layer.alpha = 1.0; layers.push_back(&layer); - status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), false, - base::unique_fd(), nullptr); + status_t status = sRE->drawLayers(settings, layers, mBuffer, false, base::unique_fd(), nullptr); sCurrentBuffer = mBuffer; ASSERT_EQ(NO_ERROR, status); ASSERT_FALSE(sRE->isFramebufferImageCachedForTesting(mBuffer->getId())); @@ -1242,31 +1242,6 @@ TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { EXPECT_EQ(NO_ERROR, barrier->result); } -TEST_F(RenderEngineTest, bindExternalBuffer_withNullBuffer) { - status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr); - ASSERT_EQ(BAD_VALUE, result); -} - -TEST_F(RenderEngineTest, bindExternalBuffer_cachesImages) { - sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); - uint32_t texName; - sRE->genTextures(1, &texName); - mTexNames.push_back(texName); - - sRE->bindExternalTextureBuffer(texName, buf, nullptr); - uint64_t bufferId = buf->getId(); - EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); - std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = - sRE->unbindExternalTextureBufferForTesting(bufferId); - std::lock_guard<std::mutex> lock(barrier->mutex); - ASSERT_TRUE(barrier->condition.wait_for(barrier->mutex, std::chrono::seconds(5), - [&]() REQUIRES(barrier->mutex) { - return barrier->isOpen; - })); - EXPECT_EQ(NO_ERROR, barrier->result); - EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); -} - TEST_F(RenderEngineTest, cacheExternalBuffer_withNullBuffer) { std::shared_ptr<renderengine::gl::ImageManager::Barrier> barrier = sRE->cacheExternalTextureBufferForTesting(nullptr); @@ -1415,11 +1390,9 @@ TEST_F(RenderEngineTest, cleanupPostRender_cleansUpOnce) { layers.push_back(&layer); base::unique_fd fenceOne; - sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), - &fenceOne); + sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fenceOne); base::unique_fd fenceTwo; - sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, std::move(fenceOne), - &fenceTwo); + sRE->drawLayers(settings, layers, mBuffer, true, std::move(fenceOne), &fenceTwo); const int fd = fenceTwo.get(); if (fd >= 0) { @@ -1445,7 +1418,7 @@ TEST_F(RenderEngineTest, cleanupPostRender_whenCleaningAll_replacesTextureMemory layers.push_back(&layer); base::unique_fd fence; - sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), true, base::unique_fd(), &fence); + sRE->drawLayers(settings, layers, mBuffer, true, base::unique_fd(), &fence); const int fd = fence.get(); if (fd >= 0) { diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp new file mode 100644 index 0000000000..ba5175d985 --- /dev/null +++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cutils/properties.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <renderengine/mock/RenderEngine.h> +#include "../threaded/RenderEngineThreaded.h" + +namespace android { + +using testing::_; +using testing::Eq; +using testing::Mock; +using testing::Return; + +struct RenderEngineThreadedTest : public ::testing::Test { + ~RenderEngineThreadedTest() {} + + void SetUp() override { + mThreadedRE = renderengine::threaded::RenderEngineThreaded::create( + [this]() { return std::unique_ptr<renderengine::RenderEngine>(mRenderEngine); }); + } + + std::unique_ptr<renderengine::threaded::RenderEngineThreaded> mThreadedRE; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); +}; + +TEST_F(RenderEngineThreadedTest, dump) { + std::string testString = "XYZ"; + EXPECT_CALL(*mRenderEngine, dump(_)); + mThreadedRE->dump(testString); +} + +TEST_F(RenderEngineThreadedTest, primeCache) { + EXPECT_CALL(*mRenderEngine, primeCache()); + mThreadedRE->primeCache(); +} + +TEST_F(RenderEngineThreadedTest, genTextures) { + uint32_t texName; + EXPECT_CALL(*mRenderEngine, genTextures(1, &texName)); + mThreadedRE->genTextures(1, &texName); +} + +TEST_F(RenderEngineThreadedTest, deleteTextures) { + uint32_t texName; + EXPECT_CALL(*mRenderEngine, deleteTextures(1, &texName)); + mThreadedRE->deleteTextures(1, &texName); +} + +TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) { + EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr))); + mThreadedRE->cacheExternalTextureBuffer(nullptr); +} + +TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) { + sp<GraphicBuffer> buf = new GraphicBuffer(); + EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf)); + mThreadedRE->cacheExternalTextureBuffer(buf); +} + +TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) { + EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0)); + mThreadedRE->unbindExternalTextureBuffer(0x0); +} + +TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) { + size_t size = 20; + EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size)); + size_t result = mThreadedRE->getMaxTextureSize(); + ASSERT_EQ(size, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns0) { + size_t size = 0; + EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size)); + size_t result = mThreadedRE->getMaxTextureSize(); + ASSERT_EQ(size, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns20) { + size_t dims = 20; + EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims)); + size_t result = mThreadedRE->getMaxViewportDims(); + ASSERT_EQ(dims, result); +} + +TEST_F(RenderEngineThreadedTest, getMaxViewportDims_returns0) { + size_t dims = 0; + EXPECT_CALL(*mRenderEngine, getMaxViewportDims()).WillOnce(Return(dims)); + size_t result = mThreadedRE->getMaxViewportDims(); + ASSERT_EQ(dims, result); +} + +TEST_F(RenderEngineThreadedTest, isProtected_returnsFalse) { + EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(false)); + status_t result = mThreadedRE->isProtected(); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, isProtected_returnsTrue) { + EXPECT_CALL(*mRenderEngine, isProtected()).WillOnce(Return(true)); + size_t result = mThreadedRE->isProtected(); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsFalse) { + EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(false)); + status_t result = mThreadedRE->supportsProtectedContent(); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, supportsProtectedContent_returnsTrue) { + EXPECT_CALL(*mRenderEngine, supportsProtectedContent()).WillOnce(Return(true)); + status_t result = mThreadedRE->supportsProtectedContent(); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsFalse) { + EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(false)); + status_t result = mThreadedRE->useProtectedContext(false); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, useProtectedContext_returnsTrue) { + EXPECT_CALL(*mRenderEngine, useProtectedContext(false)).WillOnce(Return(true)); + status_t result = mThreadedRE->useProtectedContext(false); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsFalse) { + EXPECT_CALL(*mRenderEngine, + cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL)) + .WillOnce(Return(false)); + status_t result = + mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL); + ASSERT_EQ(false, result); +} + +TEST_F(RenderEngineThreadedTest, cleanupPostRender_returnsTrue) { + EXPECT_CALL(*mRenderEngine, + cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL)) + .WillOnce(Return(true)); + status_t result = + mThreadedRE->cleanupPostRender(renderengine::RenderEngine::CleanupMode::CLEAN_ALL); + ASSERT_EQ(true, result); +} + +TEST_F(RenderEngineThreadedTest, drawLayers) { + renderengine::DisplaySettings settings; + std::vector<const renderengine::LayerSettings*> layers; + sp<GraphicBuffer> buffer = new GraphicBuffer(); + base::unique_fd bufferFence; + base::unique_fd drawFence; + + EXPECT_CALL(*mRenderEngine, drawLayers) + .WillOnce([](const renderengine::DisplaySettings&, + const std::vector<const renderengine::LayerSettings*>&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, + base::unique_fd*) -> status_t { return NO_ERROR; }); + + status_t result = mThreadedRE->drawLayers(settings, layers, buffer, false, + std::move(bufferFence), &drawFence); + ASSERT_EQ(NO_ERROR, result); +} + +} // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp new file mode 100644 index 0000000000..5453302428 --- /dev/null +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -0,0 +1,309 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "RenderEngineThreaded.h" + +#include <sched.h> +#include <chrono> +#include <future> + +#include <android-base/stringprintf.h> +#include <private/gui/SyncFeatures.h> +#include <utils/Trace.h> + +#include "gl/GLESRenderEngine.h" + +using namespace std::chrono_literals; + +namespace android { +namespace renderengine { +namespace threaded { + +std::unique_ptr<RenderEngineThreaded> RenderEngineThreaded::create(CreateInstanceFactory factory) { + return std::make_unique<RenderEngineThreaded>(std::move(factory)); +} + +RenderEngineThreaded::RenderEngineThreaded(CreateInstanceFactory factory) { + ATRACE_CALL(); + + std::lock_guard lockThread(mThreadMutex); + mThread = std::thread(&RenderEngineThreaded::threadMain, this, factory); +} + +RenderEngineThreaded::~RenderEngineThreaded() { + { + std::lock_guard lock(mThreadMutex); + mRunning = false; + mCondition.notify_one(); + } + + if (mThread.joinable()) { + mThread.join(); + } +} + +// NO_THREAD_SAFETY_ANALYSIS is because std::unique_lock presently lacks thread safety annotations. +void RenderEngineThreaded::threadMain(CreateInstanceFactory factory) NO_THREAD_SAFETY_ANALYSIS { + ATRACE_CALL(); + + struct sched_param param = {0}; + param.sched_priority = 2; + if (sched_setscheduler(0, SCHED_FIFO, ¶m) != 0) { + ALOGE("Couldn't set SCHED_FIFO"); + } + + mRenderEngine = factory(); + + std::unique_lock<std::mutex> lock(mThreadMutex); + pthread_setname_np(pthread_self(), mThreadName); + + while (mRunning) { + if (!mFunctionCalls.empty()) { + auto task = mFunctionCalls.front(); + mFunctionCalls.pop(); + task(*mRenderEngine); + } + mCondition.wait(lock, [this]() REQUIRES(mThreadMutex) { + return !mRunning || !mFunctionCalls.empty(); + }); + } +} + +void RenderEngineThreaded::primeCache() const { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::primeCache"); + instance.primeCache(); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::dump(std::string& result) { + std::promise<std::string> resultPromise; + std::future<std::string> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &result](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::dump"); + std::string localResult = result; + instance.dump(localResult); + resultPromise.set_value(std::move(localResult)); + }); + } + mCondition.notify_one(); + // Note: This is an rvalue. + result.assign(resultFuture.get()); +} + +void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, count, names](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::genTextures"); + instance.genTextures(count, names); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, count, &names](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::deleteTextures"); + instance.deleteTextures(count, names); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::cacheExternalTextureBuffer"); + instance.cacheExternalTextureBuffer(buffer); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::unbindExternalTextureBuffer"); + instance.unbindExternalTextureBuffer(bufferId); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +size_t RenderEngineThreaded::getMaxTextureSize() const { + std::promise<size_t> resultPromise; + std::future<size_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::getMaxTextureSize"); + size_t size = instance.getMaxTextureSize(); + resultPromise.set_value(size); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +size_t RenderEngineThreaded::getMaxViewportDims() const { + std::promise<size_t> resultPromise; + std::future<size_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::getMaxViewportDims"); + size_t size = instance.getMaxViewportDims(); + resultPromise.set_value(size); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::isProtected() const { + 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::isProtected"); + bool returnValue = instance.isProtected(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::supportsProtectedContent() const { + 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::supportsProtectedContent"); + bool returnValue = instance.supportsProtectedContent(); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::useProtectedContext(bool useProtectedContext) { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push( + [&resultPromise, useProtectedContext](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::useProtectedContext"); + bool returnValue = instance.useProtectedContext(useProtectedContext); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +bool RenderEngineThreaded::cleanupPostRender(CleanupMode mode) { + std::promise<bool> resultPromise; + std::future<bool> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, mode](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::cleanupPostRender"); + bool returnValue = instance.cleanupPostRender(mode); + resultPromise.set_value(returnValue); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +status_t RenderEngineThreaded::drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, + const bool useFramebufferCache, + base::unique_fd&& bufferFence, + base::unique_fd* drawFence) { + std::promise<status_t> resultPromise; + std::future<status_t> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise, &display, &layers, &buffer, useFramebufferCache, + &bufferFence, &drawFence](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::drawLayers"); + status_t status = instance.drawLayers(display, layers, buffer, useFramebufferCache, + std::move(bufferFence), drawFence); + resultPromise.set_value(status); + }); + } + mCondition.notify_one(); + return resultFuture.get(); +} + +void RenderEngineThreaded::cleanFramebufferCache() { + std::promise<void> resultPromise; + std::future<void> resultFuture = resultPromise.get_future(); + { + std::lock_guard lock(mThreadMutex); + mFunctionCalls.push([&resultPromise](renderengine::RenderEngine& instance) { + ATRACE_NAME("REThreaded::cleanFramebufferCache"); + instance.cleanFramebufferCache(); + resultPromise.set_value(); + }); + } + mCondition.notify_one(); + resultFuture.wait(); +} + +} // namespace threaded +} // namespace renderengine +} // namespace android diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h new file mode 100644 index 0000000000..cdfbd04872 --- /dev/null +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -0,0 +1,89 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/thread_annotations.h> +#include <condition_variable> +#include <mutex> +#include <queue> +#include <thread> + +#include "renderengine/RenderEngine.h" + +namespace android { +namespace renderengine { +namespace threaded { + +using CreateInstanceFactory = std::function<std::unique_ptr<renderengine::RenderEngine>()>; + +/** + * This class extends a basic RenderEngine class. It contains a thread. Each time a function of + * this class is called, we create a lambda function that is put on a queue. The main thread then + * executes the functions in order. + */ +class RenderEngineThreaded : public RenderEngine { +public: + static std::unique_ptr<RenderEngineThreaded> create(CreateInstanceFactory factory); + + RenderEngineThreaded(CreateInstanceFactory factory); + ~RenderEngineThreaded() override; + void primeCache() const override; + + void dump(std::string& result) override; + + void genTextures(size_t count, uint32_t* names) override; + void deleteTextures(size_t count, uint32_t const* names) override; + void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; + void unbindExternalTextureBuffer(uint64_t bufferId) override; + size_t getMaxTextureSize() const override; + size_t getMaxViewportDims() const override; + + bool isProtected() const override; + bool supportsProtectedContent() const override; + bool useProtectedContext(bool useProtectedContext) override; + bool cleanupPostRender(CleanupMode mode) override; + + status_t drawLayers(const DisplaySettings& display, + const std::vector<const LayerSettings*>& layers, + const sp<GraphicBuffer>& buffer, const bool useFramebufferCache, + base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; + + void cleanFramebufferCache() override; + +private: + void threadMain(CreateInstanceFactory factory); + + /* ------------------------------------------------------------------------ + * Threading + */ + const char* const mThreadName = "RenderEngineThread"; + // Protects the creation and destruction of mThread. + mutable std::mutex mThreadMutex; + std::thread mThread GUARDED_BY(mThreadMutex); + bool mRunning GUARDED_BY(mThreadMutex) = true; + mutable std::queue<std::function<void(renderengine::RenderEngine& instance)>> mFunctionCalls + GUARDED_BY(mThreadMutex); + mutable std::condition_variable mCondition; + + /* ------------------------------------------------------------------------ + * Render Engine + */ + std::unique_ptr<renderengine::RenderEngine> mRenderEngine; +}; +} // namespace threaded +} // namespace renderengine +} // namespace android diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index 8ed09f8ff0..a6b0aaf0b5 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -216,14 +216,25 @@ status_t BnSensorServer::onTransact( int32_t type; Vector<float> floats; Vector<int32_t> ints; + uint32_t count; handle = data.readInt32(); type = data.readInt32(); - floats.resize(data.readUint32()); + + count = data.readUint32(); + if (count > (data.dataAvail() / sizeof(float))) { + return BAD_VALUE; + } + floats.resize(count); for (auto &i : floats) { i = data.readFloat(); } - ints.resize(data.readUint32()); + + count = data.readUint32(); + if (count > (data.dataAvail() / sizeof(int32_t))) { + return BAD_VALUE; + } + ints.resize(count); for (auto &i : ints) { i = data.readInt32(); } diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 47eb59fb0a..7c68aaa389 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -12,6 +12,74 @@ // See the License for the specific language governing permissions and // limitations under the License. +cc_defaults { + name: "libui-defaults", + clang: true, + cflags: [ + "-Wall", + "-Werror", + ], + cppflags: [ + "-Wextra", + ], + + sanitize: { + integer_overflow: true, + misc_undefined: ["bounds"], + }, + +} + +cc_library_static { + name: "libui-types", + vendor_available: true, + host_supported: true, + target: { + windows: { + enabled: true, + } + }, + + defaults: [ + "libui-defaults", + ], + + apex_available: [ + "//apex_available:anyapex", + "//apex_available:platform", + ], + min_sdk_version: "apex_inherit", + + shared_libs: [ + "libbase", + "libutils", + ], + + static_libs: [ + "libarect", + "libmath", + ], + + srcs: [ + "ColorSpace.cpp", + "Rect.cpp", + "Region.cpp", + "Transform.cpp", + ], + + export_include_dirs: [ + "include", + "include_private", + "include_types", + ], + + export_static_lib_headers: [ + "libarect", + "libmath", + ], + +} + cc_library_shared { name: "libui", vendor_available: true, @@ -35,8 +103,9 @@ cc_library_shared { }, srcs: [ - "ColorSpace.cpp", "DebugUtils.cpp", + "DeviceProductInfo.cpp", + "DisplayInfo.cpp", "Fence.cpp", "FenceTime.cpp", "FrameStats.cpp", @@ -50,11 +119,7 @@ cc_library_shared { "HdrCapabilities.cpp", "PixelFormat.cpp", "PublicFormat.cpp", - "Rect.cpp", - "Region.cpp", "Size.cpp", - "Transform.cpp", - "UiConfig.cpp", ], include_dirs: [ @@ -65,8 +130,11 @@ cc_library_shared { "include_private", ], - // Uncomment the following line to enable VALIDATE_REGIONS traces - //defaults: ["libui-validate-regions-defaults"], + defaults: [ + "libui-defaults", + // Uncomment the following line to enable VALIDATE_REGIONS traces + //defaults: ["libui-validate-regions-defaults"], + ], shared_libs: [ "android.hardware.graphics.allocator@2.0", @@ -100,6 +168,10 @@ cc_library_shared { "libmath", ], + whole_static_libs: [ + "libui-types", + ], + // bufferhub is not used when building libgui for vendors target: { vendor: { @@ -171,6 +243,8 @@ filegroup { name: "libui_host_common", srcs: [ "Rect.cpp", - "PixelFormat.cpp" + "Region.cpp", + "PixelFormat.cpp", + "Transform.cpp" ], } diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index f394635aa2..1f006ceb69 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -321,10 +321,6 @@ std::string decodeRenderIntent(RenderIntent renderIntent) { return std::string("Unknown RenderIntent"); } -std::string to_string(const android::Rect& rect) { - return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom); -} - std::string toString(const android::DeviceProductInfo::ManufactureOrModelDate& date) { using ModelYear = android::DeviceProductInfo::ModelYear; using ManufactureYear = android::DeviceProductInfo::ManufactureYear; diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp new file mode 100644 index 0000000000..4d6ce4306a --- /dev/null +++ b/libs/ui/DeviceProductInfo.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/DeviceProductInfo.h> + +#include <android-base/stringprintf.h> +#include <ui/FlattenableHelpers.h> +#include <utils/Log.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android { + +using base::StringAppendF; + +size_t DeviceProductInfo::getFlattenedSize() const { + return FlattenableHelpers::getFlattenedSize(name) + + FlattenableHelpers::getFlattenedSize(manufacturerPnpId) + + FlattenableHelpers::getFlattenedSize(productId) + + FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) + + FlattenableHelpers::getFlattenedSize(relativeAddress); +} + +status_t DeviceProductInfo::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress)); + return OK; +} + +status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) { + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress)); + return OK; +} + +void DeviceProductInfo::dump(std::string& result) const { + StringAppendF(&result, "{name=%s, ", name.c_str()); + StringAppendF(&result, "manufacturerPnpId=%s, ", manufacturerPnpId.data()); + StringAppendF(&result, "productId=%s, ", productId.c_str()); + + if (const auto* model = std::get_if<ModelYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "modelYear=%u, ", model->year); + } else if (const auto* manufactureWeekAndYear = + std::get_if<ManufactureWeekAndYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "manufactureWeek=%u, ", manufactureWeekAndYear->week); + StringAppendF(&result, "manufactureYear=%d, ", manufactureWeekAndYear->year); + } else if (const auto* manufactureYear = + std::get_if<ManufactureYear>(&manufactureOrModelDate)) { + StringAppendF(&result, "manufactureYear=%d, ", manufactureYear->year); + } else { + ALOGE("Unknown alternative for variant DeviceProductInfo::ManufactureOrModelDate"); + } + + result.append("relativeAddress=["); + for (size_t i = 0; i < relativeAddress.size(); i++) { + if (i != 0) { + result.append(", "); + } + StringAppendF(&result, "%u", relativeAddress[i]); + } + result.append("]}"); +} + +} // namespace android diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/DisplayInfo.cpp new file mode 100644 index 0000000000..73a78af186 --- /dev/null +++ b/libs/ui/DisplayInfo.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/DisplayInfo.h> + +#include <cstdint> + +#include <ui/FlattenableHelpers.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +namespace android { + +size_t DisplayInfo::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 { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure)); + RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo)); + return OK; +} + +status_t DisplayInfo::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)); + RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo)); + return OK; +} + +} // namespace android diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 943d13ec68..91d2d58df7 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -83,20 +83,17 @@ void GraphicBufferAllocator::dump(std::string& result, bool less) const { KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); uint64_t total = 0; result.append("GraphicBufferAllocator buffers:\n"); - const size_t c = list.size(); - for (size_t i=0 ; i<c ; i++) { + const size_t count = list.size(); + StringAppendF(&result, "%10s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size", + "W (Stride) x H", "Layers", "Format", "Usage", "Requestor"); + for (size_t i = 0; i < count; i++) { const alloc_rec_t& rec(list.valueAt(i)); - if (rec.size) { - StringAppendF(&result, - "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", - list.keyAt(i), static_cast<double>(rec.size) / 1024.0, rec.width, rec.stride, rec.height, - rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str()); - } else { - StringAppendF(&result, - "%10p: unknown | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", - list.keyAt(i), rec.width, rec.stride, rec.height, rec.layerCount, - rec.format, rec.usage, rec.requestorName.c_str()); - } + std::string sizeStr = (rec.size) + ? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0) + : "unknown"; + StringAppendF(&result, "%10p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n", + list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height, + rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str()); total += rec.size; } StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n", diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp index 70e3ce768c..a6595cfc27 100644 --- a/libs/ui/PublicFormat.cpp +++ b/libs/ui/PublicFormat.cpp @@ -35,6 +35,8 @@ int mapPublicFormatToHalFormat(PublicFormat f) { case PublicFormat::RAW_SENSOR: case PublicFormat::RAW_DEPTH: return HAL_PIXEL_FORMAT_RAW16; + case PublicFormat::RAW_DEPTH10: + return HAL_PIXEL_FORMAT_RAW10; default: // Most formats map 1:1 return static_cast<int>(f); @@ -50,6 +52,7 @@ android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { case PublicFormat::DEPTH_POINT_CLOUD: case PublicFormat::DEPTH16: case PublicFormat::RAW_DEPTH: + case PublicFormat::RAW_DEPTH10: dataspace = Dataspace::DEPTH; break; case PublicFormat::RAW_SENSOR: @@ -80,6 +83,13 @@ android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) { Dataspace ds = static_cast<Dataspace>(dataSpace); switch (format) { + case HAL_PIXEL_FORMAT_RAW10: + switch (ds) { + case Dataspace::DEPTH: + return PublicFormat::RAW_DEPTH10; + default: + return PublicFormat::RAW10; + } case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: case HAL_PIXEL_FORMAT_RGBA_FP16: @@ -87,7 +97,6 @@ PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace d case HAL_PIXEL_FORMAT_RGB_888: case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_Y8: - case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW12: case HAL_PIXEL_FORMAT_YCbCr_420_888: case HAL_PIXEL_FORMAT_YV12: diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index 13fed3a239..a8d6285169 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android-base/stringprintf.h> #include <system/graphics.h> #include <ui/Rect.h> @@ -149,4 +150,13 @@ Rect Rect::reduce(const Rect& exclude) const { return result; } +std::string to_string(const android::Rect& rect) { + return android::base::StringPrintf("Rect(%d, %d, %d, %d)", rect.left, rect.top, rect.right, + rect.bottom); +} + +void PrintTo(const Rect& rect, ::std::ostream* os) { + *os << to_string(rect); +} + }; // namespace android diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp index 06b6bfe797..cd68c1c0ec 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#undef LOG_TAG +#define LOG_TAG "Transform" + #include <math.h> #include <android-base/stringprintf.h> @@ -22,8 +25,7 @@ #include <ui/Transform.h> #include <utils/String8.h> -namespace android { -namespace ui { +namespace android::ui { Transform::Transform() { reset(); @@ -55,11 +57,9 @@ bool Transform::operator==(const Transform& other) const { mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] && mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] && mMatrix[2][2] == other.mMatrix[2][2]; - ; } -Transform Transform::operator * (const Transform& rhs) const -{ +Transform Transform::operator*(const Transform& rhs) const { if (CC_LIKELY(mType == IDENTITY)) return rhs; @@ -87,6 +87,19 @@ Transform Transform::operator * (const Transform& rhs) const return r; } +Transform Transform::operator * (float value) const { + Transform r(*this); + const mat33& M(mMatrix); + mat33& R(r.mMatrix); + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < 2; j++) { + R[i][j] = M[i][j] * value; + } + } + r.type(); + return r; +} + Transform& Transform::operator=(const Transform& other) { mMatrix = other.mMatrix; mType = other.mType; @@ -105,14 +118,30 @@ float Transform::ty() const { return mMatrix[2][1]; } -float Transform::sx() const { +float Transform::dsdx() const { return mMatrix[0][0]; } -float Transform::sy() const { +float Transform::dtdx() const { + return mMatrix[1][0]; +} + +float Transform::dtdy() const { + return mMatrix[0][1]; +} + +float Transform::dsdy() const { return mMatrix[1][1]; } +float Transform::getScaleX() const { + return sqrt((dsdx() * dsdx()) + (dtdx() * dtdx())); +} + +float Transform::getScaleY() const { + return sqrt((dtdy() * dtdy()) + (dsdy() * dsdy())); +} + void Transform::reset() { mType = IDENTITY; for(size_t i = 0; i < 3; i++) { @@ -122,8 +151,7 @@ void Transform::reset() { } } -void Transform::set(float tx, float ty) -{ +void Transform::set(float tx, float ty) { mMatrix[2][0] = tx; mMatrix[2][1] = ty; mMatrix[2][2] = 1.0f; @@ -135,8 +163,7 @@ void Transform::set(float tx, float ty) } } -void Transform::set(float a, float b, float c, float d) -{ +void Transform::set(float a, float b, float c, float d) { mat33& M(mMatrix); M[0][0] = a; M[1][0] = b; M[0][1] = c; M[1][1] = d; @@ -144,8 +171,7 @@ void Transform::set(float a, float b, float c, float d) mType = UNKNOWN_TYPE; } -status_t Transform::set(uint32_t flags, float w, float h) -{ +status_t Transform::set(uint32_t flags, float w, float h) { if (flags & ROT_INVALID) { // that's not allowed! reset(); @@ -187,6 +213,15 @@ status_t Transform::set(uint32_t flags, float w, float h) return NO_ERROR; } +void Transform::set(const std::array<float, 9>& matrix) { + mat33& M(mMatrix); + M[0][0] = matrix[0]; M[1][0] = matrix[1]; M[2][0] = matrix[2]; + M[0][1] = matrix[3]; M[1][1] = matrix[4]; M[2][1] = matrix[5]; + M[0][2] = matrix[6]; M[1][2] = matrix[7]; M[2][2] = matrix[8]; + mType = UNKNOWN_TYPE; + type(); +} + vec2 Transform::transform(const vec2& v) const { vec2 r; const mat33& M(mMatrix); @@ -204,18 +239,15 @@ vec3 Transform::transform(const vec3& v) const { return r; } -vec2 Transform::transform(int x, int y) const -{ - return transform(vec2(x,y)); +vec2 Transform::transform(float x, float y) const { + return transform(vec2(x, y)); } -Rect Transform::makeBounds(int w, int h) const -{ +Rect Transform::makeBounds(int w, int h) const { return transform( Rect(w, h) ); } -Rect Transform::transform(const Rect& bounds, bool roundOutwards) const -{ +Rect Transform::transform(const Rect& bounds, bool roundOutwards) const { Rect r; vec2 lt( bounds.left, bounds.top ); vec2 rt( bounds.right, bounds.top ); @@ -242,8 +274,7 @@ Rect Transform::transform(const Rect& bounds, bool roundOutwards) const return r; } -FloatRect Transform::transform(const FloatRect& bounds) const -{ +FloatRect Transform::transform(const FloatRect& bounds) const { vec2 lt(bounds.left, bounds.top); vec2 rt(bounds.right, bounds.top); vec2 lb(bounds.left, bounds.bottom); @@ -263,8 +294,7 @@ FloatRect Transform::transform(const FloatRect& bounds) const return r; } -Region Transform::transform(const Region& reg) const -{ +Region Transform::transform(const Region& reg) const { Region out; if (CC_UNLIKELY(type() > TRANSLATE)) { if (CC_LIKELY(preserveRects())) { @@ -284,8 +314,7 @@ Region Transform::transform(const Region& reg) const return out; } -uint32_t Transform::type() const -{ +uint32_t Transform::type() const { if (mType & UNKNOWN_TYPE) { // recompute what this transform is @@ -380,16 +409,18 @@ uint32_t Transform::getType() const { return type() & 0xFF; } -uint32_t Transform::getOrientation() const -{ +uint32_t Transform::getOrientation() const { return (type() >> 8) & 0xFF; } -bool Transform::preserveRects() const -{ +bool Transform::preserveRects() const { return (getOrientation() & ROT_INVALID) ? false : true; } +bool Transform::needsBilinearFiltering() const { + return (!preserveRects() || getType() >= ui::Transform::SCALE); +} + mat4 Transform::asMatrix4() const { // Internally Transform uses a 3x3 matrix since the transform is meant for // two-dimensional values. An equivalent 4x4 matrix means inserting an extra @@ -421,7 +452,43 @@ mat4 Transform::asMatrix4() const { return m; } -void Transform::dump(std::string& out, const char* name) const { +static std::string rotationToString(const uint32_t rotationFlags) { + switch (rotationFlags) { + case Transform::ROT_0: + return "ROT_0"; + case Transform::FLIP_H: + return "FLIP_H"; + case Transform::FLIP_V: + return "FLIP_V"; + case Transform::ROT_90: + return "ROT_90"; + case Transform::ROT_180: + return "ROT_180"; + case Transform::ROT_270: + return "ROT_270"; + case Transform::ROT_INVALID: + default: + return "ROT_INVALID"; + } +} + +static std::string transformToString(const uint32_t transform) { + if (transform == Transform::IDENTITY) { + return "IDENTITY"; + } + + if (transform == Transform::UNKNOWN) { + return "UNKNOWN"; + } + + std::string out; + if (transform & Transform::SCALE) out.append("SCALE "); + if (transform & Transform::ROTATE) out.append("ROTATE "); + if (transform & Transform::TRANSLATE) out.append("TRANSLATE"); + return out; +} + +void Transform::dump(std::string& out, const char* name, const char* prefix) const { using android::base::StringAppendF; type(); // Ensure the information in mType is up to date @@ -429,40 +496,34 @@ void Transform::dump(std::string& out, const char* name) const { const uint32_t type = mType; const uint32_t orient = type >> 8; - StringAppendF(&out, "%s 0x%08x (", name, orient); + out += prefix; + out += name; + out += " "; if (orient & ROT_INVALID) { - out.append("ROT_INVALID "); - } else { - if (orient & ROT_90) { - out.append("ROT_90 "); - } else { - out.append("ROT_0 "); - } - if (orient & FLIP_V) out.append("FLIP_V "); - if (orient & FLIP_H) out.append("FLIP_H "); + StringAppendF(&out, "0x%08x ", orient); } + out += "(" + rotationToString(orient) + ") "; - StringAppendF(&out, ") 0x%02x (", type); - - if (!(type & (SCALE | ROTATE | TRANSLATE))) out.append("IDENTITY "); - if (type & SCALE) out.append("SCALE "); - if (type & ROTATE) out.append("ROTATE "); - if (type & TRANSLATE) out.append("TRANSLATE "); + if (type & UNKNOWN) { + StringAppendF(&out, "0x%02x ", type); + } + out += "(" + transformToString(type) + ")\n"; - out.append(")\n"); + if (type == IDENTITY) { + return; + } for (size_t i = 0; i < 3; i++) { - StringAppendF(&out, " %.4f %.4f %.4f\n", static_cast<double>(mMatrix[0][i]), + StringAppendF(&out, "%s %.4f %.4f %.4f\n", prefix, static_cast<double>(mMatrix[0][i]), static_cast<double>(mMatrix[1][i]), static_cast<double>(mMatrix[2][i])); } } -void Transform::dump(const char* name) const { +void Transform::dump(const char* name, const char* prefix) const { std::string out; - dump(out, name); + dump(out, name, prefix); ALOGD("%s", out.c_str()); } -} // namespace ui -} // namespace android +} // namespace android::ui diff --git a/libs/ui/include/ui/PhysicalDisplayId.h b/libs/ui/include/ui/BlurRegion.h index 1a345acc86..c5a5d477cf 100644 --- a/libs/ui/include/ui/PhysicalDisplayId.h +++ b/libs/ui/include/ui/BlurRegion.h @@ -1,5 +1,5 @@ /* - * Copyright 2019 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,21 @@ #pragma once -#include <cinttypes> -#include <cstdint> - -#define ANDROID_PHYSICAL_DISPLAY_ID_FORMAT PRIu64 +#include <inttypes.h> namespace android { -using PhysicalDisplayId = uint64_t; - -constexpr uint8_t getPhysicalDisplayPort(PhysicalDisplayId displayId) { - return static_cast<uint8_t>(displayId); -} +struct BlurRegion { + uint32_t blurRadius; + float cornerRadiusTL; + float cornerRadiusTR; + float cornerRadiusBL; + float cornerRadiusBR; + float alpha; + int left; + int top; + int right; + int bottom; +}; -} // namespace android +} // namespace android
\ No newline at end of file diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h index 4685575441..18cd487b8f 100644 --- a/libs/ui/include/ui/DebugUtils.h +++ b/libs/ui/include/ui/DebugUtils.h @@ -34,5 +34,4 @@ std::string decodeColorMode(android::ui::ColorMode colormode); std::string decodeColorTransform(android_color_transform colorTransform); std::string decodePixelFormat(android::PixelFormat format); std::string decodeRenderIntent(android::ui::RenderIntent renderIntent); -std::string to_string(const android::Rect& rect); std::string toString(const android::DeviceProductInfo&); diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h index af00342c0c..807a5d96a3 100644 --- a/libs/ui/include/ui/DeviceProductInfo.h +++ b/libs/ui/include/ui/DeviceProductInfo.h @@ -19,7 +19,12 @@ #include <array> #include <cstdint> #include <optional> +#include <string> +#include <type_traits> #include <variant> +#include <vector> + +#include <utils/Flattenable.h> namespace android { @@ -29,13 +34,7 @@ using PnpId = std::array<char, 4>; // Product-specific information about the display or the directly connected device on the // display chain. For example, if the display is transitively connected, this field may contain // product information about the intermediate device. -struct DeviceProductInfo { - static constexpr size_t TEXT_BUFFER_SIZE = 20; - static constexpr size_t RELATIVE_ADDRESS_SIZE = 4; - - using RelativeAddress = std::array<uint8_t, RELATIVE_ADDRESS_SIZE>; - static constexpr RelativeAddress NO_RELATIVE_ADDRESS = {0xff, 0xff, 0xff, 0xff}; - +struct DeviceProductInfo : LightFlattenable<DeviceProductInfo> { struct ModelYear { uint32_t year; }; @@ -48,21 +47,29 @@ struct DeviceProductInfo { }; // Display name. - std::array<char, TEXT_BUFFER_SIZE> name; + std::string name; // Manufacturer Plug and Play ID. PnpId manufacturerPnpId; // Manufacturer product ID. - std::array<char, TEXT_BUFFER_SIZE> productId; + std::string productId; using ManufactureOrModelDate = std::variant<ModelYear, ManufactureYear, ManufactureWeekAndYear>; + static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>); ManufactureOrModelDate manufactureOrModelDate; - // Relative address in the display network. Unavailable address is indicated - // by all elements equal to 255. + // Relative address in the display network. Empty vector indicates that the + // address is unavailable. // For example, for HDMI connected device this will be the physical address. - RelativeAddress relativeAddress; + std::vector<uint8_t> relativeAddress; + + bool isFixedSize() const { return false; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + void dump(std::string& result) const; }; } // namespace android diff --git a/libs/ui/include/ui/DisplayId.h b/libs/ui/include/ui/DisplayId.h new file mode 100644 index 0000000000..f196ab901a --- /dev/null +++ b/libs/ui/include/ui/DisplayId.h @@ -0,0 +1,194 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> +#include <optional> +#include <string> + +namespace android { + +// ID of a physical or a virtual display. This class acts as a type safe wrapper around uint64_t. +// The encoding of the ID is type-specific for bits 0 to 61. +struct DisplayId { + // Flag indicating that the display is virtual. + static constexpr uint64_t FLAG_VIRTUAL = 1ULL << 63; + + // Flag indicating that the ID is stable across reboots. + static constexpr uint64_t FLAG_STABLE = 1ULL << 62; + + // TODO(b/162612135) Remove default constructor + DisplayId() = default; + constexpr DisplayId(const DisplayId&) = default; + DisplayId& operator=(const DisplayId&) = default; + + uint64_t value; + +protected: + explicit constexpr DisplayId(uint64_t id) : value(id) {} +}; + +static_assert(sizeof(DisplayId) == sizeof(uint64_t)); + +inline bool operator==(DisplayId lhs, DisplayId rhs) { + return lhs.value == rhs.value; +} + +inline bool operator!=(DisplayId lhs, DisplayId rhs) { + return !(lhs == rhs); +} + +inline std::string to_string(DisplayId displayId) { + return std::to_string(displayId.value); +} + +// DisplayId of a physical display, such as the internal display or externally connected display. +struct PhysicalDisplayId : DisplayId { + static constexpr std::optional<PhysicalDisplayId> tryCast(DisplayId id) { + if (id.value & FLAG_VIRTUAL) { + return std::nullopt; + } + return {PhysicalDisplayId(id)}; + } + + // Returns a stable ID based on EDID information. + static constexpr PhysicalDisplayId fromEdid(uint8_t port, uint16_t manufacturerId, + uint32_t modelHash) { + return PhysicalDisplayId(FLAG_STABLE, port, manufacturerId, modelHash); + } + + // Returns an unstable ID. If EDID is available using "fromEdid" is preferred. + static constexpr PhysicalDisplayId fromPort(uint8_t port) { + constexpr uint16_t kManufacturerId = 0; + constexpr uint32_t kModelHash = 0; + return PhysicalDisplayId(0, port, kManufacturerId, kModelHash); + } + + // TODO(b/162612135) Remove default constructor + PhysicalDisplayId() = default; + // TODO(b/162612135) Remove constructor + explicit constexpr PhysicalDisplayId(uint64_t id) : DisplayId(id) {} + + constexpr uint16_t getManufacturerId() const { return static_cast<uint16_t>(value >> 40); } + + constexpr uint8_t getPort() const { return static_cast<uint8_t>(value); } + +private: + constexpr PhysicalDisplayId(uint64_t flags, uint8_t port, uint16_t manufacturerId, + uint32_t modelHash) + : DisplayId(flags | (static_cast<uint64_t>(manufacturerId) << 40) | + (static_cast<uint64_t>(modelHash) << 8) | port) {} + + explicit constexpr PhysicalDisplayId(DisplayId other) : DisplayId(other) {} +}; + +static_assert(sizeof(PhysicalDisplayId) == sizeof(uint64_t)); + +struct VirtualDisplayId : DisplayId { + using BaseId = uint32_t; + // Flag indicating that this virtual display is backed by the GPU. + static constexpr uint64_t FLAG_GPU = 1ULL << 61; + + static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) { + if (id.value & FLAG_VIRTUAL) { + return {VirtualDisplayId(id)}; + } + return std::nullopt; + } + +protected: + constexpr VirtualDisplayId(uint64_t flags, BaseId baseId) + : DisplayId(DisplayId::FLAG_VIRTUAL | flags | baseId) {} + + explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {} +}; + +struct HalVirtualDisplayId : VirtualDisplayId { + explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(0, baseId) {} + + static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) { + if ((id.value & FLAG_VIRTUAL) && !(id.value & VirtualDisplayId::FLAG_GPU)) { + return {HalVirtualDisplayId(id)}; + } + return std::nullopt; + } + +private: + explicit constexpr HalVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {} +}; + +struct GpuVirtualDisplayId : VirtualDisplayId { + explicit constexpr GpuVirtualDisplayId(BaseId baseId) + : VirtualDisplayId(VirtualDisplayId::FLAG_GPU, baseId) {} + + static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) { + if ((id.value & FLAG_VIRTUAL) && (id.value & VirtualDisplayId::FLAG_GPU)) { + return {GpuVirtualDisplayId(id)}; + } + return std::nullopt; + } + +private: + explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {} +}; + +// HalDisplayId is the ID of a display which is managed by HWC. +// PhysicalDisplayId and HalVirtualDisplayId are implicitly convertible to HalDisplayId. +struct HalDisplayId : DisplayId { + constexpr HalDisplayId(HalVirtualDisplayId other) : DisplayId(other) {} + constexpr HalDisplayId(PhysicalDisplayId other) : DisplayId(other) {} + + static constexpr std::optional<HalDisplayId> tryCast(DisplayId id) { + if (GpuVirtualDisplayId::tryCast(id)) { + return std::nullopt; + } + return {HalDisplayId(id)}; + } + +private: + explicit constexpr HalDisplayId(DisplayId other) : DisplayId(other) {} +}; + +static_assert(sizeof(VirtualDisplayId) == sizeof(uint64_t)); +static_assert(sizeof(HalVirtualDisplayId) == sizeof(uint64_t)); +static_assert(sizeof(GpuVirtualDisplayId) == sizeof(uint64_t)); +static_assert(sizeof(HalDisplayId) == sizeof(uint64_t)); + +} // namespace android + +namespace std { + +template <> +struct hash<android::DisplayId> { + size_t operator()(android::DisplayId displayId) const { + return hash<uint64_t>()(displayId.value); + } +}; + +template <> +struct hash<android::PhysicalDisplayId> : hash<android::DisplayId> {}; + +template <> +struct hash<android::HalVirtualDisplayId> : hash<android::DisplayId> {}; + +template <> +struct hash<android::GpuVirtualDisplayId> : hash<android::DisplayId> {}; + +template <> +struct hash<android::HalDisplayId> : hash<android::DisplayId> {}; + +} // namespace std diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h index 897060c2ed..03e0a3886e 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h @@ -20,19 +20,23 @@ #include <type_traits> #include <ui/DeviceProductInfo.h> +#include <utils/Flattenable.h> namespace android { enum class DisplayConnectionType { Internal, External }; // Immutable information about physical display. -struct DisplayInfo { +struct DisplayInfo : LightFlattenable<DisplayInfo> { DisplayConnectionType connectionType = DisplayConnectionType::Internal; float density = 0.f; bool secure = false; std::optional<DeviceProductInfo> deviceProductInfo; -}; -static_assert(std::is_trivially_copyable_v<DisplayInfo>); + bool isFixedSize() const { return false; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); +}; } // namespace android diff --git a/libs/ui/include/ui/DisplayState.h b/libs/ui/include/ui/DisplayState.h index 64efc84f1b..70a0d50611 100644 --- a/libs/ui/include/ui/DisplayState.h +++ b/libs/ui/include/ui/DisplayState.h @@ -32,7 +32,7 @@ constexpr LayerStack NO_LAYER_STACK = static_cast<LayerStack>(-1); struct DisplayState { LayerStack layerStack = NO_LAYER_STACK; Rotation orientation = ROTATION_0; - Size viewport; + Size layerStackSpaceRect; }; static_assert(std::is_trivially_copyable_v<DisplayState>); diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h index 1152cc5526..22274a27d2 100644 --- a/libs/ui/include/ui/PublicFormat.h +++ b/libs/ui/include/ui/PublicFormat.h @@ -50,6 +50,7 @@ enum class PublicFormat { JPEG = 0x100, DEPTH_POINT_CLOUD = 0x101, RAW_DEPTH = 0x1002, // @hide + RAW_DEPTH10 = 0x1003, // @hide YV12 = 0x32315659, Y8 = 0x20203859, Y16 = 0x20363159, // @hide diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h index 2f2229e67c..58323e553e 100644 --- a/libs/ui/include/ui/Rect.h +++ b/libs/ui/include/ui/Rect.h @@ -19,10 +19,10 @@ #include <ostream> +#include <log/log.h> #include <utils/Flattenable.h> #include <utils/Log.h> #include <utils/TypeHelpers.h> -#include <log/log.h> #include <ui/FloatRect.h> #include <ui/Point.h> @@ -202,6 +202,15 @@ public: // the input. Rect transform(uint32_t xform, int32_t width, int32_t height) const; + Rect scale(float scaleX, float scaleY) const { + return Rect(FloatRect(left * scaleX, top * scaleY, right * scaleX, bottom * scaleY)); + } + + Rect& scaleSelf(float scaleX, float scaleY) { + set(scale(scaleX, scaleY)); + return *this; + } + // this calculates (Region(*this) - exclude).bounds() efficiently Rect reduce(const Rect& exclude) const; @@ -216,11 +225,10 @@ public: } }; +std::string to_string(const android::Rect& rect); + // Defining PrintTo helps with Google Tests. -static inline void PrintTo(const Rect& rect, ::std::ostream* os) { - *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom - << ")"; -} +void PrintTo(const Rect& rect, ::std::ostream* os); ANDROID_BASIC_TYPES_TRAITS(Rect) diff --git a/libs/ui/include/ui/Rotation.h b/libs/ui/include/ui/Rotation.h index 89008f6694..83d431dea3 100644 --- a/libs/ui/include/ui/Rotation.h +++ b/libs/ui/include/ui/Rotation.h @@ -41,6 +41,15 @@ constexpr Rotation operator+(Rotation lhs, Rotation rhs) { return toRotation((toRotationInt(lhs) + toRotationInt(rhs)) % N); } +constexpr Rotation operator-(Rotation lhs, Rotation rhs) { + constexpr auto N = toRotationInt(ROTATION_270) + 1; + return toRotation((N + toRotationInt(lhs) - toRotationInt(rhs)) % N); +} + +constexpr Rotation operator-(Rotation rotation) { + return ROTATION_0 - rotation; +} + constexpr const char* toCString(Rotation rotation) { switch (rotation) { case ROTATION_0: diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h index c6bb598d7f..a197b3b20e 100644 --- a/libs/ui/include/ui/Transform.h +++ b/libs/ui/include/ui/Transform.h @@ -18,10 +18,10 @@ #include <stdint.h> #include <sys/types.h> +#include <array> #include <ostream> #include <string> -#include <hardware/hardware.h> #include <math/mat4.h> #include <math/vec2.h> #include <math/vec3.h> @@ -44,9 +44,9 @@ public: enum RotationFlags : uint32_t { ROT_0 = 0, - FLIP_H = HAL_TRANSFORM_FLIP_H, - FLIP_V = HAL_TRANSFORM_FLIP_V, - ROT_90 = HAL_TRANSFORM_ROT_90, + FLIP_H = 1, // HAL_TRANSFORM_FLIP_H + FLIP_V = 2, // HAL_TRANSFORM_FLIP_V + ROT_90 = 4, // HAL_TRANSFORM_ROT_90 ROT_180 = FLIP_H | FLIP_V, ROT_270 = ROT_180 | ROT_90, ROT_INVALID = 0x80 @@ -61,32 +61,43 @@ public: }; // query the transform - bool preserveRects() const; - uint32_t getType() const; - uint32_t getOrientation() const; + bool preserveRects() const; + + // Returns if bilinear filtering is needed after applying this transform to avoid aliasing. + bool needsBilinearFiltering() const; + + uint32_t getType() const; + uint32_t getOrientation() const; bool operator==(const Transform& other) const; const vec3& operator [] (size_t i) const; // returns column i float tx() const; float ty() const; - float sx() const; - float sy() const; + float dsdx() const; + float dtdx() const; + float dtdy() const; + float dsdy() const; + + float getScaleX() const; + float getScaleY() const; // modify the transform void reset(); void set(float tx, float ty); void set(float a, float b, float c, float d); status_t set(uint32_t flags, float w, float h); + void set(const std::array<float, 9>& matrix); // transform data Rect makeBounds(int w, int h) const; - vec2 transform(int x, int y) const; + vec2 transform(float x, float y) const; Region transform(const Region& reg) const; Rect transform(const Rect& bounds, bool roundOutwards = false) const; FloatRect transform(const FloatRect& bounds) const; Transform& operator = (const Transform& other); Transform operator * (const Transform& rhs) const; + Transform operator * (float value) const; // assumes the last row is < 0 , 0 , 1 > vec2 transform(const vec2& v) const; vec3 transform(const vec3& v) const; @@ -97,10 +108,10 @@ public: Transform inverse() const; // for debugging - void dump(std::string& result, const char* name) const; - void dump(const char* name) const; + void dump(std::string& result, const char* name, const char* prefix = "") const; + void dump(const char* name, const char* prefix = "") const; - static RotationFlags toRotationFlags(Rotation); + static constexpr RotationFlags toRotationFlags(Rotation); private: struct mat33 { @@ -125,7 +136,7 @@ inline void PrintTo(const Transform& t, ::std::ostream* os) { *os << out; } -inline Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) { +inline constexpr Transform::RotationFlags Transform::toRotationFlags(Rotation rotation) { switch (rotation) { case ROTATION_0: return ROT_0; diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h new file mode 100644 index 0000000000..8e316d8ae0 --- /dev/null +++ b/libs/ui/include_private/ui/FlattenableHelpers.h @@ -0,0 +1,160 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <numeric> +#include <optional> +#include <type_traits> +#include <vector> + +#include <utils/Flattenable.h> + +#define RETURN_IF_ERROR(op) \ + if (const status_t status = (op); status != OK) return status; + +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>>> + static constexpr size_t getFlattenedSize(const T&) { + return sizeof(T); + } + + template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<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>>> + static status_t unflatten(const void** buffer, size_t* size, T* value) { + if (*size < sizeof(T)) return NO_MEMORY; + FlattenableUtils::read(*buffer, *size, *value); + return OK; + } + + // Helpers for reading and writing std::string + static size_t getFlattenedSize(const std::string& str) { + return sizeof(uint64_t) + str.length(); + } + + static status_t flatten(void** buffer, size_t* size, const std::string& str) { + if (*size < getFlattenedSize(str)) return NO_MEMORY; + flatten(buffer, size, (uint64_t)str.length()); + memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length()); + FlattenableUtils::advance(*buffer, *size, str.length()); + return OK; + } + + static status_t unflatten(const void** buffer, size_t* size, std::string* str) { + uint64_t length; + RETURN_IF_ERROR(unflatten(buffer, size, &length)); + if (*size < length) return NO_MEMORY; + str->assign(reinterpret_cast<const char*>(*buffer), length); + FlattenableUtils::advance(*buffer, *size, length); + return OK; + } + + // Helpers for reading and writing LightFlattenable + template <class T> + static size_t getFlattenedSize(const LightFlattenable<T>& value) { + return value.getFlattenedSize(); + } + + template <class T> + static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) { + RETURN_IF_ERROR(value.flatten(*buffer, *size)); + FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize()); + return OK; + } + + template <class T> + static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) { + RETURN_IF_ERROR(value->unflatten(*buffer, *size)); + FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize()); + return OK; + } + + // Helpers for reading and writing std::optional + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static size_t getFlattenedSize(const std::optional<T>& value) { + return sizeof(bool) + (value ? getFlattenedSize(*value) : 0); + } + + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) { + if (value) { + RETURN_IF_ERROR(flatten(buffer, size, true)); + RETURN_IF_ERROR(flatten(buffer, size, *value)); + } else { + RETURN_IF_ERROR(flatten(buffer, size, false)); + } + return OK; + } + + template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>> + static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) { + bool isPresent; + RETURN_IF_ERROR(unflatten(buffer, size, &isPresent)); + if (isPresent) { + *value = T(); + RETURN_IF_ERROR(unflatten(buffer, size, &(**value))); + } else { + value->reset(); + } + return OK; + } + + // Helpers for reading and writing std::vector + template <class T> + static size_t getFlattenedSize(const std::vector<T>& value) { + return std::accumulate(value.begin(), value.end(), sizeof(uint64_t), + [](size_t sum, const T& element) { + return sum + getFlattenedSize(element); + }); + } + + template <class T> + static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) { + RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size())); + for (const auto& element : value) { + RETURN_IF_ERROR(flatten(buffer, size, element)); + } + return OK; + } + + template <class T> + static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) { + uint64_t numElements; + RETURN_IF_ERROR(unflatten(buffer, size, &numElements)); + // We don't need an extra size check since each iteration of the loop does that + std::vector<T> elements; + for (size_t i = 0; i < numElements; i++) { + T element; + RETURN_IF_ERROR(unflatten(buffer, size, &element)); + elements.push_back(element); + } + *value = std::move(elements); + return OK; + } +}; + +} // namespace android + +#undef RETURN_IF_ERROR
\ No newline at end of file diff --git a/libs/ui/include/ui/ColorSpace.h b/libs/ui/include_types/ui/ColorSpace.h index 241ec106c0..241ec106c0 100644 --- a/libs/ui/include/ui/ColorSpace.h +++ b/libs/ui/include_types/ui/ColorSpace.h diff --git a/libs/ui/include_vndk/ui/ColorSpace.h b/libs/ui/include_vndk/ui/ColorSpace.h index ddf70d5bdf..7d2a6d307e 120000 --- a/libs/ui/include_vndk/ui/ColorSpace.h +++ b/libs/ui/include_vndk/ui/ColorSpace.h @@ -1 +1 @@ -../../include/ui/ColorSpace.h
\ No newline at end of file +../../include_types/ui/ColorSpace.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/DisplayId.h b/libs/ui/include_vndk/ui/DisplayId.h new file mode 120000 index 0000000000..73c9fe8d68 --- /dev/null +++ b/libs/ui/include_vndk/ui/DisplayId.h @@ -0,0 +1 @@ +../../include/ui/DisplayId.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/PhysicalDisplayId.h b/libs/ui/include_vndk/ui/PhysicalDisplayId.h deleted file mode 120000 index 6e3fb1e62e..0000000000 --- a/libs/ui/include_vndk/ui/PhysicalDisplayId.h +++ /dev/null @@ -1 +0,0 @@ -../../include/ui/PhysicalDisplayId.h
\ No newline at end of file diff --git a/libs/ui/include_vndk/ui/UiConfig.h b/libs/ui/include_vndk/ui/UiConfig.h deleted file mode 120000 index f580ce1095..0000000000 --- a/libs/ui/include_vndk/ui/UiConfig.h +++ /dev/null @@ -1 +0,0 @@ -../../include/ui/UiConfig.h
\ No newline at end of file diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index b53342cb79..d005ce8e23 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -29,6 +29,20 @@ cc_test { } cc_test { + name: "DisplayId_test", + shared_libs: ["libui"], + srcs: ["DisplayId_test.cpp"], + cflags: ["-Wall", "-Werror"], +} + +cc_test { + name: "FlattenableHelpers_test", + shared_libs: ["libui"], + srcs: ["FlattenableHelpers_test.cpp"], + cflags: ["-Wall", "-Werror"], +} + +cc_test { name: "GraphicBufferAllocator_test", header_libs: [ "libnativewindow_headers", @@ -78,6 +92,14 @@ cc_test { } cc_test { + name: "Rect_test", + test_suites: ["device-tests"], + shared_libs: ["libui"], + srcs: ["Rect_test.cpp"], + cflags: ["-Wall", "-Werror"], +} + +cc_test { name: "Size_test", test_suites: ["device-tests"], shared_libs: ["libui"], diff --git a/libs/ui/tests/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp new file mode 100644 index 0000000000..1d908b8ef1 --- /dev/null +++ b/libs/ui/tests/DisplayId_test.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/DisplayId.h> + +#include <gtest/gtest.h> + +namespace android::ui { + +TEST(DisplayIdTest, createPhysicalIdFromEdid) { + constexpr uint8_t port = 1; + constexpr uint16_t manufacturerId = 13; + constexpr uint32_t modelHash = 42; + PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash); + EXPECT_EQ(port, id.getPort()); + EXPECT_EQ(manufacturerId, id.getManufacturerId()); + EXPECT_FALSE(VirtualDisplayId::tryCast(id)); + EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); + EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); + EXPECT_TRUE(HalDisplayId::tryCast(id)); +} + +TEST(DisplayIdTest, createPhysicalIdFromPort) { + constexpr uint8_t port = 3; + PhysicalDisplayId id = PhysicalDisplayId::fromPort(port); + EXPECT_EQ(port, id.getPort()); + EXPECT_FALSE(VirtualDisplayId::tryCast(id)); + EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); + EXPECT_TRUE(PhysicalDisplayId::tryCast(id)); + EXPECT_TRUE(HalDisplayId::tryCast(id)); +} + +TEST(DisplayIdTest, createGpuVirtualId) { + GpuVirtualDisplayId id(42); + EXPECT_TRUE(VirtualDisplayId::tryCast(id)); + EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(HalVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); + EXPECT_FALSE(HalDisplayId::tryCast(id)); +} + +TEST(DisplayIdTest, createHalVirtualId) { + HalVirtualDisplayId id(42); + EXPECT_TRUE(VirtualDisplayId::tryCast(id)); + EXPECT_TRUE(HalVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id)); + EXPECT_FALSE(PhysicalDisplayId::tryCast(id)); + EXPECT_TRUE(HalDisplayId::tryCast(id)); +} + +} // namespace android::ui diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp new file mode 100644 index 0000000000..db32bc7596 --- /dev/null +++ b/libs/ui/tests/FlattenableHelpers_test.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "FlattenableHelpersTest" + +#include <ui/FlattenableHelpers.h> + +#include <gtest/gtest.h> +#include <utils/Flattenable.h> +#include <cstdint> +#include <memory> +#include <optional> +#include <string> +#include <vector> + +namespace android { + +namespace { + +struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> { + std::unique_ptr<int32_t> ptr; + + bool isFixedSize() const { return true; } + size_t getFlattenedSize() const { return sizeof(int32_t); } + + status_t flatten(void* buffer, size_t size) const { + FlattenableUtils::write(buffer, size, *ptr); + return OK; + } + + status_t unflatten(void const* buffer, size_t size) { + int value; + FlattenableUtils::read(buffer, size, value); + ptr = std::make_unique<int32_t>(value); + return OK; + } +}; + +class FlattenableHelpersTest : public testing::Test { +public: + template <class T> + void testWriteThenRead(const T& value, size_t bufferSize) { + std::vector<int8_t> buffer(bufferSize); + auto rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + + auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size = buffer.size(); + T valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + EXPECT_EQ(value, valueRead); + } + + template <class T> + void testTriviallyCopyable(const T& value) { + testWriteThenRead(value, sizeof(T)); + } + + template <class T> + void testWriteThenRead(const T& value) { + testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value)); + } +}; + +TEST_F(FlattenableHelpersTest, TriviallyCopyable) { + testTriviallyCopyable(42); + testTriviallyCopyable(1LL << 63); + testTriviallyCopyable(false); + testTriviallyCopyable(true); + testTriviallyCopyable(std::optional<int>()); + testTriviallyCopyable(std::optional<int>(4)); +} + +TEST_F(FlattenableHelpersTest, String) { + testWriteThenRead(std::string("Android")); + testWriteThenRead(std::string()); +} + +TEST_F(FlattenableHelpersTest, Vector) { + testWriteThenRead(std::vector<int>({1, 2, 3})); + testWriteThenRead(std::vector<int>()); +} + +TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) { + std::vector<size_t> buffer; + constexpr int kInternalValue = 16; + { + std::optional<TestLightFlattenable> value = + TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)}; + buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0); + void* rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + } + + const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size_t size = buffer.size(); + std::optional<TestLightFlattenable> valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + ASSERT_TRUE(valueRead.has_value()); + EXPECT_EQ(kInternalValue, *valueRead->ptr); +} + +TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) { + std::vector<size_t> buffer; + { + std::optional<TestLightFlattenable> value; + buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0); + void* rawBuffer = reinterpret_cast<void*>(buffer.data()); + size_t size = buffer.size(); + ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value)); + } + + const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data()); + size_t size = buffer.size(); + std::optional<TestLightFlattenable> valueRead; + ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead)); + ASSERT_FALSE(valueRead.has_value()); +} + +} // namespace +} // namespace android diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp new file mode 100644 index 0000000000..5499a5b507 --- /dev/null +++ b/libs/ui/tests/Rect_test.cpp @@ -0,0 +1,262 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <system/graphics.h> +#include <ui/FloatRect.h> +#include <ui/Point.h> +#include <ui/Rect.h> +#include <ui/Size.h> + +#include <gtest/gtest.h> + +namespace android::ui { + +TEST(RectTest, constructDefault) { + const Rect rect; + EXPECT_FALSE(rect.isValid()); + EXPECT_TRUE(rect.isEmpty()); +} + +TEST(RectTest, constructFromWidthAndHeight) { + const Rect rect(100, 200); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(0, rect.top); + EXPECT_EQ(0, rect.left); + EXPECT_EQ(100, rect.right); + EXPECT_EQ(200, rect.bottom); + EXPECT_EQ(100, rect.getWidth()); + EXPECT_EQ(200, rect.getHeight()); +} + +TEST(RectTest, constructFromSize) { + const Rect rect(Size(100, 200)); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(0, rect.top); + EXPECT_EQ(0, rect.left); + EXPECT_EQ(100, rect.right); + EXPECT_EQ(200, rect.bottom); + EXPECT_EQ(100, rect.getWidth()); + EXPECT_EQ(200, rect.getHeight()); +} + +TEST(RectTest, constructFromLTRB) { + const Rect rect(11, 12, 14, 14); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(11, rect.left); + EXPECT_EQ(12, rect.top); + EXPECT_EQ(14, rect.right); + EXPECT_EQ(14, rect.bottom); + EXPECT_EQ(3, rect.getWidth()); + EXPECT_EQ(2, rect.getHeight()); +} + +TEST(RectTest, constructFromPoints) { + const Rect rect(Point(11, 12), Point(14, 14)); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(11, rect.left); + EXPECT_EQ(12, rect.top); + EXPECT_EQ(14, rect.right); + EXPECT_EQ(14, rect.bottom); + EXPECT_EQ(3, rect.getWidth()); + EXPECT_EQ(2, rect.getHeight()); +} + +TEST(RectTest, constructFromFloatRect) { + { + const Rect rect(FloatRect(10, 20, 30, 40)); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(10, rect.left); + EXPECT_EQ(20, rect.top); + EXPECT_EQ(30, rect.right); + EXPECT_EQ(40, rect.bottom); + } + // Construct with floating point error + { + constexpr float kError = 1e-3; + const Rect rect(FloatRect(10 - kError, 20 - kError, 30 - kError, 40 - kError)); + EXPECT_TRUE(rect.isValid()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_EQ(10, rect.left); + EXPECT_EQ(20, rect.top); + EXPECT_EQ(30, rect.right); + EXPECT_EQ(40, rect.bottom); + } +} + +TEST(RectTest, makeInvalid) { + Rect rect(10, 20, 60, 60); + EXPECT_TRUE(rect.isValid()); + rect.makeInvalid(); + EXPECT_FALSE(rect.isValid()); +} + +TEST(RectTest, clear) { + Rect rect(10, 20, 60, 60); + EXPECT_FALSE(rect.isEmpty()); + rect.clear(); + EXPECT_TRUE(rect.isEmpty()); +} + +TEST(RectTest, getSize) { + const Rect rect(10, 20, 60, 60); + EXPECT_EQ(Size(50, 40), rect.getSize()); +} + +TEST(RectTest, getBounds) { + const Rect rect(10, 20, 60, 60); + const Rect bounds = rect.getBounds(); + EXPECT_EQ(0, bounds.left); + EXPECT_EQ(0, bounds.top); + EXPECT_EQ(50, bounds.right); + EXPECT_EQ(40, bounds.bottom); + EXPECT_EQ(rect.getSize(), bounds.getSize()); +} + +TEST(RectTest, getCornerPoints) { + const Rect rect(10, 20, 50, 60); + EXPECT_EQ(Point(10, 20), rect.leftTop()); + EXPECT_EQ(Point(10, 60), rect.leftBottom()); + EXPECT_EQ(Point(50, 20), rect.rightTop()); + EXPECT_EQ(Point(50, 60), rect.rightBottom()); +} + +TEST(RectTest, operatorEquals) { + const Rect rect(10, 20, 50, 60); + EXPECT_EQ(rect, rect); + EXPECT_NE(Rect(0, 20, 50, 60), rect); + EXPECT_NE(Rect(10, 0, 50, 60), rect); + EXPECT_NE(Rect(10, 20, 0, 60), rect); + EXPECT_NE(Rect(10, 20, 50, 0), rect); +} + +TEST(RectTest, operatorsPlusMinus) { + Rect rect = Rect(10, 20, 50, 60) + Point(1, 2); + EXPECT_EQ(Rect(11, 22, 51, 62), rect); + rect -= Point(1, 2); + EXPECT_EQ(Rect(10, 20, 50, 60), rect); + + rect = Rect(10, 20, 50, 60) - Point(1, 2); + EXPECT_EQ(Rect(9, 18, 49, 58), rect); + rect += Point(1, 2); + EXPECT_EQ(Rect(10, 20, 50, 60), rect); +} + +TEST(RectTest, scale) { + Rect rect(10, 20, 50, 60); + EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f, 3.f)); + rect.scaleSelf(2.f, 3.f); + EXPECT_EQ(Rect(20, 60, 100, 180), rect); + + rect = Rect(10, 20, 50, 60); + constexpr float kError = 1e-3; + EXPECT_EQ(Rect(20, 60, 100, 180), rect.scale(2.f - kError, 3.f - kError)); + rect.scaleSelf(2.f - kError, 3.f - kError); + EXPECT_EQ(Rect(20, 60, 100, 180), rect); +} + +TEST(RectTest, inset) { + Rect rect(10, 20, 50, 60); + rect.inset(0, 0, 0, 0); + EXPECT_EQ(Rect(10, 20, 50, 60), rect); + rect.inset(1, 2, 3, 4); + EXPECT_EQ(Rect(11, 22, 47, 56), rect); +} + +TEST(RectTest, intersect) { + const Rect rect(10, 20, 50, 60); + Rect intersection; + + // Intersect with self is self + intersection.makeInvalid(); + EXPECT_TRUE(rect.intersect(rect, &intersection)); + EXPECT_EQ(Rect(10, 20, 50, 60), intersection); + + // Intersect with rect contained in us + const Rect insideRect(11, 21, 45, 55); + intersection.makeInvalid(); + EXPECT_TRUE(rect.intersect(insideRect, &intersection)); + EXPECT_EQ(insideRect, intersection); + + // Intersect with rect we are contained in + intersection.makeInvalid(); + EXPECT_TRUE(insideRect.intersect(rect, &intersection)); + EXPECT_EQ(insideRect, intersection); + + // Empty intersection + intersection.makeInvalid(); + EXPECT_FALSE(rect.intersect(Rect(100, 202, 150, 260), &intersection)); + EXPECT_TRUE(intersection.isEmpty()); + + // Partial intersection + const Rect other(30, 40, 70, 80); + intersection.makeInvalid(); + EXPECT_TRUE(rect.intersect(other, &intersection)); + EXPECT_EQ(Rect(30, 40, 50, 60), intersection); + + // Intersetion is commutative + intersection.makeInvalid(); + EXPECT_TRUE(other.intersect(rect, &intersection)); + EXPECT_EQ(Rect(30, 40, 50, 60), intersection); +} + +TEST(RectTest, reduce) { + const Rect rect(10, 20, 50, 60); + + // Reduce with self is empty + EXPECT_TRUE(rect.reduce(rect).isEmpty()); + + // Reduce with rect entirely inside is a noop + const Rect insideRect(11, 21, 45, 55); + EXPECT_EQ(rect, rect.reduce(insideRect)); + + // Reduce with rect entirely outside is empty + EXPECT_TRUE(insideRect.reduce(rect).isEmpty()); + + // Reduce with rect on the right + EXPECT_EQ(Rect(10, 20, 20, 60), rect.reduce(Rect(20, 0, 60, 70))); + + // Reduce with rect on the left + EXPECT_EQ(Rect(40, 20, 50, 60), rect.reduce(Rect(0, 0, 40, 70))); + + // Reduce with rect at the top + EXPECT_EQ(Rect(10, 40, 50, 60), rect.reduce(Rect(0, 0, 70, 40))); + + // Reduce with rect at the bottom + EXPECT_EQ(Rect(10, 20, 50, 40), rect.reduce(Rect(0, 40, 70, 70))); +} + +TEST(RectTest, transform) { + const int32_t width = 100, height = 200; + const Rect rect(1, 1, 2, 3); + EXPECT_EQ(Rect(98, 1, 99, 3), rect.transform(HAL_TRANSFORM_FLIP_H, width, height)); + EXPECT_EQ(Rect(1, 197, 2, 199), rect.transform(HAL_TRANSFORM_FLIP_V, width, height)); + EXPECT_EQ(Rect(197, 1, 199, 2), rect.transform(HAL_TRANSFORM_ROT_90, width, height)); + EXPECT_EQ(Rect(98, 197, 99, 199), rect.transform(HAL_TRANSFORM_ROT_180, width, height)); + EXPECT_EQ(Rect(1, 98, 3, 99), rect.transform(HAL_TRANSFORM_ROT_270, width, height)); +} + +TEST(RectTest, toFloatRect) { + const Rect rect(10, 20, 50, 60); + const FloatRect floatRect = rect.toFloatRect(); + EXPECT_EQ(FloatRect(10.f, 20.f, 50.f, 60.f), floatRect); +} + +} // namespace android::ui diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp index 38f37ad827..5f75aeabeb 100644 --- a/libs/ui/tests/Size_test.cpp +++ b/libs/ui/tests/Size_test.cpp @@ -33,8 +33,7 @@ #include <gtest/gtest.h> -namespace android { -namespace ui { +namespace android::ui { TEST(SizeTest, BasicConstructionAndEqualityComparison) { Size s(123, 456); @@ -215,5 +214,4 @@ TEST(SizeTest, Uint32RangeIsClamped) { ClampTest(uint32_t(0), int32_t(0)); } -} // namespace ui -} // namespace android +} // namespace android::ui diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING index 7fcd7de319..eece18ee82 100644 --- a/libs/ui/tests/TEST_MAPPING +++ b/libs/ui/tests/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "Size_test" + }, + { + "name": "Rect_test" } ] } diff --git a/libs/vibrator/Android.bp b/libs/vibrator/Android.bp index e95a080957..1681fe2fcf 100644 --- a/libs/vibrator/Android.bp +++ b/libs/vibrator/Android.bp @@ -14,6 +14,8 @@ cc_library_shared { name: "libvibrator", + vendor_available: true, + double_loadable: true, shared_libs: [ "libbinder", diff --git a/libs/vibrator/ExternalVibrationUtils.cpp b/libs/vibrator/ExternalVibrationUtils.cpp new file mode 100644 index 0000000000..749c568457 --- /dev/null +++ b/libs/vibrator/ExternalVibrationUtils.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <cstring> + +#include <math.h> + +#include <vibrator/ExternalVibrationUtils.h> + +namespace android::os { + +namespace { +static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f; +static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f; +static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; + +float getHapticScaleGamma(HapticScale scale) { + switch (scale) { + case HapticScale::VERY_LOW: + return 2.0f; + case HapticScale::LOW: + return 1.5f; + case HapticScale::HIGH: + return 0.5f; + case HapticScale::VERY_HIGH: + return 0.25f; + default: + return 1.0f; + } +} + +float getHapticMaxAmplitudeRatio(HapticScale scale) { + switch (scale) { + case HapticScale::VERY_LOW: + return HAPTIC_SCALE_VERY_LOW_RATIO; + case HapticScale::LOW: + return HAPTIC_SCALE_LOW_RATIO; + case HapticScale::NONE: + case HapticScale::HIGH: + case HapticScale::VERY_HIGH: + return 1.0f; + default: + return 0.0f; + } +} + +} // namespace + +bool isValidHapticScale(HapticScale scale) { + switch (scale) { + case HapticScale::MUTE: + case HapticScale::VERY_LOW: + case HapticScale::LOW: + case HapticScale::NONE: + case HapticScale::HIGH: + case HapticScale::VERY_HIGH: + return true; + } + return false; +} + +void scaleHapticData(float* buffer, size_t length, HapticScale scale) { + if (!isValidHapticScale(scale) || scale == HapticScale::NONE) { + return; + } + if (scale == HapticScale::MUTE) { + memset(buffer, 0, length * sizeof(float)); + return; + } + float gamma = getHapticScaleGamma(scale); + float maxAmplitudeRatio = getHapticMaxAmplitudeRatio(scale); + for (size_t i = 0; i < length; i++) { + float sign = buffer[i] >= 0 ? 1.0 : -1.0; + buffer[i] = powf(fabsf(buffer[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign; + } +} + +} // namespace android::os diff --git a/libs/vibrator/include/vibrator/ExternalVibrationUtils.h b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h new file mode 100644 index 0000000000..20045d0769 --- /dev/null +++ b/libs/vibrator/include/vibrator/ExternalVibrationUtils.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_EXTERNAL_VIBRATION_UTILS_H +#define ANDROID_EXTERNAL_VIBRATION_UTILS_H + +#include <android/os/IExternalVibratorService.h> + +namespace android::os { + +enum class HapticScale { + MUTE = IExternalVibratorService::SCALE_MUTE, + VERY_LOW = IExternalVibratorService::SCALE_VERY_LOW, + LOW = IExternalVibratorService::SCALE_LOW, + NONE = IExternalVibratorService::SCALE_NONE, + HIGH = IExternalVibratorService::SCALE_HIGH, + VERY_HIGH = IExternalVibratorService::SCALE_VERY_HIGH, +}; + +bool isValidHapticScale(HapticScale scale); + +void scaleHapticData(float* buffer, size_t length, HapticScale scale); + +} // namespace android::os + +#endif // ANDROID_EXTERNAL_VIBRATION_UTILS_H diff --git a/opengl/include/EGL/eglext_angle.h b/opengl/include/EGL/eglext_angle.h index 0556ea1342..e753e0d60f 100644 --- a/opengl/include/EGL/eglext_angle.h +++ b/opengl/include/EGL/eglext_angle.h @@ -4,12 +4,12 @@ // found in the LICENSE file. // // eglext_angle.h: ANGLE modifications to the eglext.h header file. -// Currently we don't include this file directly, we patch eglext.h -// to include it implicitly so it is visible throughout our code. #ifndef INCLUDE_EGL_EGLEXT_ANGLE_ #define INCLUDE_EGL_EGLEXT_ANGLE_ +#include <EGL/eglext.h> + // clang-format off #ifndef EGL_ANGLE_robust_resource_initialization @@ -186,6 +186,26 @@ EGLAPI EGLint EGLAPIENTRY eglProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limi #define EGL_EXTENSIONS_ENABLED_ANGLE 0x345F #endif /* EGL_ANGLE_create_context_extensions_enabled */ +#ifndef EGL_ANGLE_feature_control +#define EGL_ANGLE_feature_control 1 +#define EGL_FEATURE_NAME_ANGLE 0x3460 +#define EGL_FEATURE_CATEGORY_ANGLE 0x3461 +#define EGL_FEATURE_DESCRIPTION_ANGLE 0x3462 +#define EGL_FEATURE_BUG_ANGLE 0x3463 +#define EGL_FEATURE_STATUS_ANGLE 0x3464 +#define EGL_FEATURE_COUNT_ANGLE 0x3465 +#define EGL_FEATURE_OVERRIDES_ENABLED_ANGLE 0x3466 +#define EGL_FEATURE_OVERRIDES_DISABLED_ANGLE 0x3467 +#define EGL_FEATURE_CONDITION_ANGLE 0x3468 +#define EGL_FEATURE_ALL_DISABLED_ANGLE 0x3469 +typedef const char *(EGLAPIENTRYP PFNEGLQUERYSTRINGIANGLEPROC) (EGLDisplay dpy, EGLint name, EGLint index); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDISPLAYATTRIBANGLEPROC) (EGLDisplay dpy, EGLint attribute, EGLAttrib *value); +#ifdef EGL_EGLEXT_PROTOTYPES +EGLAPI const char *EGLAPIENTRY eglQueryStringiANGLE(EGLDisplay dpy, EGLint name, EGLint index); +EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribANGLE(EGLDisplay dpy, EGLint attribute, EGLAttrib *value); +#endif +#endif /* EGL_ANGLE_feature_control */ + // clang-format on #endif // INCLUDE_EGL_EGLEXT_ANGLE_ diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index e7c2e949f5..77d887c5f2 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -102,11 +102,6 @@ cc_defaults { "libbacktrace", "libbase", ], - target: { - vendor: { - exclude_shared_libs: ["libgraphicsenv"], - }, - }, } cc_library_static { diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp index a27c09f0fe..beca7f191a 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -18,11 +18,11 @@ #include "BlobCache.h" +#include <android-base/properties.h> #include <errno.h> #include <inttypes.h> - -#include <android-base/properties.h> #include <log/log.h> + #include <chrono> namespace android { @@ -36,8 +36,8 @@ static const uint32_t blobCacheVersion = 3; // BlobCache::Header::mDeviceVersion value static const uint32_t blobCacheDeviceVersion = 1; -BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize): - mMaxTotalSize(maxTotalSize), +BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize) + : mMaxTotalSize(maxTotalSize), mMaxKeySize(maxKeySize), mMaxValueSize(maxValueSize), mTotalSize(0) { @@ -52,21 +52,21 @@ BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize ALOGV("initializing random seed using %lld", (unsigned long long)now); } -void BlobCache::set(const void* key, size_t keySize, const void* value, - size_t valueSize) { +void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) { if (mMaxKeySize < keySize) { - ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", - keySize, mMaxKeySize); + ALOGV("set: not caching because the key is too large: %zu (limit: %zu)", keySize, + mMaxKeySize); return; } if (mMaxValueSize < valueSize) { - ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", - valueSize, mMaxValueSize); + ALOGV("set: not caching because the value is too large: %zu (limit: %zu)", valueSize, + mMaxValueSize); return; } if (mMaxTotalSize < keySize + valueSize) { ALOGV("set: not caching because the combined key/value size is too " - "large: %zu (limit: %zu)", keySize + valueSize, mMaxTotalSize); + "large: %zu (limit: %zu)", + keySize + valueSize, mMaxTotalSize); return; } if (keySize == 0) { @@ -95,16 +95,16 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, continue; } else { ALOGV("set: not caching new key/value pair because the " - "total cache size limit would be exceeded: %zu " - "(limit: %zu)", - keySize + valueSize, mMaxTotalSize); + "total cache size limit would be exceeded: %zu " + "(limit: %zu)", + keySize + valueSize, mMaxTotalSize); break; } } mCacheEntries.insert(index, CacheEntry(keyBlob, valueBlob)); mTotalSize = newTotalSize; - ALOGV("set: created new cache entry with %zu byte key and %zu byte value", - keySize, valueSize); + ALOGV("set: created new cache entry with %zu byte key and %zu byte value", keySize, + valueSize); } else { // Update the existing cache entry. std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true)); @@ -117,25 +117,25 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, continue; } else { ALOGV("set: not caching new value because the total cache " - "size limit would be exceeded: %zu (limit: %zu)", - keySize + valueSize, mMaxTotalSize); + "size limit would be exceeded: %zu (limit: %zu)", + keySize + valueSize, mMaxTotalSize); break; } } index->setValue(valueBlob); mTotalSize = newTotalSize; ALOGV("set: updated existing cache entry with %zu byte key and %zu byte " - "value", keySize, valueSize); + "value", + keySize, valueSize); } break; } } -size_t BlobCache::get(const void* key, size_t keySize, void* value, - size_t valueSize) { +size_t BlobCache::get(const void* key, size_t keySize, void* value, size_t valueSize) { if (mMaxKeySize < keySize) { - ALOGV("get: not searching because the key is too large: %zu (limit %zu)", - keySize, mMaxKeySize); + ALOGV("get: not searching because the key is too large: %zu (limit %zu)", keySize, + mMaxKeySize); return 0; } std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false)); @@ -154,8 +154,8 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, ALOGV("get: copying %zu bytes to caller's buffer", valueBlobSize); memcpy(value, valueBlob->getData(), valueBlobSize); } else { - ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)", - valueSize, valueBlobSize); + ALOGV("get: caller's buffer is too small for value: %zu (needs %zu)", valueSize, + valueBlobSize); } return valueBlobSize; } @@ -167,7 +167,7 @@ static inline size_t align4(size_t size) { size_t BlobCache::getFlattenedSize() const { auto buildId = base::GetProperty("ro.build.id", ""); size_t size = align4(sizeof(Header) + buildId.size()); - for (const CacheEntry& e : mCacheEntries) { + for (const CacheEntry& e : mCacheEntries) { std::shared_ptr<Blob> const& keyBlob = e.getKey(); std::shared_ptr<Blob> const& valueBlob = e.getValue(); size += align4(sizeof(EntryHeader) + keyBlob->getSize() + valueBlob->getSize()); @@ -193,7 +193,7 @@ int BlobCache::flatten(void* buffer, size_t size) const { // Write cache entries uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer); off_t byteOffset = align4(sizeof(Header) + header->mBuildIdLength); - for (const CacheEntry& e : mCacheEntries) { + for (const CacheEntry& e : mCacheEntries) { std::shared_ptr<Blob> const& keyBlob = e.getKey(); std::shared_ptr<Blob> const& valueBlob = e.getValue(); size_t keySize = keyBlob->getSize(); @@ -259,8 +259,7 @@ int BlobCache::unflatten(void const* buffer, size_t size) { return -EINVAL; } - const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>( - &byteBuffer[byteOffset]); + const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(&byteBuffer[byteOffset]); size_t keySize = eheader->mKeySize; size_t valueSize = eheader->mValueSize; size_t entrySize = sizeof(EntryHeader) + keySize + valueSize; @@ -304,10 +303,8 @@ bool BlobCache::isCleanable() const { return mTotalSize > mMaxTotalSize / 2; } -BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) : - mData(copyData ? malloc(size) : data), - mSize(size), - mOwnsData(copyData) { +BlobCache::Blob::Blob(const void* data, size_t size, bool copyData) + : mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) { if (data != nullptr && copyData) { memcpy(const_cast<void*>(mData), data, size); } @@ -335,19 +332,13 @@ size_t BlobCache::Blob::getSize() const { return mSize; } -BlobCache::CacheEntry::CacheEntry() { -} +BlobCache::CacheEntry::CacheEntry() {} -BlobCache::CacheEntry::CacheEntry( - const std::shared_ptr<Blob>& key, const std::shared_ptr<Blob>& value): - mKey(key), - mValue(value) { -} +BlobCache::CacheEntry::CacheEntry(const std::shared_ptr<Blob>& key, + const std::shared_ptr<Blob>& value) + : mKey(key), mValue(value) {} -BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce): - mKey(ce.mKey), - mValue(ce.mValue) { -} +BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce) : mKey(ce.mKey), mValue(ce.mValue) {} bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const { return *mKey < *rhs.mKey; diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h index e5c5e5bc25..50b4e4c6d0 100644 --- a/opengl/libs/EGL/BlobCache.h +++ b/opengl/libs/EGL/BlobCache.h @@ -54,8 +54,7 @@ public: // 0 < keySize // value != NULL // 0 < valueSize - void set(const void* key, size_t keySize, const void* value, - size_t valueSize); + void set(const void* key, size_t keySize, const void* value, size_t valueSize); // get retrieves from the cache the binary value associated with a given // binary key. If the key is present in the cache then the length of the @@ -75,7 +74,6 @@ public: // 0 <= valueSize size_t get(const void* key, size_t keySize, void* value, size_t valueSize); - // getFlattenedSize returns the number of bytes needed to store the entire // serialized cache. size_t getFlattenedSize() const; @@ -168,7 +166,6 @@ private: void setValue(const std::shared_ptr<Blob>& value); private: - // mKey is the key that identifies the cache entry. std::shared_ptr<Blob> mKey; @@ -245,6 +242,6 @@ private: std::vector<CacheEntry> mCacheEntries; }; -} +} // namespace android #endif // ANDROID_BLOB_CACHE_H diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp index cf67cf443b..d31373b37a 100644 --- a/opengl/libs/EGL/BlobCache_test.cpp +++ b/opengl/libs/EGL/BlobCache_test.cpp @@ -14,25 +14,24 @@ ** limitations under the License. */ +#include "BlobCache.h" + #include <fcntl.h> +#include <gtest/gtest.h> #include <stdio.h> #include <memory> -#include <gtest/gtest.h> - -#include "BlobCache.h" - namespace android { -template<typename T> using sp = std::shared_ptr<T>; +template <typename T> +using sp = std::shared_ptr<T>; class BlobCacheTest : public ::testing::Test { protected: - enum { OK = 0, - BAD_VALUE = -EINVAL + BAD_VALUE = -EINVAL, }; enum { @@ -41,19 +40,15 @@ protected: MAX_TOTAL_SIZE = 13, }; - virtual void SetUp() { - mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE)); - } + virtual void SetUp() { mBC.reset(new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE)); } - virtual void TearDown() { - mBC.reset(); - } + virtual void TearDown() { mBC.reset(); } std::unique_ptr<BlobCache> mBC; }; TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); ASSERT_EQ('e', buf[0]); @@ -63,7 +58,7 @@ TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { } TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { - unsigned char buf[2] = { 0xee, 0xee }; + unsigned char buf[2] = {0xee, 0xee}; mBC->set("ab", 2, "cd", 2); mBC->set("ef", 2, "gh", 2); ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2)); @@ -75,9 +70,9 @@ TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { } TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { - unsigned char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[6] = {0xee, 0xee, 0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); - ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4)); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf + 1, 4)); ASSERT_EQ(0xee, buf[0]); ASSERT_EQ('e', buf[1]); ASSERT_EQ('f', buf[2]); @@ -87,7 +82,7 @@ TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { } TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { - unsigned char buf[3] = { 0xee, 0xee, 0xee }; + unsigned char buf[3] = {0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3)); ASSERT_EQ(0xee, buf[0]); @@ -101,7 +96,7 @@ TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { } TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); mBC->set("abcd", 4, "ijkl", 4); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); @@ -112,9 +107,9 @@ TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { } TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { - unsigned char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[MAX_VALUE_SIZE + 1] = {0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); - mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1); ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); ASSERT_EQ('e', buf[0]); ASSERT_EQ('f', buf[1]); @@ -123,13 +118,13 @@ TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { } TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { - char key[MAX_KEY_SIZE+1]; - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; - for (int i = 0; i < MAX_KEY_SIZE+1; i++) { + char key[MAX_KEY_SIZE + 1]; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; + for (int i = 0; i < MAX_KEY_SIZE + 1; i++) { key[i] = 'a'; } - mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4); - ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4)); + mBC->set(key, MAX_KEY_SIZE + 1, "bbbb", 4); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE + 1, buf, 4)); ASSERT_EQ(0xee, buf[0]); ASSERT_EQ(0xee, buf[1]); ASSERT_EQ(0xee, buf[2]); @@ -137,16 +132,16 @@ TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { } TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) { - char buf[MAX_VALUE_SIZE+1]; - for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + char buf[MAX_VALUE_SIZE + 1]; + for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) { buf[i] = 'b'; } - mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); - for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE + 1); + for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) { buf[i] = 0xee; } - ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1)); - for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE + 1)); + for (int i = 0; i < MAX_VALUE_SIZE + 1; i++) { SCOPED_TRACE(i); ASSERT_EQ(0xee, buf[i]); } @@ -174,7 +169,7 @@ TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { char key[MAX_KEY_SIZE]; - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; for (int i = 0; i < MAX_KEY_SIZE; i++) { key[i] = 'a'; } @@ -195,8 +190,7 @@ TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) { for (int i = 0; i < MAX_VALUE_SIZE; i++) { buf[i] = 0xee; } - ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, - MAX_VALUE_SIZE)); + ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE)); for (int i = 0; i < MAX_VALUE_SIZE; i++) { SCOPED_TRACE(i); ASSERT_EQ('b', buf[i]); @@ -223,7 +217,7 @@ TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { } TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { - unsigned char buf[1] = { 0xee }; + unsigned char buf[1] = {0xee}; mBC->set("x", 1, "y", 1); ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1)); ASSERT_EQ('y', buf[0]); @@ -258,13 +252,13 @@ TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { } // Count the number of entries in the cache. int numCached = 0; - for (int i = 0; i < maxEntries+1; i++) { + for (int i = 0; i < maxEntries + 1; i++) { uint8_t k = i; if (mBC->get(&k, 1, nullptr, 0) == 1) { numCached++; } } - ASSERT_EQ(maxEntries/2 + 1, numCached); + ASSERT_EQ(maxEntries / 2 + 1, numCached); } class BlobCacheFlattenTest : public BlobCacheTest { @@ -291,7 +285,7 @@ protected: }; TEST_F(BlobCacheFlattenTest, FlattenOneValue) { - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); roundTrip(); ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4)); @@ -359,7 +353,7 @@ TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) { } TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); size_t size = mBC->getFlattenedSize(); @@ -376,7 +370,7 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { } TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); size_t size = mBC->getFlattenedSize(); @@ -395,7 +389,7 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { } TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); size_t size = mBC->getFlattenedSize(); @@ -414,7 +408,7 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { } TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { - unsigned char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + unsigned char buf[4] = {0xee, 0xee, 0xee, 0xee}; mBC->set("abcd", 4, "efgh", 4); size_t size = mBC->getFlattenedSize(); diff --git a/opengl/libs/EGL/CallStack.h b/opengl/libs/EGL/CallStack.h index 0e2a9b3248..b7fdf97cbe 100644 --- a/opengl/libs/EGL/CallStack.h +++ b/opengl/libs/EGL/CallStack.h @@ -16,8 +16,9 @@ #pragma once -#include <log/log.h> #include <backtrace/Backtrace.h> +#include <log/log.h> + #include <memory> class CallStack { @@ -30,9 +31,8 @@ public: if (backtrace->Unwind(2)) { for (size_t i = 0, c = backtrace->NumFrames(); i < c; i++) { __android_log_print(ANDROID_LOG_DEBUG, logtag, "%s", - backtrace->FormatFrameData(i).c_str()); + backtrace->FormatFrameData(i).c_str()); } } } }; - diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index d66ef2b969..1afc6934f6 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -17,27 +17,23 @@ //#define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <EGL/Loader.h> - -#include <string> - -#include <dirent.h> -#include <dlfcn.h> +#include "EGL/Loader.h" #include <android-base/properties.h> #include <android/dlext.h> +#include <dirent.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> #include <log/log.h> #include <utils/Timers.h> - -#ifndef __ANDROID_VNDK__ -#include <graphicsenv/GraphicsEnv.h> -#endif #include <vndksupport/linker.h> +#include <string> + +#include "EGL/eglext_angle.h" #include "egl_platform_entries.h" #include "egl_trace.h" #include "egldefs.h" -#include <EGL/eglext_angle.h> namespace android { @@ -159,13 +155,11 @@ static bool should_unload_system_driver(egl_connection_t* cnx) { return true; } -#ifndef __ANDROID_VNDK__ // Return true if updated driver namespace is set. ns = android::GraphicsEnv::getInstance().getDriverNamespace(); if (ns) { return true; } -#endif return false; } @@ -276,7 +270,7 @@ void* Loader::open(egl_connection_t* cnx) // will set cnx->useAngle appropriately. // Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native), // not just loading ANGLE as option. - init_angle_backend(hnd->dso[0], cnx); + init_angle_backend(hnd->dso[2], cnx); } LOG_ALWAYS_FATAL_IF(!hnd, @@ -370,7 +364,7 @@ void Loader::init_api(void* dso, f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented; /* - * GL_EXT_debug_label is special, we always report it as + * GL_EXT_debug_marker is special, we always report it as * supported, it's handled by GLES_trace. If GLES_trace is not * enabled, then these are no-ops. */ @@ -557,12 +551,8 @@ Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) { } void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) { - void* eglCreateDeviceANGLE = nullptr; - - ALOGV("dso: %p", dso); - eglCreateDeviceANGLE = dlsym(dso, "eglCreateDeviceANGLE"); - ALOGV("eglCreateDeviceANGLE: %p", eglCreateDeviceANGLE); - if (eglCreateDeviceANGLE) { + void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform"); + if (pANGLEGetDisplayPlatform) { ALOGV("ANGLE GLES library in use"); cnx->useAngle = true; } else { @@ -573,7 +563,7 @@ void Loader::init_angle_backend(void* dso, egl_connection_t* cnx) { Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) { ATRACE_CALL(); -#ifndef __ANDROID_VNDK__ + android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace(); if (!ns) { return nullptr; @@ -603,9 +593,6 @@ Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) hnd->set(dso, GLESv2); } return hnd; -#else - return nullptr; -#endif } Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h index 7b2d7c9c27..81742ab9ae 100644 --- a/opengl/libs/EGL/Loader.h +++ b/opengl/libs/EGL/Loader.h @@ -1,29 +1,26 @@ -/* +/* ** Copyright 2009, The Android Open Source Project ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at ** - ** http://www.apache.org/licenses/LICENSE-2.0 + ** http://www.apache.org/licenses/LICENSE-2.0 ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and ** limitations under the License. */ #ifndef ANDROID_EGL_LOADER_H #define ANDROID_EGL_LOADER_H -#include <stdint.h> - #include <EGL/egl.h> +#include <stdint.h> -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- struct egl_connection_t; @@ -62,16 +59,12 @@ private: void initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask); void init_angle_backend(void* dso, egl_connection_t* cnx); - static __attribute__((noinline)) - void init_api(void* dso, - char const * const * api, - char const * const * ref_api, - __eglMustCastToProperFunctionPointerType* curr, - getProcAddressType getProcAddress); + static __attribute__((noinline)) void init_api(void* dso, const char* const* api, + const char* const* ref_api, + __eglMustCastToProperFunctionPointerType* curr, + getProcAddressType getProcAddress); }; -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- #endif /* ANDROID_EGL_LOADER_H */ diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index 43f7a075a7..e5b9e14bd5 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -14,45 +14,35 @@ ** limitations under the License. */ -#include <stdlib.h> - #include <EGL/egl.h> - #include <android-base/properties.h> - #include <log/log.h> +#include <stdlib.h> #include "../egl_impl.h" - -#include "egldefs.h" -#include "egl_tls.h" -#include "egl_display.h" -#include "egl_object.h" -#include "egl_layers.h" #include "CallStack.h" #include "Loader.h" +#include "egl_display.h" +#include "egl_layers.h" +#include "egl_object.h" +#include "egl_tls.h" +#include "egldefs.h" -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- egl_connection_t gEGLImpl; gl_hooks_t gHooks[2]; gl_hooks_t gHooksNoContext; pthread_key_t gGLWrapperKey = -1; -// ---------------------------------------------------------------------------- - -void setGLHooksThreadSpecific(gl_hooks_t const *value) { +void setGLHooksThreadSpecific(gl_hooks_t const* value) { setGlThreadSpecific(value); } -/*****************************************************************************/ - static int gl_no_context() { if (egl_tls_t::logNoContextCall()) { - char const* const error = "call to OpenGL ES API with " - "no current context (logged once per thread)"; + const char* const error = "call to OpenGL ES API with " + "no current context (logged once per thread)"; if (LOG_NDEBUG) { ALOGE(error); } else { @@ -65,10 +55,9 @@ static int gl_no_context() { return 0; } -static void early_egl_init(void) -{ +static void early_egl_init(void) { int numHooks = sizeof(gHooksNoContext) / sizeof(EGLFuncPointer); - EGLFuncPointer *iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext); + EGLFuncPointer* iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext); for (int hook = 0; hook < numHooks; ++hook) { *(iter++) = reinterpret_cast<EGLFuncPointer>(gl_no_context); } @@ -76,75 +65,40 @@ static void early_egl_init(void) setGLHooksThreadSpecific(&gHooksNoContext); } -static pthread_once_t once_control = PTHREAD_ONCE_INIT; -static int sEarlyInitState = pthread_once(&once_control, &early_egl_init); - -// ---------------------------------------------------------------------------- - -egl_display_ptr validate_display(EGLDisplay dpy) { - egl_display_ptr dp = get_display(dpy); - if (!dp) - return setError(EGL_BAD_DISPLAY, egl_display_ptr(nullptr)); - if (!dp->isReady()) - return setError(EGL_NOT_INITIALIZED, egl_display_ptr(nullptr)); - - return dp; -} - -egl_display_ptr validate_display_connection(EGLDisplay dpy, - egl_connection_t*& cnx) { - cnx = nullptr; - egl_display_ptr dp = validate_display(dpy); - if (!dp) - return dp; - cnx = &gEGLImpl; - if (cnx->dso == nullptr) { - return setError(EGL_BAD_CONFIG, egl_display_ptr(nullptr)); - } - return dp; -} - -// ---------------------------------------------------------------------------- - -const GLubyte * egl_get_string_for_current_context(GLenum name) { +const GLubyte* egl_get_string_for_current_context(GLenum name) { // NOTE: returning NULL here will fall-back to the default // implementation. EGLContext context = egl_tls_t::getContext(); - if (context == EGL_NO_CONTEXT) - return nullptr; + if (context == EGL_NO_CONTEXT) return nullptr; - egl_context_t const * const c = get_context(context); + const egl_context_t* const c = get_context(context); if (c == nullptr) // this should never happen, by construction return nullptr; - if (name != GL_EXTENSIONS) - return nullptr; + if (name != GL_EXTENSIONS) return nullptr; - return (const GLubyte *)c->gl_extensions.c_str(); + return (const GLubyte*)c->gl_extensions.c_str(); } -const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index) { +const GLubyte* egl_get_string_for_current_context(GLenum name, GLuint index) { // NOTE: returning NULL here will fall-back to the default // implementation. EGLContext context = egl_tls_t::getContext(); - if (context == EGL_NO_CONTEXT) - return nullptr; + if (context == EGL_NO_CONTEXT) return nullptr; - egl_context_t const * const c = get_context(context); + const egl_context_t* const c = get_context(context); if (c == nullptr) // this should never happen, by construction return nullptr; - if (name != GL_EXTENSIONS) - return nullptr; + if (name != GL_EXTENSIONS) return nullptr; // if index is out of bounds, assume it will be in the default // implementation too, so we don't have to generate a GL error here - if (index >= c->tokenized_gl_extensions.size()) - return nullptr; + if (index >= c->tokenized_gl_extensions.size()) return nullptr; - return (const GLubyte *)c->tokenized_gl_extensions[index].c_str(); + return (const GLubyte*)c->tokenized_gl_extensions[index].c_str(); } GLint egl_get_num_extensions_for_current_context() { @@ -152,10 +106,9 @@ GLint egl_get_num_extensions_for_current_context() { // implementation. EGLContext context = egl_tls_t::getContext(); - if (context == EGL_NO_CONTEXT) - return -1; + if (context == EGL_NO_CONTEXT) return -1; - egl_context_t const * const c = get_context(context); + const egl_context_t* const c = get_context(context); if (c == nullptr) // this should never happen, by construction return -1; @@ -166,7 +119,8 @@ egl_connection_t* egl_get_connection() { return &gEGLImpl; } -// ---------------------------------------------------------------------------- +static pthread_once_t once_control = PTHREAD_ONCE_INIT; +static int sEarlyInitState = pthread_once(&once_control, &early_egl_init); static EGLBoolean egl_init_drivers_locked() { if (sEarlyInitState) { @@ -194,7 +148,6 @@ static EGLBoolean egl_init_drivers_locked() { return cnx->dso ? EGL_TRUE : EGL_FALSE; } - // this mutex protects driver load logic as a critical section since it accesses to global variable // like gEGLImpl static pthread_mutex_t sInitDriverMutex = PTHREAD_MUTEX_INITIALIZER; @@ -228,13 +181,10 @@ void gl_unimplemented() { } } -void gl_noop() { -} - -// ---------------------------------------------------------------------------- +void gl_noop() {} -void setGlThreadSpecific(gl_hooks_t const *value) { - gl_hooks_t const * volatile * tls_hooks = get_tls_hooks(); +void setGlThreadSpecific(gl_hooks_t const* value) { + gl_hooks_t const* volatile* tls_hooks = get_tls_hooks(); tls_hooks[TLS_SLOT_OPENGL_API] = value; } @@ -270,8 +220,4 @@ char const * const platform_names[] = { #undef GL_ENTRY #undef EGL_ENTRY - -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- - diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index c51a1295e7..502c14f0f1 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -20,7 +20,6 @@ #include <EGL/eglext.h> #include "../egl_impl.h" - #include "egl_layers.h" #include "egl_platform_entries.h" #include "egl_tls.h" diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp index f82c2a4ee7..dc8e5871ae 100644 --- a/opengl/libs/EGL/egl_angle_platform.cpp +++ b/opengl/libs/EGL/egl_angle_platform.cpp @@ -16,21 +16,21 @@ #if defined(__ANDROID__) -#include "Loader.h" #include "egl_angle_platform.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #include <EGL/Platform.h> #pragma GCC diagnostic pop - #include <android/dlext.h> #include <dlfcn.h> #include <graphicsenv/GraphicsEnv.h> -#include <time.h> #include <log/log.h> +#include <time.h> #include <vndksupport/linker.h> +#include "Loader.h" + namespace angle { constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so"; @@ -129,14 +129,12 @@ bool initializeAnglePlatform(EGLDisplay dpy) { return false; } - angleResetDisplayPlatform = - reinterpret_cast<ResetDisplayPlatformFunc>( - eglGetProcAddress("ANGLEResetDisplayPlatform")); + angleResetDisplayPlatform = reinterpret_cast<ResetDisplayPlatformFunc>( + eglGetProcAddress("ANGLEResetDisplayPlatform")); PlatformMethods* platformMethods = nullptr; - if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, - g_NumPlatformMethods, nullptr, - &platformMethods))) { + if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr, + &platformMethods))) { ALOGE("ANGLEGetDisplayPlatform call failed!"); return false; } diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index bcf496164b..efa67dbc1c 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -16,17 +16,14 @@ #include "egl_cache.h" -#include "../egl_impl.h" - -#include "egl_display.h" - +#include <log/log.h> #include <private/EGL/cache.h> - #include <unistd.h> #include <thread> -#include <log/log.h> +#include "../egl_impl.h" +#include "egl_display.h" // Cache size limits. static const size_t maxKeySize = 12 * 1024; @@ -36,9 +33,7 @@ static const size_t maxTotalSize = 2 * 1024 * 1024; // The time in seconds to wait before saving newly inserted cache entries. static const unsigned int deferredSaveDelay = 4; -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- #define BC_EXT_STR "EGL_ANDROID_blob_cache" @@ -50,25 +45,22 @@ void egl_set_cache_filename(const char* filename) { // // Callback functions passed to EGL. // -static void setBlob(const void* key, EGLsizeiANDROID keySize, - const void* value, EGLsizeiANDROID valueSize) { +static void setBlob(const void* key, EGLsizeiANDROID keySize, const void* value, + EGLsizeiANDROID valueSize) { egl_cache_t::get()->setBlob(key, keySize, value, valueSize); } -static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, - void* value, EGLsizeiANDROID valueSize) { +static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, void* value, + EGLsizeiANDROID valueSize) { return egl_cache_t::get()->getBlob(key, keySize, value, valueSize); } // // egl_cache_t definition // -egl_cache_t::egl_cache_t() : - mInitialized(false) { -} +egl_cache_t::egl_cache_t() : mInitialized(false) {} -egl_cache_t::~egl_cache_t() { -} +egl_cache_t::~egl_cache_t() {} egl_cache_t egl_cache_t::sCache; @@ -76,7 +68,7 @@ egl_cache_t* egl_cache_t::get() { return &sCache; } -void egl_cache_t::initialize(egl_display_t *display) { +void egl_cache_t::initialize(egl_display_t* display) { std::lock_guard<std::mutex> lock(mMutex); egl_connection_t* const cnx = &gEGLImpl; @@ -85,28 +77,26 @@ void egl_cache_t::initialize(egl_display_t *display) { size_t bcExtLen = strlen(BC_EXT_STR); size_t extsLen = strlen(exts); bool equal = !strcmp(BC_EXT_STR, exts); - bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1); - bool atEnd = (bcExtLen+1) < extsLen && - !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1)); + bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen + 1); + bool atEnd = (bcExtLen + 1) < extsLen && + !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen + 1)); bool inMiddle = strstr(exts, " " BC_EXT_STR " ") != nullptr; if (equal || atStart || atEnd || inMiddle) { PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID; - eglSetBlobCacheFuncsANDROID = - reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>( - cnx->egl.eglGetProcAddress( - "eglSetBlobCacheFuncsANDROID")); + eglSetBlobCacheFuncsANDROID = reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>( + cnx->egl.eglGetProcAddress("eglSetBlobCacheFuncsANDROID")); if (eglSetBlobCacheFuncsANDROID == nullptr) { ALOGE("EGL_ANDROID_blob_cache advertised, " - "but unable to get eglSetBlobCacheFuncsANDROID"); + "but unable to get eglSetBlobCacheFuncsANDROID"); return; } - eglSetBlobCacheFuncsANDROID(display->disp.dpy, - android::setBlob, android::getBlob); + eglSetBlobCacheFuncsANDROID(display->disp.dpy, android::setBlob, android::getBlob); EGLint err = cnx->egl.eglGetError(); if (err != EGL_SUCCESS) { ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: " - "%#x", err); + "%#x", + err); } } } @@ -122,8 +112,8 @@ void egl_cache_t::terminate() { mBlobCache = nullptr; } -void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, - const void* value, EGLsizeiANDROID valueSize) { +void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, const void* value, + EGLsizeiANDROID valueSize) { std::lock_guard<std::mutex> lock(mMutex); if (keySize < 0 || valueSize < 0) { @@ -150,8 +140,8 @@ void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, } } -EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, - void* value, EGLsizeiANDROID valueSize) { +EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, void* value, + EGLsizeiANDROID valueSize) { std::lock_guard<std::mutex> lock(mMutex); if (keySize < 0 || valueSize < 0) { @@ -178,6 +168,4 @@ BlobCache* egl_cache_t::getBlobCacheLocked() { return mBlobCache.get(); } -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h index 7382b913fa..d10a6154b7 100644 --- a/opengl/libs/EGL/egl_cache.h +++ b/opengl/libs/EGL/egl_cache.h @@ -20,21 +20,18 @@ #include <EGL/egl.h> #include <EGL/eglext.h> -#include "FileBlobCache.h" - #include <memory> #include <mutex> #include <string> -// ---------------------------------------------------------------------------- +#include "FileBlobCache.h" + namespace android { -// ---------------------------------------------------------------------------- class egl_display_t; class EGLAPI egl_cache_t { public: - // get returns a pointer to the singleton egl_cache_t object. This // singleton object will never be destroyed. static egl_cache_t* get(); @@ -117,8 +114,6 @@ private: static egl_cache_t sCache; }; -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- #endif // ANDROID_EGL_CACHE_H diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 3b1cf712a2..0b755aadca 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -14,49 +14,41 @@ ** limitations under the License. */ -#define __STDC_LIMIT_MACROS 1 #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "egl_display.h" -#include "../egl_impl.h" - -#include <EGL/eglext_angle.h> -#include <private/EGL/display.h> - -#include "Loader.h" -#include "egl_angle_platform.h" -#include "egl_cache.h" -#include "egl_object.h" -#include "egl_tls.h" - #include <SurfaceFlingerProperties.h> #include <android-base/properties.h> #include <android/dlext.h> +#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <configstore/Utils.h> #include <dlfcn.h> #include <graphicsenv/GraphicsEnv.h> -#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> -#include <configstore/Utils.h> +#include "../egl_impl.h" +#include "EGL/eglext_angle.h" +#include "Loader.h" +#include "egl_angle_platform.h" +#include "egl_cache.h" +#include "egl_object.h" +#include "egl_tls.h" +#include "private/EGL/display.h" using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- - -static char const * const sVendorString = "Android"; -static char const* const sVersionString14 = "1.4 Android META-EGL"; -static char const* const sVersionString15 = "1.5 Android META-EGL"; -static char const * const sClientApiString = "OpenGL_ES"; -extern char const * const gBuiltinExtensionString; -extern char const * const gExtensionString; +static const char* const sVendorString = "Android"; +static const char* const sVersionString14 = "1.4 Android META-EGL"; +static const char* const sVersionString15 = "1.5 Android META-EGL"; +static const char* const sClientApiString = "OpenGL_ES"; -extern void setGLHooksThreadSpecific(gl_hooks_t const *value); +extern const char* const gBuiltinExtensionString; +extern const char* const gExtensionString; -// ---------------------------------------------------------------------------- +extern void setGLHooksThreadSpecific(gl_hooks_t const* value); bool findExtension(const char* exts, const char* name, size_t nameLen) { if (exts) { @@ -82,11 +74,15 @@ int egl_get_init_count(EGLDisplay dpy) { return eglDisplay ? eglDisplay->getRefsCount() : 0; } -egl_display_t egl_display_t::sDisplay[NUM_DISPLAYS]; +std::map<EGLDisplay, std::unique_ptr<egl_display_t>> egl_display_t::displayMap; +std::mutex egl_display_t::displayMapLock; -egl_display_t::egl_display_t() : - magic('_dpy'), finishOnSwap(false), traceGpuCompletion(false), refs(0), eglIsInitialized(false) { -} +egl_display_t::egl_display_t() + : magic('_dpy'), + finishOnSwap(false), + traceGpuCompletion(false), + refs(0), + eglIsInitialized(false) {} egl_display_t::~egl_display_t() { magic = 0; @@ -98,11 +94,12 @@ egl_display_t* egl_display_t::get(EGLDisplay dpy) { return nullptr; } - uintptr_t index = uintptr_t(dpy)-1U; - if (index >= NUM_DISPLAYS || !sDisplay[index].isValid()) { + const std::lock_guard<std::mutex> lock(displayMapLock); + auto search = displayMap.find(dpy); + if (search == displayMap.end() || !search->second->isValid()) { return nullptr; } - return &sDisplay[index]; + return search->second.get(); } void egl_display_t::addObject(egl_object_t* object) { @@ -128,10 +125,9 @@ bool egl_display_t::getObject(egl_object_t* object) const { EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list) { - if (uintptr_t(disp) >= NUM_DISPLAYS) - return nullptr; + if (uintptr_t(disp) >= NUM_DISPLAYS) return nullptr; - return sDisplay[uintptr_t(disp)].getPlatformDisplay(disp, attrib_list); + return getPlatformDisplay(disp, attrib_list); } static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx, @@ -147,6 +143,16 @@ static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_conn attrs.push_back(attr[1]); } } + const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures(); + std::vector<const char*> features; + if (eglFeatures.size() > 0) { + for (const std::string& eglFeature : eglFeatures) { + features.push_back(eglFeature.c_str()); + } + features.push_back(0); + attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE); + attrs.push_back(reinterpret_cast<EGLAttrib>(features.data())); + } attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE); attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE); @@ -176,7 +182,6 @@ static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_conn EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list) { - std::lock_guard<std::mutex> _l(lock); ATRACE_CALL(); // get our driver loader @@ -204,7 +209,7 @@ EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display, // It is possible that eglGetPlatformDisplay does not have a // working implementation for Android platform; in that case, // one last fallback to eglGetDisplay - if(dpy == EGL_NO_DISPLAY) { + if (dpy == EGL_NO_DISPLAY) { if (attrib_list) { ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored"); } @@ -212,17 +217,23 @@ EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display, } } - disp.dpy = dpy; if (dpy == EGL_NO_DISPLAY) { loader.close(cnx); + } else { + const std::lock_guard<std::mutex> lock(displayMapLock); + if (displayMap.find(dpy) == displayMap.end()) { + auto d = std::make_unique<egl_display_t>(); + d->disp.dpy = dpy; + displayMap[dpy] = std::move(d); + } + return dpy; } } - return EGLDisplay(uintptr_t(display) + 1U); + return nullptr; } -EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { - +EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) { { // scope for refLock std::unique_lock<std::mutex> _l(refLock); refs++; @@ -230,7 +241,7 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { // We don't know what to report until we know what the // driver supports. Make sure we are initialized before // returning the version info. - while(!eglIsInitialized) { + while (!eglIsInitialized) { refCond.wait(_l); } egl_connection_t* const cnx = &gEGLImpl; @@ -243,7 +254,7 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (minor != nullptr) *minor = cnx->minor; return EGL_TRUE; } - while(eglIsInitialized) { + while (eglIsInitialized) { refCond.wait(_l); } } @@ -263,40 +274,31 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { if (cnx->dso) { EGLDisplay idpy = disp.dpy; if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) { - //ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p", + // ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p", // idpy, cnx->major, cnx->minor, cnx); // display is now initialized disp.state = egl_display_t::INITIALIZED; // get the query-strings for this display for each implementation - disp.queryString.vendor = cnx->egl.eglQueryString(idpy, - EGL_VENDOR); - disp.queryString.version = cnx->egl.eglQueryString(idpy, - EGL_VERSION); - disp.queryString.extensions = cnx->egl.eglQueryString(idpy, - EGL_EXTENSIONS); - disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, - EGL_CLIENT_APIS); + disp.queryString.vendor = cnx->egl.eglQueryString(idpy, EGL_VENDOR); + disp.queryString.version = cnx->egl.eglQueryString(idpy, EGL_VERSION); + disp.queryString.extensions = cnx->egl.eglQueryString(idpy, EGL_EXTENSIONS); + disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, EGL_CLIENT_APIS); } else { ALOGW("eglInitialize(%p) failed (%s)", idpy, - egl_tls_t::egl_strerror(cnx->egl.eglGetError())); + egl_tls_t::egl_strerror(cnx->egl.eglGetError())); } } if (cnx->minor == 5) { // full list in egl_entries.in - if (!cnx->egl.eglCreateImage || - !cnx->egl.eglDestroyImage || - !cnx->egl.eglGetPlatformDisplay || - !cnx->egl.eglCreatePlatformWindowSurface || - !cnx->egl.eglCreatePlatformPixmapSurface || - !cnx->egl.eglCreateSync || - !cnx->egl.eglDestroySync || - !cnx->egl.eglClientWaitSync || - !cnx->egl.eglGetSyncAttrib || - !cnx->egl.eglWaitSync) { + if (!cnx->egl.eglCreateImage || !cnx->egl.eglDestroyImage || + !cnx->egl.eglGetPlatformDisplay || !cnx->egl.eglCreatePlatformWindowSurface || + !cnx->egl.eglCreatePlatformPixmapSurface || !cnx->egl.eglCreateSync || + !cnx->egl.eglDestroySync || !cnx->egl.eglClientWaitSync || + !cnx->egl.eglGetSyncAttrib || !cnx->egl.eglWaitSync) { ALOGE("Driver indicates EGL 1.5 support, but does not have " "a critical API"); cnx->minor = 4; @@ -391,7 +393,6 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { } EGLBoolean egl_display_t::terminate() { - { // scope for refLock std::unique_lock<std::mutex> _rl(refLock); if (refs == 0) { @@ -424,7 +425,7 @@ EGLBoolean egl_display_t::terminate() { } if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) { ALOGW("eglTerminate(%p) failed (%s)", disp.dpy, - egl_tls_t::egl_strerror(cnx->egl.eglGetError())); + egl_tls_t::egl_strerror(cnx->egl.eglGetError())); } // REVISIT: it's unclear what to do if eglTerminate() fails disp.state = egl_display_t::TERMINATED; @@ -457,8 +458,7 @@ EGLBoolean egl_display_t::terminate() { return res; } -void egl_display_t::loseCurrent(egl_context_t * cur_c) -{ +void egl_display_t::loseCurrent(egl_context_t* cur_c) { if (cur_c) { egl_display_t* display = cur_c->getDisplay(); if (display) { @@ -467,8 +467,7 @@ void egl_display_t::loseCurrent(egl_context_t * cur_c) } } -void egl_display_t::loseCurrentImpl(egl_context_t * cur_c) -{ +void egl_display_t::loseCurrentImpl(egl_context_t* cur_c) { // by construction, these are either 0 or valid (possibly terminated) // it should be impossible for these to be invalid ContextRef _cur_c(cur_c); @@ -478,7 +477,6 @@ void egl_display_t::loseCurrentImpl(egl_context_t * cur_c) { // scope for the lock std::lock_guard<std::mutex> _l(lock); cur_c->onLooseCurrent(); - } // This cannot be called with the lock held because it might end-up @@ -489,10 +487,9 @@ void egl_display_t::loseCurrentImpl(egl_context_t * cur_c) _cur_d.release(); } -EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, - EGLSurface draw, EGLSurface read, EGLContext /*ctx*/, - EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx) -{ +EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, + EGLSurface read, EGLContext /*ctx*/, EGLSurface impl_draw, + EGLSurface impl_read, EGLContext impl_ctx) { EGLBoolean result; // by construction, these are either 0 or valid (possibly terminated) @@ -504,14 +501,12 @@ EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, { // scope for the lock std::lock_guard<std::mutex> _l(lock); if (c) { - result = c->cnx->egl.eglMakeCurrent( - disp.dpy, impl_draw, impl_read, impl_ctx); + result = c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx); if (result == EGL_TRUE) { c->onMakeCurrent(draw, read); } } else { - result = cur_c->cnx->egl.eglMakeCurrent( - disp.dpy, impl_draw, impl_read, impl_ctx); + result = cur_c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx); if (result == EGL_TRUE) { cur_c->onLooseCurrent(); } @@ -537,6 +532,23 @@ bool egl_display_t::haveExtension(const char* name, size_t nameLen) const { return findExtension(mExtensionString.c_str(), name, nameLen); } -// ---------------------------------------------------------------------------- +egl_display_t* validate_display(EGLDisplay dpy) { + egl_display_t* const dp = get_display(dpy); + if (!dp) return setError(EGL_BAD_DISPLAY, (egl_display_t*)nullptr); + if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (egl_display_t*)nullptr); + + return dp; +} + +egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx) { + *outCnx = nullptr; + egl_display_t* dp = validate_display(dpy); + if (!dp) return dp; + *outCnx = &gEGLImpl; + if ((*outCnx)->dso == nullptr) { + return setError(EGL_BAD_CONFIG, (egl_display_t*)nullptr); + } + return dp; +} + }; // namespace android -// ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index e117314d71..87c2176045 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -17,26 +17,22 @@ #ifndef ANDROID_EGL_DISPLAY_H #define ANDROID_EGL_DISPLAY_H - -#include <stdint.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> #include <stddef.h> +#include <stdint.h> #include <condition_variable> +#include <map> +#include <memory> #include <mutex> #include <string> #include <unordered_set> -#include <EGL/egl.h> -#include <EGL/eglext.h> - -#include <cutils/compiler.h> - -#include "egldefs.h" #include "../hooks.h" +#include "egldefs.h" -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- class egl_object_t; class egl_context_t; @@ -45,25 +41,25 @@ struct egl_connection_t; bool findExtension(const char* exts, const char* name, size_t nameLen = 0); bool needsAndroidPEglMitigation(); -// ---------------------------------------------------------------------------- - class EGLAPI egl_display_t { // marked as EGLAPI for testing purposes - static egl_display_t sDisplay[NUM_DISPLAYS]; + static std::map<EGLDisplay, std::unique_ptr<egl_display_t>> displayMap; + static std::mutex displayMapLock; EGLDisplay getDisplay(EGLNativeDisplayType display); - EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, const EGLAttrib* attrib_list); - void loseCurrentImpl(egl_context_t * cur_c); + static EGLDisplay getPlatformDisplay(EGLNativeDisplayType display, + const EGLAttrib* attrib_list); + void loseCurrentImpl(egl_context_t* cur_c); public: enum { NOT_INITIALIZED = 0, - INITIALIZED = 1, - TERMINATED = 2 + INITIALIZED = 1, + TERMINATED = 2, }; egl_display_t(); ~egl_display_t(); - EGLBoolean initialize(EGLint *major, EGLint *minor); + EGLBoolean initialize(EGLint* major, EGLint* minor); EGLBoolean terminate(); // add object to this display's list @@ -76,123 +72,69 @@ public: static egl_display_t* get(EGLDisplay dpy); static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp, const EGLAttrib* attrib_list); - EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, - EGLSurface draw, EGLSurface read, EGLContext ctx, - EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx); - static void loseCurrent(egl_context_t * cur_c); + EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw, EGLSurface read, + EGLContext ctx, EGLSurface impl_draw, EGLSurface impl_read, + EGLContext impl_ctx); + static void loseCurrent(egl_context_t* cur_c); inline bool isReady() const { return (refs > 0); } inline bool isValid() const { return magic == '_dpy'; } inline bool isAlive() const { return isValid(); } - char const * getVendorString() const { return mVendorString.c_str(); } - char const * getVersionString() const { return mVersionString.c_str(); } - char const * getClientApiString() const { return mClientApiString.c_str(); } - char const * getExtensionString() const { return mExtensionString.c_str(); } + char const* getVendorString() const { return mVendorString.c_str(); } + char const* getVersionString() const { return mVersionString.c_str(); } + char const* getClientApiString() const { return mClientApiString.c_str(); } + char const* getExtensionString() const { return mExtensionString.c_str(); } bool haveExtension(const char* name, size_t nameLen = 0) const; inline uint32_t getRefsCount() const { return refs; } struct strings_t { - char const * vendor; - char const * version; - char const * clientApi; - char const * extensions; + char const* vendor; + char const* version; + char const* clientApi; + char const* extensions; }; struct DisplayImpl { - DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) { } - EGLDisplay dpy; - EGLint state; - strings_t queryString; + DisplayImpl() : dpy(EGL_NO_DISPLAY), state(NOT_INITIALIZED) {} + EGLDisplay dpy; + EGLint state; + strings_t queryString; }; private: - uint32_t magic; - -public: - DisplayImpl disp; - bool finishOnSwap; // property: debug.egl.finish - bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion - bool hasColorSpaceSupport; - -private: - friend class egl_display_ptr; - - uint32_t refs; - bool eglIsInitialized; - mutable std::mutex lock; - mutable std::mutex refLock; - mutable std::condition_variable refCond; - std::unordered_set<egl_object_t*> objects; - std::string mVendorString; - std::string mVersionString; - std::string mClientApiString; - std::string mExtensionString; -}; - -// ---------------------------------------------------------------------------- + uint32_t magic; -// An egl_display_ptr is a kind of smart pointer for egl_display_t objects. -// It doesn't refcount the egl_display_t, but does ensure that the underlying -// EGL implementation is "awake" (not hibernating) and ready for use as long -// as the egl_display_ptr exists. -class egl_display_ptr { public: - explicit egl_display_ptr(egl_display_t* dpy): mDpy(dpy) {} - - // We only really need a C++11 move constructor, not a copy constructor. - // A move constructor would save an enter()/leave() pair on every EGL API - // call. But enabling -std=c++0x causes lots of errors elsewhere, so I - // can't use a move constructor until those are cleaned up. - // - // egl_display_ptr(egl_display_ptr&& other) { - // mDpy = other.mDpy; - // other.mDpy = NULL; - // } - // - egl_display_ptr(const egl_display_ptr& other): mDpy(other.mDpy) {} - - ~egl_display_ptr() {} - - const egl_display_t* operator->() const { return mDpy; } - egl_display_t* operator->() { return mDpy; } - - const egl_display_t* get() const { return mDpy; } - egl_display_t* get() { return mDpy; } - - operator bool() const { return mDpy != nullptr; } + DisplayImpl disp; + bool finishOnSwap; // property: debug.egl.finish + bool traceGpuCompletion; // property: debug.egl.traceGpuCompletion + bool hasColorSpaceSupport; private: - egl_display_t* mDpy; - - // non-assignable - egl_display_ptr& operator=(const egl_display_ptr&); + uint32_t refs; + bool eglIsInitialized; + mutable std::mutex lock; + mutable std::mutex refLock; + mutable std::condition_variable refCond; + std::unordered_set<egl_object_t*> objects; + std::string mVendorString; + std::string mVersionString; + std::string mClientApiString; + std::string mExtensionString; }; -// ---------------------------------------------------------------------------- - -inline egl_display_ptr get_display(EGLDisplay dpy) { - return egl_display_ptr(egl_display_t::get(dpy)); -} - -// Does not ensure EGL is unhibernated. Use with caution: calls into the -// underlying EGL implementation are not safe. -inline egl_display_t* get_display_nowake(EGLDisplay dpy) { +inline egl_display_t* get_display(EGLDisplay dpy) { return egl_display_t::get(dpy); } -// ---------------------------------------------------------------------------- - -egl_display_ptr validate_display(EGLDisplay dpy); -egl_display_ptr validate_display_connection(EGLDisplay dpy, - egl_connection_t*& cnx); +egl_display_t* validate_display(EGLDisplay dpy); +egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx); EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx); EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface); -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- #endif // ANDROID_EGL_DISPLAY_H diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in index 2921d512f1..1c91f1d61b 100644 --- a/opengl/libs/EGL/egl_entries.in +++ b/opengl/libs/EGL/egl_entries.in @@ -104,11 +104,6 @@ EGL_ENTRY(EGLClientBuffer, eglGetNativeClientBufferANDROID, const AHardwareBuffe EGL_ENTRY(EGLuint64NV, eglGetSystemTimeFrequencyNV, void) EGL_ENTRY(EGLuint64NV, eglGetSystemTimeNV, void) -/* IMG extensions */ - -EGL_ENTRY(EGLBoolean, eglHibernateProcessIMG, void) -EGL_ENTRY(EGLBoolean, eglAwakenProcessIMG, void) - /* Partial update extensions */ EGL_ENTRY(EGLBoolean, eglSwapBuffersWithDamageKHR, EGLDisplay, EGLSurface, EGLint *, EGLint) diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp index ea86c9a05b..9752e382d9 100644 --- a/opengl/libs/EGL/egl_layers.cpp +++ b/opengl/libs/EGL/egl_layers.cpp @@ -85,17 +85,21 @@ const void* getNextLayerProcAddress(void* layer_id, const char* name) { // Look up which GPA we should use int gpaIndex = func_indices["eglGetProcAddress"]; - ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, gpaIndex); + ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) <- using GPA from this index", name, + gpaIndex); EGLFuncPointer gpaNext = (*next_layer_funcs)[gpaIndex]; - ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this address", name, gpaIndex, (unsigned long long)gpaNext); - + ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) <- using GPA at this " + "address", + name, gpaIndex, (unsigned long long)gpaNext); // Call it for the requested function typedef void* (*PFNEGLGETPROCADDRESSPROC)(const char*); PFNEGLGETPROCADDRESSPROC next = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(gpaNext); val = reinterpret_cast<EGLFuncPointer>(next(name)); - ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from GPA", name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val); + ALOGV("getNextLayerProcAddress - name(%s) gpaIndex(%i) gpaNext(%llu) Got back (%llu) from " + "GPA", + name, gpaIndex, (unsigned long long)gpaNext, (unsigned long long)val); // We should store it now, but to do that, we need to move func_idx to the class so we can // increment it separately @@ -105,7 +109,9 @@ const void* getNextLayerProcAddress(void* layer_id, const char* name) { int index = func_indices[name]; val = (*next_layer_funcs)[index]; - ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known entry", name, index, (unsigned long long)val); + ALOGV("getNextLayerProcAddress - name(%s) index(%i) entry(%llu) - Got a hit, returning known " + "entry", + name, index, (unsigned long long)val); return reinterpret_cast<void*>(val); } @@ -117,20 +123,26 @@ void SetupFuncMaps(FunctionTable& functions, char const* const* entries, EGLFunc // Some names overlap, only fill with initial entry // This does mean that some indices will not be used if (func_indices.find(name) == func_indices.end()) { - ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning now", name, func_idx); + ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for func_indices, assigning " + "now", + name, func_idx); func_names[func_idx] = name; func_indices[name] = func_idx; } else { - ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, func_idx); + ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for func_indices", name, + func_idx); } // Populate layer_functions once with initial value // These values will arrive in priority order, starting with platform entries if (functions[func_idx] == nullptr) { - ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning (%llu)", name, func_idx, (unsigned long long) *curr); + ALOGV("SetupFuncMaps - name(%s), func_idx(%i), No entry for functions, assigning " + "(%llu)", + name, func_idx, (unsigned long long)*curr); functions[func_idx] = *curr; } else { - ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, func_idx, (unsigned long long) functions[func_idx]); + ALOGV("SetupFuncMaps - name(%s), func_idx(%i), Found entry for functions (%llu)", name, + func_idx, (unsigned long long)functions[func_idx]); } entries++; @@ -380,8 +392,8 @@ void LayerLoader::LoadLayers() { auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace(); if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) { char* error_message = nullptr; - dlhandle_ = OpenNativeLibraryInNamespace( - app_namespace, layer.c_str(), &native_bridge_, &error_message); + dlhandle_ = OpenNativeLibraryInNamespace(app_namespace, layer.c_str(), + &native_bridge_, &error_message); if (!dlhandle_) { ALOGE("Failed to load layer %s with error: %s", layer.c_str(), error_message); diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h index 1e2783fc98..705525d857 100644 --- a/opengl/libs/EGL/egl_layers.h +++ b/opengl/libs/EGL/egl_layers.h @@ -17,19 +17,18 @@ #ifndef ANDROID_EGL_LAYERS_H #define ANDROID_EGL_LAYERS_H +#include <EGL/egldefs.h> +#include <android/dlext.h> +#include <dlfcn.h> +#include <nativebridge/native_bridge.h> +#include <nativeloader/native_loader.h> + #include <string> #include <unordered_map> #include <vector> -#include <android/dlext.h> -#include <dlfcn.h> - -#include <EGL/egldefs.h> #include "egl_platform_entries.h" -#include <nativebridge/native_bridge.h> -#include <nativeloader/native_loader.h> - typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; namespace android { @@ -46,8 +45,8 @@ public: void LoadLayers(); void InitLayers(egl_connection_t*); - void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*); - void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, char const* const*); + void LayerPlatformEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*); + void LayerDriverEntries(layer_setup_func layer_setup, EGLFuncPointer*, const char* const*); bool Initialized(); std::string GetDebugLayers(); @@ -59,18 +58,23 @@ public: std::vector<layer_setup_func> layer_setup_; private: - LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0), dlhandle_(nullptr), native_bridge_(false){}; + LayerLoader() + : layers_loaded_(false), + initialized_(false), + current_layer_(0), + dlhandle_(nullptr), + native_bridge_(false){}; bool layers_loaded_; bool initialized_; unsigned current_layer_; void* dlhandle_; bool native_bridge_; - template<typename Func = void*> + template <typename Func = void*> Func GetTrampoline(const char* name) const { if (native_bridge_) { - return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline( - dlhandle_, name, nullptr, 0)); + return reinterpret_cast<Func>( + android::NativeBridgeGetTrampoline(dlhandle_, name, nullptr, 0)); } return reinterpret_cast<Func>(dlsym(dlhandle_, name)); } diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp index ff4fe2dd9c..847b3519b1 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -18,19 +18,14 @@ #include <sstream> - -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- -egl_object_t::egl_object_t(egl_display_t* disp) : - display(disp), count(1) { +egl_object_t::egl_object_t(egl_display_t* disp) : display(disp), count(1) { // NOTE: this does an implicit incRef display->addObject(this); } -egl_object_t::~egl_object_t() { -} +egl_object_t::~egl_object_t() {} void egl_object_t::terminate() { // this marks the object as "terminated" @@ -53,8 +48,6 @@ bool egl_object_t::get(egl_display_t const* display, egl_object_t* object) { return display->getObject(object); } -// ---------------------------------------------------------------------------- - egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWindowType win, EGLSurface surface, EGLint colorSpace, egl_connection_t const* cnx) : egl_object_t(dpy), @@ -66,10 +59,10 @@ egl_surface_t::egl_surface_t(egl_display_t* dpy, EGLConfig config, EGLNativeWind colorSpace(colorSpace), egl_smpte2086_dirty(false), egl_cta861_3_dirty(false) { - egl_smpte2086_metadata.displayPrimaryRed = { EGL_DONT_CARE, EGL_DONT_CARE }; - egl_smpte2086_metadata.displayPrimaryGreen = { EGL_DONT_CARE, EGL_DONT_CARE }; - egl_smpte2086_metadata.displayPrimaryBlue = { EGL_DONT_CARE, EGL_DONT_CARE }; - egl_smpte2086_metadata.whitePoint = { EGL_DONT_CARE, EGL_DONT_CARE }; + egl_smpte2086_metadata.displayPrimaryRed = {EGL_DONT_CARE, EGL_DONT_CARE}; + egl_smpte2086_metadata.displayPrimaryGreen = {EGL_DONT_CARE, EGL_DONT_CARE}; + egl_smpte2086_metadata.displayPrimaryBlue = {EGL_DONT_CARE, EGL_DONT_CARE}; + egl_smpte2086_metadata.whitePoint = {EGL_DONT_CARE, EGL_DONT_CARE}; egl_smpte2086_metadata.maxLuminance = EGL_DONT_CARE; egl_smpte2086_metadata.minLuminance = EGL_DONT_CARE; egl_cta861_3_metadata.maxFrameAverageLightLevel = EGL_DONT_CARE; @@ -173,16 +166,30 @@ EGLBoolean egl_surface_t::getSmpte2086Metadata(android_smpte2086_metadata& metad return EGL_FALSE; } - metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) / EGL_METADATA_SCALING_EXT; - metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) / EGL_METADATA_SCALING_EXT; - metadata.displayPrimaryGreen.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) / EGL_METADATA_SCALING_EXT; - metadata.displayPrimaryGreen.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) / EGL_METADATA_SCALING_EXT; - metadata.displayPrimaryBlue.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) / EGL_METADATA_SCALING_EXT; - metadata.displayPrimaryBlue.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) / EGL_METADATA_SCALING_EXT; - metadata.whitePoint.x = static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT; - metadata.whitePoint.y = static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT; - metadata.maxLuminance = static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT; - metadata.minLuminance = static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT; + metadata.displayPrimaryRed.x = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.x) / + EGL_METADATA_SCALING_EXT; + metadata.displayPrimaryRed.y = static_cast<float>(egl_smpte2086_metadata.displayPrimaryRed.y) / + EGL_METADATA_SCALING_EXT; + metadata.displayPrimaryGreen.x = + static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.x) / + EGL_METADATA_SCALING_EXT; + metadata.displayPrimaryGreen.y = + static_cast<float>(egl_smpte2086_metadata.displayPrimaryGreen.y) / + EGL_METADATA_SCALING_EXT; + metadata.displayPrimaryBlue.x = + static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.x) / + EGL_METADATA_SCALING_EXT; + metadata.displayPrimaryBlue.y = + static_cast<float>(egl_smpte2086_metadata.displayPrimaryBlue.y) / + EGL_METADATA_SCALING_EXT; + metadata.whitePoint.x = + static_cast<float>(egl_smpte2086_metadata.whitePoint.x) / EGL_METADATA_SCALING_EXT; + metadata.whitePoint.y = + static_cast<float>(egl_smpte2086_metadata.whitePoint.y) / EGL_METADATA_SCALING_EXT; + metadata.maxLuminance = + static_cast<float>(egl_smpte2086_metadata.maxLuminance) / EGL_METADATA_SCALING_EXT; + metadata.minLuminance = + static_cast<float>(egl_smpte2086_metadata.minLuminance) / EGL_METADATA_SCALING_EXT; return EGL_TRUE; } @@ -196,13 +203,15 @@ EGLBoolean egl_surface_t::getCta8613Metadata(android_cta861_3_metadata& metadata return EGL_FALSE; } - metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) / EGL_METADATA_SCALING_EXT; - metadata.maxFrameAverageLightLevel = static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) / EGL_METADATA_SCALING_EXT; + metadata.maxContentLightLevel = static_cast<float>(egl_cta861_3_metadata.maxContentLightLevel) / + EGL_METADATA_SCALING_EXT; + metadata.maxFrameAverageLightLevel = + static_cast<float>(egl_cta861_3_metadata.maxFrameAverageLightLevel) / + EGL_METADATA_SCALING_EXT; return EGL_TRUE; } - EGLBoolean egl_surface_t::getColorSpaceAttribute(EGLint attribute, EGLint* value) const { if (attribute == EGL_GL_COLORSPACE_KHR) { *value = colorSpace; @@ -211,7 +220,7 @@ EGLBoolean egl_surface_t::getColorSpaceAttribute(EGLint attribute, EGLint* value return EGL_FALSE; } -EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint *value) const { +EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint* value) const { switch (attribute) { case EGL_SMPTE2086_DISPLAY_PRIMARY_RX_EXT: *value = egl_smpte2086_metadata.displayPrimaryRed.x; @@ -257,7 +266,7 @@ EGLBoolean egl_surface_t::getSmpte2086Attribute(EGLint attribute, EGLint *value) return EGL_FALSE; } -EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint *value) const { +EGLBoolean egl_surface_t::getCta8613Attribute(EGLint attribute, EGLint* value) const { switch (attribute) { case EGL_CTA861_3_MAX_CONTENT_LIGHT_LEVEL_EXT: *value = egl_cta861_3_metadata.maxContentLightLevel; @@ -276,13 +285,16 @@ void egl_surface_t::terminate() { egl_object_t::terminate(); } -// ---------------------------------------------------------------------------- - egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, - egl_connection_t const* cnx, int version) : - egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context), - config(config), read(nullptr), draw(nullptr), cnx(cnx), version(version) { -} + egl_connection_t const* cnx, int version) + : egl_object_t(get_display(dpy)), + dpy(dpy), + context(context), + config(config), + read(nullptr), + draw(nullptr), + cnx(cnx), + version(version) {} void egl_context_t::onLooseCurrent() { read = nullptr; @@ -297,31 +309,39 @@ void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) { * Here we cache the GL_EXTENSIONS string for this context and we * add the extensions always handled by the wrapper */ + if (!gl_extensions.empty()) return; + + // call the implementation's glGetString(GL_EXTENSIONS) + const char* exts = (const char*)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS); + if (!exts) return; + + // If this context is sharing with another context, and the other context was reset + // e.g. due to robustness failure, this context might also be reset and glGetString can + // return NULL. + gl_extensions = exts; + if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) { + gl_extensions.insert(0, "GL_EXT_debug_marker "); + // eglGetProcAddress could return function pointers to these + // functions while they actually don't work. Fix them now. + __eglMustCastToProperFunctionPointerType* f; + f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version] + ->gl.glInsertEventMarkerEXT; + if (*f != gl_noop) *f = gl_noop; + f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version] + ->gl.glPushGroupMarkerEXT; + if (*f != gl_noop) *f = gl_noop; + f = (__eglMustCastToProperFunctionPointerType*)&gEGLImpl.hooks[version] + ->gl.glPopGroupMarkerEXT; + if (*f != gl_noop) *f = gl_noop; + } - if (gl_extensions.empty()) { - // call the implementation's glGetString(GL_EXTENSIONS) - const char* exts = (const char *)gEGLImpl.hooks[version]->gl.glGetString(GL_EXTENSIONS); - - // If this context is sharing with another context, and the other context was reset - // e.g. due to robustness failure, this context might also be reset and glGetString can - // return NULL. - if (exts) { - gl_extensions = exts; - if (gl_extensions.find("GL_EXT_debug_marker") == std::string::npos) { - gl_extensions.insert(0, "GL_EXT_debug_marker "); - } - - // tokenize the supported extensions for the glGetStringi() wrapper - std::stringstream ss; - std::string str; - ss << gl_extensions; - while (ss >> str) { - tokenized_gl_extensions.push_back(str); - } - } + // tokenize the supported extensions for the glGetStringi() wrapper + std::stringstream ss; + std::string str; + ss << gl_extensions; + while (ss >> str) { + tokenized_gl_extensions.push_back(str); } } -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h index fb2bdf4c51..e593b1cf5b 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -17,30 +17,25 @@ #ifndef ANDROID_EGL_OBJECT_H #define ANDROID_EGL_OBJECT_H -#include <atomic> -#include <stdint.h> -#include <stddef.h> - -#include <string> -#include <vector> - #include <EGL/egl.h> #include <EGL/eglext.h> - +#include <log/log.h> +#include <stddef.h> +#include <stdint.h> #include <system/window.h> -#include <log/log.h> +#include <atomic> +#include <string> +#include <vector> #include "egl_display.h" -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- class egl_display_t; class egl_object_t { - egl_display_t *display; + egl_display_t* display; mutable std::atomic_size_t count; protected: @@ -64,6 +59,7 @@ public: egl_object_t* ref; LocalRef() = delete; LocalRef(const LocalRef* rhs) = delete; + public: ~LocalRef(); explicit LocalRef(egl_object_t* rhs); @@ -73,9 +69,7 @@ public: ref = native; } } - inline N* get() { - return static_cast<N*>(ref); - } + inline N* get() { return static_cast<N*>(ref); } void acquire() const; void release() const; void terminate(); @@ -84,7 +78,7 @@ public: friend class LocalRef; }; -template<typename N, typename T> +template <typename N, typename T> egl_object_t::LocalRef<N, T>::LocalRef(egl_object_t* rhs) : ref(rhs) { if (ref) { ref->incRef(); @@ -92,21 +86,21 @@ egl_object_t::LocalRef<N, T>::LocalRef(egl_object_t* rhs) : ref(rhs) { } template <typename N, typename T> -egl_object_t::LocalRef<N,T>::~LocalRef() { +egl_object_t::LocalRef<N, T>::~LocalRef() { if (ref) { ref->destroy(); } } template <typename N, typename T> -void egl_object_t::LocalRef<N,T>::acquire() const { +void egl_object_t::LocalRef<N, T>::acquire() const { if (ref) { ref->incRef(); } } template <typename N, typename T> -void egl_object_t::LocalRef<N,T>::release() const { +void egl_object_t::LocalRef<N, T>::release() const { if (ref) { if (ref->decRef() == 1) { // shouldn't happen because this is called from LocalRef @@ -116,7 +110,7 @@ void egl_object_t::LocalRef<N,T>::release() const { } template <typename N, typename T> -void egl_object_t::LocalRef<N,T>::terminate() { +void egl_object_t::LocalRef<N, T>::terminate() { if (ref) { ref->terminate(); } @@ -128,6 +122,7 @@ class egl_surface_t : public egl_object_t { protected: ~egl_surface_t(); void terminate() override; + public: typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref; @@ -151,10 +146,13 @@ public: // it's not hard to imagine native games accessing them. EGLSurface surface; EGLConfig config; + private: ANativeWindow* win; + public: egl_connection_t const* cnx; + private: bool connected; void disconnect(); @@ -186,14 +184,15 @@ private: egl_cta861_3_metadata egl_cta861_3_metadata; }; -class egl_context_t: public egl_object_t { +class egl_context_t : public egl_object_t { protected: ~egl_context_t() {} + public: typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref; - egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, - egl_connection_t const* cnx, int version); + egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, egl_connection_t const* cnx, + int version); void onLooseCurrent(); void onMakeCurrent(EGLSurface draw, EGLSurface read); @@ -209,30 +208,22 @@ public: std::vector<std::string> tokenized_gl_extensions; }; -// ---------------------------------------------------------------------------- - -typedef egl_surface_t::Ref SurfaceRef; -typedef egl_context_t::Ref ContextRef; +typedef egl_surface_t::Ref SurfaceRef; +typedef egl_context_t::Ref ContextRef; -// ---------------------------------------------------------------------------- - -template<typename NATIVE, typename EGL> +template <typename NATIVE, typename EGL> static inline NATIVE* egl_to_native_cast(EGL arg) { return reinterpret_cast<NATIVE*>(arg); } -static inline -egl_surface_t* get_surface(EGLSurface surface) { +static inline egl_surface_t* get_surface(EGLSurface surface) { return egl_to_native_cast<egl_surface_t>(surface); } -static inline -egl_context_t* get_context(EGLContext context) { +static inline egl_context_t* get_context(EGLContext context) { return egl_to_native_cast<egl_context_t>(context); } -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- #endif // ANDROID_EGL_OBJECT_H diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index aa24e8ee68..398efc0a76 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -18,36 +18,32 @@ #include "egl_platform_entries.h" -#include <ctype.h> -#include <dlfcn.h> -#include <stdlib.h> -#include <string.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <EGL/eglext_angle.h> - #include <android-base/properties.h> #include <android-base/strings.h> #include <android/hardware_buffer.h> -#include <graphicsenv/GraphicsEnv.h> -#include <private/android/AHardwareBufferHelpers.h> - +#include <ctype.h> #include <cutils/compiler.h> +#include <dlfcn.h> +#include <graphicsenv/GraphicsEnv.h> #include <log/log.h> +#include <private/android/AHardwareBufferHelpers.h> +#include <stdlib.h> +#include <string.h> #include <condition_variable> #include <deque> #include <mutex> -#include <unordered_map> #include <string> #include <thread> +#include <unordered_map> #include "../egl_impl.h" - +#include "EGL/egl.h" +#include "EGL/eglext.h" +#include "EGL/eglext_angle.h" #include "egl_display.h" -#include "egl_object.h" #include "egl_layers.h" +#include "egl_object.h" #include "egl_tls.h" #include "egl_trace.h" @@ -80,71 +76,70 @@ struct extension_map_t { * NOTE: Both strings MUST have a single space as the last character. */ -extern char const * const gBuiltinExtensionString; -extern char const * const gExtensionString; +extern const char* const gBuiltinExtensionString; +extern const char* const gExtensionString; // clang-format off // Extensions implemented by the EGL wrapper. -char const * const gBuiltinExtensionString = - "EGL_KHR_get_all_proc_addresses " - "EGL_ANDROID_presentation_time " - "EGL_KHR_swap_buffers_with_damage " - "EGL_ANDROID_get_native_client_buffer " +const char* const gBuiltinExtensionString = "EGL_ANDROID_front_buffer_auto_refresh " "EGL_ANDROID_get_frame_timestamps " - "EGL_EXT_surface_SMPTE2086_metadata " + "EGL_ANDROID_get_native_client_buffer " + "EGL_ANDROID_presentation_time " "EGL_EXT_surface_CTA861_3_metadata " + "EGL_EXT_surface_SMPTE2086_metadata " + "EGL_KHR_get_all_proc_addresses " + "EGL_KHR_swap_buffers_with_damage " ; // Allowed list of extensions exposed to applications if implemented in the vendor driver. -char const * const gExtensionString = - "EGL_KHR_image " // mandatory - "EGL_KHR_image_base " // mandatory +const char* const gExtensionString = + "EGL_ANDROID_image_native_buffer " // mandatory + "EGL_ANDROID_native_fence_sync " // strongly recommended + "EGL_ANDROID_recordable " // mandatory + "EGL_EXT_buffer_age " // strongly recommended with partial_update + "EGL_EXT_create_context_robustness " "EGL_EXT_image_gl_colorspace " - "EGL_KHR_image_pixmap " - "EGL_KHR_lock_surface " + "EGL_EXT_pixel_format_float " + "EGL_EXT_protected_content " + "EGL_EXT_yuv_surface " + "EGL_IMG_context_priority " + "EGL_KHR_config_attribs " + "EGL_KHR_create_context " + "EGL_KHR_create_context_no_error " + "EGL_KHR_fence_sync " "EGL_KHR_gl_colorspace " + "EGL_KHR_gl_renderbuffer_image " "EGL_KHR_gl_texture_2D_image " "EGL_KHR_gl_texture_3D_image " "EGL_KHR_gl_texture_cubemap_image " - "EGL_KHR_gl_renderbuffer_image " + "EGL_KHR_image " // mandatory + "EGL_KHR_image_base " // mandatory + "EGL_KHR_image_pixmap " + "EGL_KHR_lock_surface " + "EGL_KHR_mutable_render_buffer " + "EGL_KHR_no_config_context " + "EGL_KHR_partial_update " // strongly recommended "EGL_KHR_reusable_sync " - "EGL_KHR_fence_sync " - "EGL_KHR_create_context " - "EGL_KHR_config_attribs " - "EGL_KHR_surfaceless_context " "EGL_KHR_stream " - "EGL_KHR_stream_fifo " - "EGL_KHR_stream_producer_eglsurface " "EGL_KHR_stream_consumer_gltexture " "EGL_KHR_stream_cross_process_fd " - "EGL_EXT_create_context_robustness " - "EGL_NV_system_time " - "EGL_ANDROID_image_native_buffer " // mandatory + "EGL_KHR_stream_fifo " + "EGL_KHR_stream_producer_eglsurface " + "EGL_KHR_surfaceless_context " "EGL_KHR_wait_sync " // strongly recommended - "EGL_ANDROID_recordable " // mandatory - "EGL_KHR_partial_update " // strongly recommended - "EGL_EXT_pixel_format_float " - "EGL_EXT_buffer_age " // strongly recommended with partial_update - "EGL_KHR_create_context_no_error " - "EGL_KHR_mutable_render_buffer " - "EGL_EXT_yuv_surface " - "EGL_EXT_protected_content " - "EGL_IMG_context_priority " - "EGL_KHR_no_config_context " + "EGL_NV_system_time " ; -char const * const gClientExtensionString = +const char* const gClientExtensionString = + "EGL_ANDROID_GLES_layers " + "EGL_ANGLE_platform_angle " "EGL_EXT_client_extensions " "EGL_KHR_platform_android " - "EGL_ANGLE_platform_angle " - "EGL_ANDROID_GLES_layers"; -// clang-format on + ; // extensions not exposed to applications but used by the ANDROID system // "EGL_ANDROID_blob_cache " // strongly recommended -// "EGL_IMG_hibernate_process " // optional -// "EGL_ANDROID_native_fence_sync " // strongly recommended // "EGL_ANDROID_framebuffer_target " // mandatory for HWC 1.1 /* @@ -154,105 +149,69 @@ char const * const gClientExtensionString = */ static const extension_map_t sExtensionMap[] = { // EGL_KHR_lock_surface - { "eglLockSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, - { "eglUnlockSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, + { "eglLockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR }, + { "eglUnlockSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR }, // EGL_KHR_image, EGL_KHR_image_base - { "eglCreateImageKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, - { "eglDestroyImageKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, + { "eglCreateImageKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, + { "eglDestroyImageKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, // EGL_KHR_reusable_sync, EGL_KHR_fence_sync - { "eglCreateSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR }, - { "eglDestroySyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR }, - { "eglClientWaitSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR }, - { "eglSignalSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR }, - { "eglGetSyncAttribKHR", - (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR }, + { "eglCreateSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR }, + { "eglDestroySyncKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR }, + { "eglClientWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR }, + { "eglSignalSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglSignalSyncKHR }, + { "eglGetSyncAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR }, // EGL_NV_system_time - { "eglGetSystemTimeFrequencyNV", - (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, - { "eglGetSystemTimeNV", - (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, + { "eglGetSystemTimeFrequencyNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, + { "eglGetSystemTimeNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV }, // EGL_KHR_wait_sync - { "eglWaitSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR }, + { "eglWaitSyncKHR", (__eglMustCastToProperFunctionPointerType)&eglWaitSyncKHR }, // EGL_ANDROID_presentation_time - { "eglPresentationTimeANDROID", - (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID }, + { "eglPresentationTimeANDROID", (__eglMustCastToProperFunctionPointerType)&eglPresentationTimeANDROID }, // EGL_KHR_swap_buffers_with_damage - { "eglSwapBuffersWithDamageKHR", - (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR }, + { "eglSwapBuffersWithDamageKHR", (__eglMustCastToProperFunctionPointerType)&eglSwapBuffersWithDamageKHR }, // EGL_ANDROID_get_native_client_buffer - { "eglGetNativeClientBufferANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID }, + { "eglGetNativeClientBufferANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNativeClientBufferANDROID }, // EGL_KHR_partial_update - { "eglSetDamageRegionKHR", - (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR }, - - { "eglCreateStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR }, - { "eglDestroyStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR }, - { "eglStreamAttribKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR }, - { "eglQueryStreamKHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR }, - { "eglQueryStreamu64KHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR }, - { "eglQueryStreamTimeKHR", - (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR }, - { "eglCreateStreamProducerSurfaceKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR }, - { "eglStreamConsumerGLTextureExternalKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR }, - { "eglStreamConsumerAcquireKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR }, - { "eglStreamConsumerReleaseKHR", - (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR }, - { "eglGetStreamFileDescriptorKHR", - (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, - { "eglCreateStreamFromFileDescriptorKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, + { "eglSetDamageRegionKHR", (__eglMustCastToProperFunctionPointerType)&eglSetDamageRegionKHR }, + + { "eglCreateStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamKHR }, + { "eglDestroyStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyStreamKHR }, + { "eglStreamAttribKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamAttribKHR }, + { "eglQueryStreamKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamKHR }, + { "eglQueryStreamu64KHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamu64KHR }, + { "eglQueryStreamTimeKHR", (__eglMustCastToProperFunctionPointerType)&eglQueryStreamTimeKHR }, + { "eglCreateStreamProducerSurfaceKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamProducerSurfaceKHR }, + { "eglStreamConsumerGLTextureExternalKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerGLTextureExternalKHR }, + { "eglStreamConsumerAcquireKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerAcquireKHR }, + { "eglStreamConsumerReleaseKHR", (__eglMustCastToProperFunctionPointerType)&eglStreamConsumerReleaseKHR }, + { "eglGetStreamFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglGetStreamFileDescriptorKHR }, + { "eglCreateStreamFromFileDescriptorKHR", (__eglMustCastToProperFunctionPointerType)&eglCreateStreamFromFileDescriptorKHR }, // EGL_ANDROID_get_frame_timestamps - { "eglGetNextFrameIdANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, - { "eglGetCompositorTimingANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, - { "eglGetCompositorTimingSupportedANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, - { "eglGetFrameTimestampsANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, - { "eglGetFrameTimestampSupportedANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, + { "eglGetNextFrameIdANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetNextFrameIdANDROID }, + { "eglGetCompositorTimingANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingANDROID }, + { "eglGetCompositorTimingSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetCompositorTimingSupportedANDROID }, + { "eglGetFrameTimestampsANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampsANDROID }, + { "eglGetFrameTimestampSupportedANDROID", (__eglMustCastToProperFunctionPointerType)&eglGetFrameTimestampSupportedANDROID }, // EGL_ANDROID_native_fence_sync - { "eglDupNativeFenceFDANDROID", - (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID }, + { "eglDupNativeFenceFDANDROID", (__eglMustCastToProperFunctionPointerType)&eglDupNativeFenceFDANDROID }, }; +// clang-format on /* * These extensions entry-points should not be exposed to applications. * They're used internally by the Android EGL layer. */ -#define FILTER_EXTENSIONS(procname) \ - (!strcmp((procname), "eglSetBlobCacheFuncsANDROID") || \ - !strcmp((procname), "eglHibernateProcessIMG") || \ - !strcmp((procname), "eglAwakenProcessIMG")) +#define FILTER_EXTENSIONS(procname) (!strcmp((procname), "eglSetBlobCacheFuncsANDROID")) // accesses protected by sExtensionMapMutex static std::unordered_map<std::string, __eglMustCastToProperFunctionPointerType> sGLExtensionMap; @@ -261,9 +220,8 @@ static std::unordered_map<std::string, int> sGLExtensionSlotMap; static int sGLExtensionSlot = 0; static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER; -static void(*findProcAddress(const char* name, - const extension_map_t* map, size_t n))() { - for (uint32_t i=0 ; i<n ; i++) { +static void (*findProcAddress(const char* name, const extension_map_t* map, size_t n))() { + for (uint32_t i = 0; i < n; i++) { if (!strcmp(name, map[i].name)) { return map[i].address; } @@ -273,14 +231,17 @@ static void(*findProcAddress(const char* name, // ---------------------------------------------------------------------------- -extern void setGLHooksThreadSpecific(gl_hooks_t const *value); +extern void setGLHooksThreadSpecific(gl_hooks_t const* value); extern EGLBoolean egl_init_drivers(); -extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; +extern const __eglMustCastToProperFunctionPointerType + gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; extern gl_hooks_t gHooksTrace; // ---------------------------------------------------------------------------- -static inline EGLContext getContext() { return egl_tls_t::getContext(); } +static inline EGLContext getContext() { + return egl_tls_t::getContext(); +} // ---------------------------------------------------------------------------- @@ -313,9 +274,8 @@ EGLDisplay eglGetPlatformDisplayImpl(EGLenum platform, void* native_display, // Initialization // ---------------------------------------------------------------------------- -EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor) -{ - egl_display_ptr dp = get_display(dpy); +EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint* major, EGLint* minor) { + egl_display_t* dp = get_display(dpy); if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); EGLBoolean res = dp->initialize(major, minor); @@ -323,13 +283,12 @@ EGLBoolean eglInitializeImpl(EGLDisplay dpy, EGLint *major, EGLint *minor) return res; } -EGLBoolean eglTerminateImpl(EGLDisplay dpy) -{ +EGLBoolean eglTerminateImpl(EGLDisplay dpy) { // NOTE: don't unload the drivers b/c some APIs can be called // after eglTerminate() has been called. eglTerminate() only // terminates an EGLDisplay, not a EGL itself. - egl_display_ptr dp = get_display(dpy); + egl_display_t* dp = get_display(dpy); if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); EGLBoolean res = dp->terminate(); @@ -341,14 +300,12 @@ EGLBoolean eglTerminateImpl(EGLDisplay dpy) // configuration // ---------------------------------------------------------------------------- -EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, - EGLConfig *configs, - EGLint config_size, EGLint *num_config) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, + EGLint* num_config) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - if (num_config==nullptr) { + if (num_config == nullptr) { return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); } @@ -357,96 +314,88 @@ EGLBoolean eglGetConfigsImpl(EGLDisplay dpy, egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso) { - res = cnx->egl.eglGetConfigs( - dp->disp.dpy, configs, config_size, num_config); + res = cnx->egl.eglGetConfigs(dp->disp.dpy, configs, config_size, num_config); } return res; } -EGLBoolean eglChooseConfigImpl( EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglChooseConfigImpl(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, + EGLint config_size, EGLint* num_config) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - if (num_config==nullptr) { + if (num_config == nullptr) { return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); } - EGLBoolean res = EGL_FALSE; *num_config = 0; egl_connection_t* const cnx = &gEGLImpl; - if (cnx->dso) { - if (attrib_list) { - if (base::GetBoolProperty("debug.egl.force_msaa", false)) { - size_t attribCount = 0; - EGLint attrib = attrib_list[0]; - - // Only enable MSAA if the context is OpenGL ES 2.0 and - // if no caveat is requested - const EGLint *attribRendererable = nullptr; - const EGLint *attribCaveat = nullptr; - - // Count the number of attributes and look for - // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT - while (attrib != EGL_NONE) { - attrib = attrib_list[attribCount]; - switch (attrib) { - case EGL_RENDERABLE_TYPE: - attribRendererable = &attrib_list[attribCount]; - break; - case EGL_CONFIG_CAVEAT: - attribCaveat = &attrib_list[attribCount]; - break; - default: - break; - } - attribCount++; - } - - if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && - (!attribCaveat || attribCaveat[1] != EGL_NONE)) { + if (!cnx->dso) return EGL_FALSE; + + if (!attrib_list || !base::GetBoolProperty("debug.egl.force_msaa", false)) + return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size, + num_config); + + // Force 4x MSAA + size_t attribCount = 0; + EGLint attrib = attrib_list[0]; + + // Only enable MSAA if the context is OpenGL ES 2.0 and + // if no caveat is requested + const EGLint* attribRendererable = nullptr; + const EGLint* attribCaveat = nullptr; + + // Count the number of attributes and look for + // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT + while (attrib != EGL_NONE) { + attrib = attrib_list[attribCount]; + switch (attrib) { + case EGL_RENDERABLE_TYPE: + attribRendererable = &attrib_list[attribCount]; + break; + case EGL_CONFIG_CAVEAT: + attribCaveat = &attrib_list[attribCount]; + break; + default: + break; + } + attribCount++; + } - // Insert 2 extra attributes to force-enable MSAA 4x - EGLint aaAttribs[attribCount + 4]; - aaAttribs[0] = EGL_SAMPLE_BUFFERS; - aaAttribs[1] = 1; - aaAttribs[2] = EGL_SAMPLES; - aaAttribs[3] = 4; + if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT && + (!attribCaveat || attribCaveat[1] != EGL_NONE)) { + // Insert 2 extra attributes to force-enable MSAA 4x + EGLint aaAttribs[attribCount + 4]; + aaAttribs[0] = EGL_SAMPLE_BUFFERS; + aaAttribs[1] = 1; + aaAttribs[2] = EGL_SAMPLES; + aaAttribs[3] = 4; - memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); + memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint)); - EGLint numConfigAA; - EGLBoolean resAA = cnx->egl.eglChooseConfig( - dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA); + EGLint numConfigAA; + EGLBoolean resAA = cnx->egl.eglChooseConfig(dp->disp.dpy, aaAttribs, configs, config_size, + &numConfigAA); - if (resAA == EGL_TRUE && numConfigAA > 0) { - ALOGD("Enabling MSAA 4x"); - *num_config = numConfigAA; - return resAA; - } - } - } + if (resAA == EGL_TRUE && numConfigAA > 0) { + ALOGD("Enabling MSAA 4x"); + *num_config = numConfigAA; + return resAA; } - - res = cnx->egl.eglChooseConfig( - dp->disp.dpy, attrib_list, configs, config_size, num_config); } - return res; + + return cnx->egl.eglChooseConfig(dp->disp.dpy, attrib_list, configs, config_size, num_config); } -EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value) -{ +EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, EGLint attribute, + EGLint* value) { egl_connection_t* cnx = nullptr; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); + const egl_display_t* dp = validate_display_connection(dpy, &cnx); if (!dp) return EGL_FALSE; - return cnx->egl.eglGetConfigAttrib( - dp->disp.dpy, config, attribute, value); + return cnx->egl.eglGetConfigAttrib(dp->disp.dpy, config, attribute, value); } // ---------------------------------------------------------------------------- @@ -484,7 +433,7 @@ static EGLint getReportedColorSpace(EGLint colorspace) { } // Returns a list of color spaces understood by the vendor EGL driver. -static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { +static std::vector<EGLint> getDriverColorSpaces(egl_display_t* dp) { std::vector<EGLint> colorSpaces; // sRGB and linear are always supported when color space support is present. @@ -509,7 +458,8 @@ static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_linear")) { colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT); } - if (findExtension(dp->disp.queryString.extensions, "EGL_EXT_gl_colorspace_display_p3_passthrough")) { + if (findExtension(dp->disp.queryString.extensions, + "EGL_EXT_gl_colorspace_display_p3_passthrough")) { colorSpaces.push_back(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT); } return colorSpaces; @@ -519,7 +469,7 @@ static std::vector<EGLint> getDriverColorSpaces(egl_display_ptr dp) { // If there is no color space attribute in attrib_list, colorSpace is left // unmodified. template <typename AttrType> -static EGLBoolean processAttributes(egl_display_ptr dp, ANativeWindow* window, +static EGLBoolean processAttributes(egl_display_t* dp, ANativeWindow* window, const AttrType* attrib_list, EGLint* colorSpace, std::vector<AttrType>* strippedAttribList) { for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { @@ -699,7 +649,7 @@ EGLBoolean sendSurfaceMetadata(egl_surface_t* s) { } template <typename AttrType, typename CreateFuncType> -EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLConfig config, +EGLSurface eglCreateWindowSurfaceTmpl(egl_display_t* dp, egl_connection_t* cnx, EGLConfig config, ANativeWindow* window, const AttrType* attrib_list, CreateFuncType createWindowSurfaceFunc) { const AttrType* origAttribList = attrib_list; @@ -768,7 +718,7 @@ EGLSurface eglCreateWindowSurfaceTmpl(egl_display_ptr dp, egl_connection_t* cnx, EGLSurface surface = createWindowSurfaceFunc(iDpy, config, window, attrib_list); if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dp.get(), config, window, surface, + egl_surface_t* s = new egl_surface_t(dp, config, window, surface, getReportedColorSpace(colorSpace), cnx); return s; } @@ -789,8 +739,8 @@ typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEPROC)( EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint* attrib_list) { - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); + egl_connection_t* cnx = nullptr; + egl_display_t* dp = validate_display_connection(dpy, &cnx); if (dp) { return eglCreateWindowSurfaceTmpl< EGLint, PFNEGLCREATEWINDOWSURFACEPROC>(dp, cnx, config, window, attrib_list, @@ -801,8 +751,8 @@ EGLSurface eglCreateWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, NativeWi EGLSurface eglCreatePlatformWindowSurfaceImpl(EGLDisplay dpy, EGLConfig config, void* native_window, const EGLAttrib* attrib_list) { - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); + egl_connection_t* cnx = nullptr; + egl_display_t* dp = validate_display_connection(dpy, &cnx); if (dp) { if (cnx->driverVersion >= EGL_MAKE_VERSION(1, 5, 0)) { if (cnx->egl.eglCreatePlatformWindowSurface) { @@ -842,8 +792,8 @@ EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config // belongs to the Android platform. Any such call fails and generates // an EGL_BAD_PARAMETER error. - egl_connection_t* cnx = NULL; - egl_display_ptr dp = validate_display_connection(dpy, cnx); + egl_connection_t* cnx = nullptr; + const egl_display_t* dp = validate_display_connection(dpy, &cnx); if (dp) { return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } @@ -853,7 +803,7 @@ EGLSurface eglCreatePlatformPixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/, NativePixmapType /*pixmap*/, const EGLint* /*attrib_list*/) { egl_connection_t* cnx = nullptr; - egl_display_ptr dp = validate_display_connection(dpy, cnx); + const egl_display_t* dp = validate_display_connection(dpy, &cnx); if (dp) { return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } @@ -863,36 +813,33 @@ EGLSurface eglCreatePixmapSurfaceImpl(EGLDisplay dpy, EGLConfig /*config*/, EGLSurface eglCreatePbufferSurfaceImpl(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list) { egl_connection_t* cnx = nullptr; - egl_display_ptr dp = validate_display_connection(dpy, cnx); - if (dp) { - EGLDisplay iDpy = dp->disp.dpy; - android_pixel_format format; - getNativePixelFormat(iDpy, cnx, config, &format); - - // Select correct colorspace based on user's attribute list - EGLint colorSpace = EGL_UNKNOWN; - std::vector<EGLint> strippedAttribList; - if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) { - ALOGE("error invalid colorspace: %d", colorSpace); - return EGL_NO_SURFACE; - } - attrib_list = strippedAttribList.data(); + egl_display_t* dp = validate_display_connection(dpy, &cnx); + if (!dp) return EGL_NO_SURFACE; - EGLSurface surface = cnx->egl.eglCreatePbufferSurface(dp->disp.dpy, config, attrib_list); - if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface, - getReportedColorSpace(colorSpace), cnx); - return s; - } + EGLDisplay iDpy = dp->disp.dpy; + android_pixel_format format; + getNativePixelFormat(iDpy, cnx, config, &format); + + // Select correct colorspace based on user's attribute list + EGLint colorSpace = EGL_UNKNOWN; + std::vector<EGLint> strippedAttribList; + if (!processAttributes(dp, nullptr, attrib_list, &colorSpace, &strippedAttribList)) { + ALOGE("error invalid colorspace: %d", colorSpace); + return EGL_NO_SURFACE; } - return EGL_NO_SURFACE; + attrib_list = strippedAttribList.data(); + + EGLSurface surface = cnx->egl.eglCreatePbufferSurface(iDpy, config, attrib_list); + if (surface == EGL_NO_SURFACE) return surface; + + return new egl_surface_t(dp, config, nullptr, surface, getReportedColorSpace(colorSpace), cnx); } EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); egl_surface_t* const s = get_surface(surface); @@ -905,10 +852,10 @@ EGLBoolean eglDestroySurfaceImpl(EGLDisplay dpy, EGLSurface surface) { EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); egl_surface_t const* const s = get_surface(surface); @@ -923,12 +870,12 @@ EGLBoolean eglQuerySurfaceImpl(EGLDisplay dpy, EGLSurface surface, EGLint attrib } void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) { return; } - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) { setError(EGL_BAD_SURFACE, EGL_FALSE); } @@ -938,14 +885,13 @@ void EGLAPI eglBeginFrameImpl(EGLDisplay dpy, EGLSurface surface) { // Contexts // ---------------------------------------------------------------------------- -EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, - EGLContext share_list, const EGLint *attrib_list) -{ +EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, EGLContext share_list, + const EGLint* attrib_list) { egl_connection_t* cnx = nullptr; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); + const egl_display_t* dp = validate_display_connection(dpy, &cnx); if (dp) { if (share_list != EGL_NO_CONTEXT) { - if (!ContextRef(dp.get(), share_list).get()) { + if (!ContextRef(dp, share_list).get()) { return setError(EGL_BAD_CONTEXT, EGL_NO_CONTEXT); } egl_context_t* const c = get_context(share_list); @@ -967,8 +913,8 @@ EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, } }; } - EGLContext context = cnx->egl.eglCreateContext( - dp->disp.dpy, config, share_list, attrib_list); + EGLContext context = + cnx->egl.eglCreateContext(dp->disp.dpy, config, share_list, attrib_list); if (context != EGL_NO_CONTEXT) { // figure out if it's a GLESv1 or GLESv2 int version = egl_connection_t::GLESv1_INDEX; @@ -992,17 +938,14 @@ EGLContext eglCreateContextImpl(EGLDisplay dpy, EGLConfig config, return EGL_NO_CONTEXT; } -EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) -{ - const egl_display_ptr dp = validate_display(dpy); - if (!dp) - return EGL_FALSE; +EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) { + const egl_display_t* dp = validate_display(dpy); + if (!dp) return EGL_FALSE; - ContextRef _c(dp.get(), ctx); - if (!_c.get()) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + ContextRef _c(dp, ctx); + if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - egl_context_t * const c = get_context(ctx); + egl_context_t* const c = get_context(ctx); EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context); if (result == EGL_TRUE) { _c.terminate(); @@ -1010,24 +953,21 @@ EGLBoolean eglDestroyContextImpl(EGLDisplay dpy, EGLContext ctx) return result; } -EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx) -{ - egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglMakeCurrentImpl(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { + egl_display_t* dp = validate_display(dpy); if (!dp) return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is // a valid but uninitialized display. - if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || - (draw != EGL_NO_SURFACE) ) { + if ((ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) || (draw != EGL_NO_SURFACE)) { if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); } // get a reference to the object passed in - ContextRef _c(dp.get(), ctx); - SurfaceRef _d(dp.get(), draw); - SurfaceRef _r(dp.get(), read); + ContextRef _c(dp, ctx); + SurfaceRef _d(dp, draw); + SurfaceRef _r(dp, read); // validate the context (if not EGL_NO_CONTEXT) if ((ctx != EGL_NO_CONTEXT) && !_c.get()) { @@ -1036,17 +976,17 @@ EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw, } // these are the underlying implementation's object - EGLContext impl_ctx = EGL_NO_CONTEXT; + EGLContext impl_ctx = EGL_NO_CONTEXT; EGLSurface impl_draw = EGL_NO_SURFACE; EGLSurface impl_read = EGL_NO_SURFACE; // these are our objects structs passed in - egl_context_t * c = nullptr; - egl_surface_t const * d = nullptr; - egl_surface_t const * r = nullptr; + egl_context_t* c = nullptr; + egl_surface_t const* d = nullptr; + egl_surface_t const* r = nullptr; // these are the current objects structs - egl_context_t * cur_c = get_context(getContext()); + egl_context_t* cur_c = get_context(getContext()); if (ctx != EGL_NO_CONTEXT) { c = get_context(ctx); @@ -1078,10 +1018,7 @@ EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw, impl_read = r->surface; } - - EGLBoolean result = dp->makeCurrent(c, cur_c, - draw, read, ctx, - impl_draw, impl_read, impl_ctx); + EGLBoolean result = dp->makeCurrent(c, cur_c, draw, read, ctx, impl_draw, impl_read, impl_ctx); if (result == EGL_TRUE) { if (c) { @@ -1102,81 +1039,72 @@ EGLBoolean eglMakeCurrentImpl( EGLDisplay dpy, EGLSurface draw, return result; } -EGLBoolean eglQueryContextImpl( EGLDisplay dpy, EGLContext ctx, - EGLint attribute, EGLint *value) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglQueryContextImpl(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - ContextRef _c(dp.get(), ctx); + ContextRef _c(dp, ctx); if (!_c.get()) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); - egl_context_t * const c = get_context(ctx); - return c->cnx->egl.eglQueryContext( - dp->disp.dpy, c->context, attribute, value); - + egl_context_t* const c = get_context(ctx); + return c->cnx->egl.eglQueryContext(dp->disp.dpy, c->context, attribute, value); } -EGLContext eglGetCurrentContextImpl(void) -{ +EGLContext eglGetCurrentContextImpl(void) { // could be called before eglInitialize(), but we wouldn't have a context // then, and this function would correctly return EGL_NO_CONTEXT. EGLContext ctx = getContext(); return ctx; } -EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) -{ +EGLSurface eglGetCurrentSurfaceImpl(EGLint readdraw) { // could be called before eglInitialize(), but we wouldn't have a context // then, and this function would correctly return EGL_NO_SURFACE. EGLContext ctx = getContext(); if (ctx) { - egl_context_t const * const c = get_context(ctx); + egl_context_t const* const c = get_context(ctx); if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); switch (readdraw) { - case EGL_READ: return c->read; - case EGL_DRAW: return c->draw; - default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); + case EGL_READ: + return c->read; + case EGL_DRAW: + return c->draw; + default: + return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); } } return EGL_NO_SURFACE; } -EGLDisplay eglGetCurrentDisplayImpl(void) -{ +EGLDisplay eglGetCurrentDisplayImpl(void) { // could be called before eglInitialize(), but we wouldn't have a context // then, and this function would correctly return EGL_NO_DISPLAY. EGLContext ctx = getContext(); if (ctx) { - egl_context_t const * const c = get_context(ctx); + egl_context_t const* const c = get_context(ctx); if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE); return c->dpy; } return EGL_NO_DISPLAY; } -EGLBoolean eglWaitGLImpl(void) -{ +EGLBoolean eglWaitGLImpl(void) { egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); return cnx->egl.eglWaitGL(); } -EGLBoolean eglWaitNativeImpl(EGLint engine) -{ +EGLBoolean eglWaitNativeImpl(EGLint engine) { egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); return cnx->egl.eglWaitNative(engine); } -EGLint eglGetErrorImpl(void) -{ +EGLint eglGetErrorImpl(void) { EGLint err = EGL_SUCCESS; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso) { @@ -1188,8 +1116,7 @@ EGLint eglGetErrorImpl(void) return err; } -static __eglMustCastToProperFunctionPointerType findBuiltinWrapper( - const char* procname) { +static __eglMustCastToProperFunctionPointerType findBuiltinWrapper(const char* procname) { const egl_connection_t* cnx = &gEGLImpl; void* proc = nullptr; @@ -1205,8 +1132,7 @@ static __eglMustCastToProperFunctionPointerType findBuiltinWrapper( return nullptr; } -__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procname) -{ +__eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char* procname) { if (FILTER_EXTENSIONS(procname)) { return nullptr; } @@ -1253,13 +1179,10 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procn // Ensure we have room to track it const int slot = sGLExtensionSlot; if (slot < MAX_NUMBER_OF_GL_EXTENSIONS) { - if (cnx->dso && cnx->egl.eglGetProcAddress) { - // Extensions are independent of the bound context addr = cnx->egl.eglGetProcAddress(procname); if (addr) { - // purposefully track the bottom of the stack in extensionMap extensionMap[name] = addr; @@ -1268,7 +1191,7 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procn // Track the top most entry point return the extension forwarder cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] = - cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] = addr; addr = gExtensionForwarders[slot]; // Remember the slot for this extension @@ -1300,7 +1223,7 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procn // Track the top most entry point and return the extension forwarder cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[ext_slot] = - cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr; + cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[ext_slot] = addr; addr = gExtensionForwarders[ext_slot]; } @@ -1310,7 +1233,6 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddressImpl(const char *procn class FrameCompletionThread { public: - static void queueSync(EGLSyncKHR sync) { static FrameCompletionThread thread; @@ -1327,7 +1249,6 @@ public: } private: - FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) { std::thread thread(&FrameCompletionThread::loop, this); thread.detach(); @@ -1382,15 +1303,13 @@ private: std::mutex mMutex; }; -EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, - EGLint *rects, EGLint n_rects) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, EGLint* rects, + EGLint n_rects) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), draw); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + SurfaceRef _s(dp, draw); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); if (n_rects < 0 || (n_rects > 0 && rects == NULL)) return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); @@ -1406,11 +1325,11 @@ EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, if (CC_UNLIKELY(dp->finishOnSwap)) { uint32_t pixel; - egl_context_t * const c = get_context( egl_tls_t::getContext() ); + egl_context_t* const c = get_context(egl_tls_t::getContext()); if (c) { // glReadPixels() ensures that the frame is complete - s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1, - GL_RGBA,GL_UNSIGNED_BYTE,&pixel); + s->cnx->hooks[c->version]->gl.glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, + &pixel); } } @@ -1445,41 +1364,35 @@ EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, } if (s->cnx->egl.eglSwapBuffersWithDamageKHR) { - return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, - rects, n_rects); - } else { - return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); + return s->cnx->egl.eglSwapBuffersWithDamageKHR(dp->disp.dpy, s->surface, rects, n_rects); } + + return s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface); } -EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) -{ +EGLBoolean eglSwapBuffersImpl(EGLDisplay dpy, EGLSurface surface) { return eglSwapBuffersWithDamageKHRImpl(dpy, surface, nullptr, 0); } -EGLBoolean eglCopyBuffersImpl( EGLDisplay dpy, EGLSurface surface, - NativePixmapType target) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglCopyBuffersImpl(EGLDisplay dpy, EGLSurface surface, NativePixmapType target) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + SurfaceRef _s(dp, surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target); } -const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) -{ +const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) { if (dpy == EGL_NO_DISPLAY && name == EGL_EXTENSIONS) { // Return list of client extensions return gClientExtensionString; } - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return (const char *) nullptr; + const egl_display_t* dp = validate_display(dpy); + if (!dp) return (const char*)nullptr; switch (name) { case EGL_VENDOR: @@ -1493,13 +1406,12 @@ const char* eglQueryStringImpl(EGLDisplay dpy, EGLint name) default: break; } - return setError(EGL_BAD_PARAMETER, (const char *)nullptr); + return setError(EGL_BAD_PARAMETER, (const char*)nullptr); } -EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) -{ - const egl_display_ptr dp = validate_display(dpy); - if (!dp) return (const char *) nullptr; +EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLint name) { + const egl_display_t* dp = validate_display(dpy); + if (!dp) return (const char*)nullptr; switch (name) { case EGL_VENDOR: @@ -1513,24 +1425,22 @@ EGLAPI const char* eglQueryStringImplementationANDROIDImpl(EGLDisplay dpy, EGLin default: break; } - return setError(EGL_BAD_PARAMETER, (const char *)nullptr); + return setError(EGL_BAD_PARAMETER, (const char*)nullptr); } // ---------------------------------------------------------------------------- // EGL 1.1 // ---------------------------------------------------------------------------- -EGLBoolean eglSurfaceAttribImpl( - EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglSurfaceAttribImpl(EGLDisplay dpy, EGLSurface surface, EGLint attribute, + EGLint value) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + SurfaceRef _s(dp, surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - egl_surface_t * const s = get_surface(surface); + egl_surface_t* const s = get_surface(surface); if (attribute == EGL_FRONT_BUFFER_AUTO_REFRESH_ANDROID) { if (!s->getNativeWindow()) { @@ -1553,51 +1463,41 @@ EGLBoolean eglSurfaceAttribImpl( } else if (s->setCta8613Attribute(attribute, value)) { return EGL_TRUE; } else if (s->cnx->egl.eglSurfaceAttrib) { - return s->cnx->egl.eglSurfaceAttrib( - dp->disp.dpy, s->surface, attribute, value); + return s->cnx->egl.eglSurfaceAttrib(dp->disp.dpy, s->surface, attribute, value); } return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); } -EGLBoolean eglBindTexImageImpl( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglBindTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + SurfaceRef _s(dp, surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); if (s->cnx->egl.eglBindTexImage) { - return s->cnx->egl.eglBindTexImage( - dp->disp.dpy, s->surface, buffer); + return s->cnx->egl.eglBindTexImage(dp->disp.dpy, s->surface, buffer); } return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); } -EGLBoolean eglReleaseTexImageImpl( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglReleaseTexImageImpl(EGLDisplay dpy, EGLSurface surface, EGLint buffer) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + SurfaceRef _s(dp, surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); if (s->cnx->egl.eglReleaseTexImage) { - return s->cnx->egl.eglReleaseTexImage( - dp->disp.dpy, s->surface, buffer); + return s->cnx->egl.eglReleaseTexImage(dp->disp.dpy, s->surface, buffer); } return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); } -EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean res = EGL_TRUE; @@ -1609,16 +1509,13 @@ EGLBoolean eglSwapIntervalImpl(EGLDisplay dpy, EGLint interval) return res; } - // ---------------------------------------------------------------------------- // EGL 1.2 // ---------------------------------------------------------------------------- -EGLBoolean eglWaitClientImpl(void) -{ +EGLBoolean eglWaitClientImpl(void) { egl_connection_t* const cnx = &gEGLImpl; - if (!cnx->dso) - return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); + if (!cnx->dso) return setError(EGL_BAD_CONTEXT, (EGLBoolean)EGL_FALSE); EGLBoolean res; if (cnx->egl.eglWaitClient) { @@ -1629,8 +1526,7 @@ EGLBoolean eglWaitClientImpl(void) return res; } -EGLBoolean eglBindAPIImpl(EGLenum api) -{ +EGLBoolean eglBindAPIImpl(EGLenum api) { // bind this API on all EGLs EGLBoolean res = EGL_TRUE; egl_connection_t* const cnx = &gEGLImpl; @@ -1640,8 +1536,7 @@ EGLBoolean eglBindAPIImpl(EGLenum api) return res; } -EGLenum eglQueryAPIImpl(void) -{ +EGLenum eglQueryAPIImpl(void) { egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglQueryAPI) { return cnx->egl.eglQueryAPI(); @@ -1651,8 +1546,7 @@ EGLenum eglQueryAPIImpl(void) return EGL_OPENGL_ES_API; } -EGLBoolean eglReleaseThreadImpl(void) -{ +EGLBoolean eglReleaseThreadImpl(void) { egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglReleaseThread) { cnx->egl.eglReleaseThread(); @@ -1665,16 +1559,15 @@ EGLBoolean eglReleaseThreadImpl(void) return EGL_TRUE; } -EGLSurface eglCreatePbufferFromClientBufferImpl( - EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, - EGLConfig config, const EGLint *attrib_list) -{ +EGLSurface eglCreatePbufferFromClientBufferImpl(EGLDisplay dpy, EGLenum buftype, + EGLClientBuffer buffer, EGLConfig config, + const EGLint* attrib_list) { egl_connection_t* cnx = nullptr; - const egl_display_ptr dp = validate_display_connection(dpy, cnx); + const egl_display_t* dp = validate_display_connection(dpy, &cnx); if (!dp) return EGL_FALSE; if (cnx->egl.eglCreatePbufferFromClientBuffer) { - return cnx->egl.eglCreatePbufferFromClientBuffer( - dp->disp.dpy, buftype, buffer, config, attrib_list); + return cnx->egl.eglCreatePbufferFromClientBuffer(dp->disp.dpy, buftype, buffer, config, + attrib_list); } return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE); } @@ -1683,34 +1576,28 @@ EGLSurface eglCreatePbufferFromClientBufferImpl( // EGL_EGLEXT_VERSION 3 // ---------------------------------------------------------------------------- -EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, - const EGLint *attrib_list) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglLockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + SurfaceRef _s(dp, surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); if (s->cnx->egl.eglLockSurfaceKHR) { - return s->cnx->egl.eglLockSurfaceKHR( - dp->disp.dpy, s->surface, attrib_list); + return s->cnx->egl.eglLockSurfaceKHR(dp->disp.dpy, s->surface, attrib_list); } return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); } -EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglUnlockSurfaceKHRImpl(EGLDisplay dpy, EGLSurface surface) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; - SurfaceRef _s(dp.get(), surface); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + SurfaceRef _s(dp, surface); + if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); if (s->cnx->egl.eglUnlockSurfaceKHR) { return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface); } @@ -1723,7 +1610,7 @@ template <typename AttrType, typename FuncType> EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const AttrType* attrib_list, FuncType eglCreateImageFunc) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_NO_IMAGE_KHR; std::vector<AttrType> strippedAttribs; @@ -1732,7 +1619,7 @@ EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, // EGL_GL_COLORSPACE_LINEAR_KHR, EGL_GL_COLORSPACE_SRGB_KHR and // EGL_GL_COLORSPACE_DEFAULT_EXT if EGL_EXT_image_gl_colorspace is supported, // but some drivers don't like the DEFAULT value and generate an error. - for (const AttrType *attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { + for (const AttrType* attr = attrib_list; attr && attr[0] != EGL_NONE; attr += 2) { if (attr[0] == EGL_GL_COLORSPACE_KHR && dp->haveExtension("EGL_EXT_image_gl_colorspace")) { if (attr[1] != EGL_GL_COLORSPACE_LINEAR_KHR && @@ -1746,14 +1633,15 @@ EGLImageKHR eglCreateImageTmpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, strippedAttribs.push_back(EGL_NONE); } - ContextRef _c(dp.get(), ctx); + ContextRef _c(dp, ctx); egl_context_t* const c = _c.get(); EGLImageKHR result = EGL_NO_IMAGE_KHR; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && eglCreateImageFunc) { result = eglCreateImageFunc(dp->disp.dpy, c ? c->context : EGL_NO_CONTEXT, target, buffer, - needsAndroidPEglMitigation() ? strippedAttribs.data() : attrib_list); + needsAndroidPEglMitigation() ? strippedAttribs.data() + : attrib_list); } return result; } @@ -1792,7 +1680,7 @@ EGLImage eglCreateImageImpl(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLC EGLBoolean eglDestroyImageTmpl(EGLDisplay dpy, EGLImageKHR img, PFNEGLDESTROYIMAGEKHRPROC destroyImageFunc) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; @@ -1829,7 +1717,7 @@ EGLBoolean eglDestroyImageImpl(EGLDisplay dpy, EGLImageKHR img) { template <typename AttrType, typename FuncType> EGLSyncKHR eglCreateSyncTmpl(EGLDisplay dpy, EGLenum type, const AttrType* attrib_list, FuncType eglCreateSyncFunc) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_NO_SYNC_KHR; egl_connection_t* const cnx = &gEGLImpl; @@ -1868,7 +1756,7 @@ EGLSync eglCreateSyncImpl(EGLDisplay dpy, EGLenum type, const EGLAttrib* attrib_ EGLBoolean eglDestroySyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, PFNEGLDESTROYSYNCKHRPROC eglDestroySyncFunc) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; @@ -1897,7 +1785,7 @@ EGLBoolean eglDestroySyncImpl(EGLDisplay dpy, EGLSyncKHR sync) { } EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; @@ -1910,7 +1798,7 @@ EGLBoolean eglSignalSyncKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) { EGLint eglClientWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout, PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncFunc) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLint result = EGL_FALSE; @@ -1942,7 +1830,7 @@ EGLint eglClientWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime template <typename AttrType, typename FuncType> EGLBoolean eglGetSyncAttribTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, AttrType* value, FuncType eglGetSyncAttribFunc) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; @@ -1987,106 +1875,93 @@ EGLBoolean eglGetSyncAttribKHRImpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint attri .eglGetSyncAttribKHR); } -EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint *attrib_list) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLStreamKHR eglCreateStreamKHRImpl(EGLDisplay dpy, const EGLint* attrib_list) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_NO_STREAM_KHR; EGLStreamKHR result = EGL_NO_STREAM_KHR; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglCreateStreamKHR) { - result = cnx->egl.eglCreateStreamKHR( - dp->disp.dpy, attrib_list); + result = cnx->egl.eglCreateStreamKHR(dp->disp.dpy, attrib_list); } return result; } -EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglDestroyStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglDestroyStreamKHR) { - result = cnx->egl.eglDestroyStreamKHR( - dp->disp.dpy, stream); + result = cnx->egl.eglDestroyStreamKHR(dp->disp.dpy, stream); } return result; } -EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLint value) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglStreamAttribKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLint value) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglStreamAttribKHR) { - result = cnx->egl.eglStreamAttribKHR( - dp->disp.dpy, stream, attribute, value); + result = cnx->egl.eglStreamAttribKHR(dp->disp.dpy, stream, attribute, value); } return result; } -EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLint *value) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglQueryStreamKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLint* value) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglQueryStreamKHR) { - result = cnx->egl.eglQueryStreamKHR( - dp->disp.dpy, stream, attribute, value); + result = cnx->egl.eglQueryStreamKHR(dp->disp.dpy, stream, attribute, value); } return result; } -EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLuint64KHR *value) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglQueryStreamu64KHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLuint64KHR* value) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglQueryStreamu64KHR) { - result = cnx->egl.eglQueryStreamu64KHR( - dp->disp.dpy, stream, attribute, value); + result = cnx->egl.eglQueryStreamu64KHR(dp->disp.dpy, stream, attribute, value); } return result; } -EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, - EGLenum attribute, EGLTimeKHR *value) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglQueryStreamTimeKHRImpl(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, + EGLTimeKHR* value) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglQueryStreamTimeKHR) { - result = cnx->egl.eglQueryStreamTimeKHR( - dp->disp.dpy, stream, attribute, value); + result = cnx->egl.eglQueryStreamTimeKHR(dp->disp.dpy, stream, attribute, value); } return result; } EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig config, - EGLStreamKHR stream, const EGLint *attrib_list) -{ - egl_display_ptr dp = validate_display(dpy); + EGLStreamKHR stream, const EGLint* attrib_list) { + egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_NO_SURFACE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglCreateStreamProducerSurfaceKHR) { - EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR( - dp->disp.dpy, config, stream, attrib_list); + EGLSurface surface = cnx->egl.eglCreateStreamProducerSurfaceKHR(dp->disp.dpy, config, + stream, attrib_list); if (surface != EGL_NO_SURFACE) { - egl_surface_t* s = new egl_surface_t(dp.get(), config, nullptr, surface, + egl_surface_t* s = new egl_surface_t(dp, config, nullptr, surface, EGL_GL_COLORSPACE_LINEAR_KHR, cnx); return s; } @@ -2094,77 +1969,63 @@ EGLSurface eglCreateStreamProducerSurfaceKHRImpl(EGLDisplay dpy, EGLConfig confi return EGL_NO_SURFACE; } -EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, - EGLStreamKHR stream) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglStreamConsumerGLTextureExternalKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglStreamConsumerGLTextureExternalKHR) { - result = cnx->egl.eglStreamConsumerGLTextureExternalKHR( - dp->disp.dpy, stream); + result = cnx->egl.eglStreamConsumerGLTextureExternalKHR(dp->disp.dpy, stream); } return result; } -EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, - EGLStreamKHR stream) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglStreamConsumerAcquireKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglStreamConsumerAcquireKHR) { - result = cnx->egl.eglStreamConsumerAcquireKHR( - dp->disp.dpy, stream); + result = cnx->egl.eglStreamConsumerAcquireKHR(dp->disp.dpy, stream); } return result; } -EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, - EGLStreamKHR stream) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglStreamConsumerReleaseKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; EGLBoolean result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglStreamConsumerReleaseKHR) { - result = cnx->egl.eglStreamConsumerReleaseKHR( - dp->disp.dpy, stream); + result = cnx->egl.eglStreamConsumerReleaseKHR(dp->disp.dpy, stream); } return result; } -EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl( - EGLDisplay dpy, EGLStreamKHR stream) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLNativeFileDescriptorKHR eglGetStreamFileDescriptorKHRImpl(EGLDisplay dpy, EGLStreamKHR stream) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_NO_FILE_DESCRIPTOR_KHR; EGLNativeFileDescriptorKHR result = EGL_NO_FILE_DESCRIPTOR_KHR; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglGetStreamFileDescriptorKHR) { - result = cnx->egl.eglGetStreamFileDescriptorKHR( - dp->disp.dpy, stream); + result = cnx->egl.eglGetStreamFileDescriptorKHR(dpy, stream); } return result; } -EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl( - EGLDisplay dpy, EGLNativeFileDescriptorKHR file_descriptor) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl(EGLDisplay dpy, + EGLNativeFileDescriptorKHR file_descriptor) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_NO_STREAM_KHR; EGLStreamKHR result = EGL_NO_STREAM_KHR; egl_connection_t* const cnx = &gEGLImpl; if (cnx->dso && cnx->egl.eglCreateStreamFromFileDescriptorKHR) { - result = cnx->egl.eglCreateStreamFromFileDescriptorKHR( - dp->disp.dpy, file_descriptor); + result = cnx->egl.eglCreateStreamFromFileDescriptorKHR(dp->disp.dpy, file_descriptor); } return result; } @@ -2177,7 +2038,7 @@ EGLStreamKHR eglCreateStreamFromFileDescriptorKHRImpl( template <typename ReturnType, typename FuncType> ReturnType eglWaitSyncTmpl(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, FuncType eglWaitSyncFunc) { - const egl_display_ptr dp = validate_display(dpy); + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_FALSE; ReturnType result = EGL_FALSE; egl_connection_t* const cnx = &gEGLImpl; @@ -2214,9 +2075,8 @@ EGLBoolean eglWaitSyncImpl(EGLDisplay dpy, EGLSync sync, EGLint flags) { // ANDROID extensions // ---------------------------------------------------------------------------- -EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) { + const egl_display_t* dp = validate_display(dpy); if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID; EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID; @@ -2228,42 +2088,33 @@ EGLint eglDupNativeFenceFDANDROIDImpl(EGLDisplay dpy, EGLSyncKHR sync) } EGLBoolean eglPresentationTimeANDROIDImpl(EGLDisplay dpy, EGLSurface surface, - EGLnsecsANDROID time) -{ - const egl_display_ptr dp = validate_display(dpy); + EGLnsecsANDROID time) { + const egl_display_t* dp = validate_display(dpy); if (!dp) { return EGL_FALSE; } - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) { setError(EGL_BAD_SURFACE, EGL_FALSE); return EGL_FALSE; } - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); native_window_set_buffers_timestamp(s->getNativeWindow(), time); return EGL_TRUE; } -EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer *buffer) { - // AHardwareBuffer_to_ANativeWindowBuffer is a platform-only symbol and thus - // this function cannot be implemented when this libEGL is built for - // vendors. -#ifndef __ANDROID_VNDK__ +EGLClientBuffer eglGetNativeClientBufferANDROIDImpl(const AHardwareBuffer* buffer) { if (!buffer) return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); - return const_cast<ANativeWindowBuffer *>(AHardwareBuffer_to_ANativeWindowBuffer(buffer)); -#else - return setError(EGL_BAD_PARAMETER, (EGLClientBuffer) nullptr); -#endif + return const_cast<ANativeWindowBuffer*>(AHardwareBuffer_to_ANativeWindowBuffer(buffer)); } // ---------------------------------------------------------------------------- // NVIDIA extensions // ---------------------------------------------------------------------------- -EGLuint64NV eglGetSystemTimeFrequencyNVImpl() -{ +EGLuint64NV eglGetSystemTimeFrequencyNVImpl() { EGLuint64NV ret = 0; egl_connection_t* const cnx = &gEGLImpl; @@ -2274,8 +2125,7 @@ EGLuint64NV eglGetSystemTimeFrequencyNVImpl() return setErrorQuiet(EGL_BAD_DISPLAY, (EGLuint64NV)0); } -EGLuint64NV eglGetSystemTimeNVImpl() -{ +EGLuint64NV eglGetSystemTimeNVImpl() { EGLuint64NV ret = 0; egl_connection_t* const cnx = &gEGLImpl; @@ -2289,43 +2139,40 @@ EGLuint64NV eglGetSystemTimeNVImpl() // ---------------------------------------------------------------------------- // Partial update extension // ---------------------------------------------------------------------------- -EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, - EGLint *rects, EGLint n_rects) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglSetDamageRegionKHRImpl(EGLDisplay dpy, EGLSurface surface, EGLint* rects, + EGLint n_rects) { + const egl_display_t* dp = validate_display(dpy); if (!dp) { setError(EGL_BAD_DISPLAY, EGL_FALSE); return EGL_FALSE; } - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) { setError(EGL_BAD_SURFACE, EGL_FALSE); return EGL_FALSE; } - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); if (s->cnx->egl.eglSetDamageRegionKHR) { - return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, - rects, n_rects); + return s->cnx->egl.eglSetDamageRegionKHR(dp->disp.dpy, s->surface, rects, n_rects); } return EGL_FALSE; } -EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, - EGLuint64KHR *frameId) { - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, EGLuint64KHR* frameId) { + const egl_display_t* dp = validate_display(dpy); if (!dp) { return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); } - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) { return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); } - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); if (!s->getNativeWindow()) { return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); @@ -2346,19 +2193,19 @@ EGLBoolean eglGetNextFrameIdANDROIDImpl(EGLDisplay dpy, EGLSurface surface, } EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface, - EGLint numTimestamps, const EGLint *names, EGLnsecsANDROID *values) -{ - const egl_display_ptr dp = validate_display(dpy); + EGLint numTimestamps, const EGLint* names, + EGLnsecsANDROID* values) { + const egl_display_t* dp = validate_display(dpy); if (!dp) { return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); } - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) { return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); } - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); if (!s->getNativeWindow()) { return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); @@ -2384,36 +2231,35 @@ EGLBoolean eglGetCompositorTimingANDROIDImpl(EGLDisplay dpy, EGLSurface surface, } } - int ret = native_window_get_compositor_timing(s->getNativeWindow(), - compositeDeadline, compositeInterval, compositeToPresentLatency); + int ret = native_window_get_compositor_timing(s->getNativeWindow(), compositeDeadline, + compositeInterval, compositeToPresentLatency); switch (ret) { - case 0: - return EGL_TRUE; - case -ENOSYS: - return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); - default: - // This should not happen. Return an error that is not in the spec - // so it's obvious something is very wrong. - ALOGE("eglGetCompositorTiming: Unexpected error."); - return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); + case 0: + return EGL_TRUE; + case -ENOSYS: + return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + default: + // This should not happen. Return an error that is not in the spec + // so it's obvious something is very wrong. + ALOGE("eglGetCompositorTiming: Unexpected error."); + return setError(EGL_NOT_INITIALIZED, (EGLBoolean)EGL_FALSE); } } -EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl( - EGLDisplay dpy, EGLSurface surface, EGLint name) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLint name) { + const egl_display_t* dp = validate_display(dpy); if (!dp) { return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); } - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) { return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); } - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); ANativeWindow* window = s->getNativeWindow(); if (!window) { @@ -2431,20 +2277,19 @@ EGLBoolean eglGetCompositorTimingSupportedANDROIDImpl( } EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface, - EGLuint64KHR frameId, EGLint numTimestamps, const EGLint *timestamps, - EGLnsecsANDROID *values) -{ - const egl_display_ptr dp = validate_display(dpy); + EGLuint64KHR frameId, EGLint numTimestamps, + const EGLint* timestamps, EGLnsecsANDROID* values) { + const egl_display_t* dp = validate_display(dpy); if (!dp) { return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); } - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) { return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); } - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); if (!s->getNativeWindow()) { return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); @@ -2494,10 +2339,11 @@ EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface, } } - int ret = native_window_get_frame_timestamps(s->getNativeWindow(), frameId, - requestedPresentTime, acquireTime, latchTime, firstRefreshStartTime, - lastRefreshStartTime, gpuCompositionDoneTime, displayPresentTime, - dequeueReadyTime, releaseTime); + int ret = + native_window_get_frame_timestamps(s->getNativeWindow(), frameId, requestedPresentTime, + acquireTime, latchTime, firstRefreshStartTime, + lastRefreshStartTime, gpuCompositionDoneTime, + displayPresentTime, dequeueReadyTime, releaseTime); switch (ret) { case 0: @@ -2516,20 +2362,19 @@ EGLBoolean eglGetFrameTimestampsANDROIDImpl(EGLDisplay dpy, EGLSurface surface, } } -EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl( - EGLDisplay dpy, EGLSurface surface, EGLint timestamp) -{ - const egl_display_ptr dp = validate_display(dpy); +EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl(EGLDisplay dpy, EGLSurface surface, + EGLint timestamp) { + const egl_display_t* dp = validate_display(dpy); if (!dp) { return setError(EGL_BAD_DISPLAY, (EGLBoolean)EGL_FALSE); } - SurfaceRef _s(dp.get(), surface); + SurfaceRef _s(dp, surface); if (!_s.get()) { return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); } - egl_surface_t const * const s = get_surface(surface); + egl_surface_t const* const s = get_surface(surface); ANativeWindow* window = s->getNativeWindow(); if (!window) { @@ -2551,8 +2396,7 @@ EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl( return EGL_TRUE; case EGL_DISPLAY_PRESENT_TIME_ANDROID: { int value = 0; - window->query(window, - NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value); + window->query(window, NATIVE_WINDOW_FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value); return value == 0 ? EGL_FALSE : EGL_TRUE; } default: @@ -2560,25 +2404,25 @@ EGLBoolean eglGetFrameTimestampSupportedANDROIDImpl( } } -const GLubyte * glGetStringImpl(GLenum name) { - const GLubyte * ret = egl_get_string_for_current_context(name); +const GLubyte* glGetStringImpl(GLenum name) { + const GLubyte* ret = egl_get_string_for_current_context(name); if (ret == NULL) { - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if(_c) ret = _c->glGetString(name); + gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl; + if (_c) ret = _c->glGetString(name); } return ret; } -const GLubyte * glGetStringiImpl(GLenum name, GLuint index) { - const GLubyte * ret = egl_get_string_for_current_context(name, index); +const GLubyte* glGetStringiImpl(GLenum name, GLuint index) { + const GLubyte* ret = egl_get_string_for_current_context(name, index); if (ret == NULL) { - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; - if(_c) ret = _c->glGetStringi(name, index); + gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl; + if (_c) ret = _c->glGetStringi(name, index); } return ret; } -void glGetBooleanvImpl(GLenum pname, GLboolean * data) { +void glGetBooleanvImpl(GLenum pname, GLboolean* data) { if (pname == GL_NUM_EXTENSIONS) { int num_exts = egl_get_num_extensions_for_current_context(); if (num_exts >= 0) { @@ -2587,11 +2431,11 @@ void glGetBooleanvImpl(GLenum pname, GLboolean * data) { } } - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl; if (_c) _c->glGetBooleanv(pname, data); } -void glGetFloatvImpl(GLenum pname, GLfloat * data) { +void glGetFloatvImpl(GLenum pname, GLfloat* data) { if (pname == GL_NUM_EXTENSIONS) { int num_exts = egl_get_num_extensions_for_current_context(); if (num_exts >= 0) { @@ -2600,11 +2444,11 @@ void glGetFloatvImpl(GLenum pname, GLfloat * data) { } } - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl; if (_c) _c->glGetFloatv(pname, data); } -void glGetIntegervImpl(GLenum pname, GLint * data) { +void glGetIntegervImpl(GLenum pname, GLint* data) { if (pname == GL_NUM_EXTENSIONS) { int num_exts = egl_get_num_extensions_for_current_context(); if (num_exts >= 0) { @@ -2613,11 +2457,11 @@ void glGetIntegervImpl(GLenum pname, GLint * data) { } } - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl; if (_c) _c->glGetIntegerv(pname, data); } -void glGetInteger64vImpl(GLenum pname, GLint64 * data) { +void glGetInteger64vImpl(GLenum pname, GLint64* data) { if (pname == GL_NUM_EXTENSIONS) { int num_exts = egl_get_num_extensions_for_current_context(); if (num_exts >= 0) { @@ -2626,7 +2470,7 @@ void glGetInteger64vImpl(GLenum pname, GLint64 * data) { } } - gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl; + gl_hooks_t::gl_t const* const _c = &getGlThreadSpecific()->gl; if (_c) _c->glGetInteger64v(pname, data); } @@ -2635,8 +2479,8 @@ struct implementation_map_t { EGLFuncPointer address; }; +// clang-format off static const implementation_map_t sPlatformImplMap[] = { - // clang-format off { "eglGetDisplay", (EGLFuncPointer)&eglGetDisplayImpl }, { "eglGetPlatformDisplay", (EGLFuncPointer)&eglGetPlatformDisplayImpl }, { "eglInitialize", (EGLFuncPointer)&eglInitializeImpl }, @@ -2723,11 +2567,10 @@ static const implementation_map_t sPlatformImplMap[] = { { "glGetFloatv", (EGLFuncPointer)&glGetFloatvImpl }, { "glGetIntegerv", (EGLFuncPointer)&glGetIntegervImpl }, { "glGetInteger64v", (EGLFuncPointer)&glGetInteger64vImpl }, - // clang-format on }; +// clang-format on -EGLFuncPointer FindPlatformImplAddr(const char* name) -{ +EGLFuncPointer FindPlatformImplAddr(const char* name) { static const bool DEBUG = false; if (name == nullptr) { @@ -2741,7 +2584,8 @@ EGLFuncPointer FindPlatformImplAddr(const char* name) return nullptr; } if (!strcmp(name, sPlatformImplMap[i].name)) { - ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", (unsigned long long)sPlatformImplMap[i].address, i, name); + ALOGV("FindPlatformImplAddr found %llu for sPlatformImplMap[%i].address (%s)", + (unsigned long long)sPlatformImplMap[i].address, i, name); return sPlatformImplMap[i].address; } } diff --git a/opengl/libs/EGL/egl_tls.cpp b/opengl/libs/EGL/egl_tls.cpp index 8d118e07ed..dd1dcc275f 100644 --- a/opengl/libs/EGL/egl_tls.cpp +++ b/opengl/libs/EGL/egl_tls.cpp @@ -16,10 +16,10 @@ #include "egl_tls.h" -#include <stdlib.h> - #include <android-base/properties.h> #include <log/log.h> +#include <stdlib.h> + #include "CallStack.h" #include "egl_platform_entries.h" @@ -28,33 +28,46 @@ namespace android { pthread_key_t egl_tls_t::sKey = TLS_KEY_NOT_INITIALIZED; pthread_once_t egl_tls_t::sOnceKey = PTHREAD_ONCE_INIT; -egl_tls_t::egl_tls_t() - : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) { -} +egl_tls_t::egl_tls_t() : error(EGL_SUCCESS), ctx(nullptr), logCallWithNoContext(true) {} -const char *egl_tls_t::egl_strerror(EGLint err) { +const char* egl_tls_t::egl_strerror(EGLint err) { switch (err) { - case EGL_SUCCESS: return "EGL_SUCCESS"; - case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; - case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; - case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; - case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; - case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; - case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; - case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; - case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; - case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; - case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; - case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; - case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; - case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; - case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; - default: return "UNKNOWN"; + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "UNKNOWN"; } } -void egl_tls_t::validateTLSKey() -{ +void egl_tls_t::validateTLSKey() { struct TlsKeyInitializer { static void create() { pthread_key_create(&sKey, destructTLSData); } }; @@ -88,14 +101,12 @@ void egl_tls_t::destructTLSData(void* data) { "EGL TLS data still exists after eglReleaseThread"); } -void egl_tls_t::setErrorEtcImpl( - const char* caller, int line, EGLint error, bool quiet) { +void egl_tls_t::setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet) { validateTLSKey(); egl_tls_t* tls = getTLS(); if (tls->error != error) { if (!quiet) { - ALOGE("%s:%d error %x (%s)", - caller, line, error, egl_strerror(error)); + ALOGE("%s:%d error %x (%s)", caller, line, error, egl_strerror(error)); if (base::GetBoolProperty("debug.egl.callstack", false)) { CallStack::log(LOG_TAG); } @@ -111,7 +122,6 @@ bool egl_tls_t::logNoContextCall() { return true; } return false; - } egl_tls_t* egl_tls_t::getTLS() { @@ -161,10 +171,9 @@ EGLContext egl_tls_t::getContext() { if (sKey == TLS_KEY_NOT_INITIALIZED) { return EGL_NO_CONTEXT; } - egl_tls_t* tls = (egl_tls_t *)pthread_getspecific(sKey); + egl_tls_t* tls = (egl_tls_t*)pthread_getspecific(sKey); if (!tls) return EGL_NO_CONTEXT; return tls->ctx; } - } // namespace android diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h index 86a375c002..b5fcc1a805 100644 --- a/opengl/libs/EGL/egl_tls.h +++ b/opengl/libs/EGL/egl_tls.h @@ -18,12 +18,9 @@ #define ANDROID_EGL_TLS_H #include <EGL/egl.h> - #include <pthread.h> -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- class DbgContext; @@ -32,15 +29,14 @@ class egl_tls_t { static pthread_key_t sKey; static pthread_once_t sOnceKey; - EGLint error; - EGLContext ctx; - bool logCallWithNoContext; + EGLint error; + EGLContext ctx; + bool logCallWithNoContext; egl_tls_t(); static void validateTLSKey(); static void destructTLSData(void* data); - static void setErrorEtcImpl( - const char* caller, int line, EGLint error, bool quiet); + static void setErrorEtcImpl(const char* caller, int line, EGLint error, bool quiet); public: static egl_tls_t* getTLS(); @@ -50,24 +46,20 @@ public: static void setContext(EGLContext ctx); static EGLContext getContext(); static bool logNoContextCall(); - static const char *egl_strerror(EGLint err); + static const char* egl_strerror(EGLint err); - template<typename T> - static T setErrorEtc(const char* caller, - int line, EGLint error, T returnValue, bool quiet = false) { + template <typename T> + static T setErrorEtc(const char* caller, int line, EGLint error, T returnValue, + bool quiet = false) { setErrorEtcImpl(caller, line, error, quiet); return returnValue; } }; -#define setError(_e, _r) \ - egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r) +#define setError(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r) -#define setErrorQuiet(_e, _r) \ - egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true) +#define setErrorQuiet(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r, true) -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- #endif // ANDROID_EGL_TLS_H diff --git a/opengl/libs/EGL/egl_trace.h b/opengl/libs/EGL/egl_trace.h index 7664de2231..ffdf6761c6 100644 --- a/opengl/libs/EGL/egl_trace.h +++ b/opengl/libs/EGL/egl_trace.h @@ -18,16 +18,14 @@ #if defined(__ANDROID__) -#include <stdint.h> - #include <cutils/trace.h> +#include <stdint.h> // See <cutils/trace.h> for more ATRACE_* macros. // ATRACE_NAME traces from its location until the end of its enclosing scope. -#define _PASTE(x, y) x ## y -#define PASTE(x, y) _PASTE(x,y) -#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name) +#define PASTE(x, y) x##y +#define ATRACE_NAME(name) android::EglScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name) // ATRACE_CALL is an ATRACE_NAME that uses the current function name. #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__) @@ -36,13 +34,9 @@ namespace android { class EglScopedTrace { public: - inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) { - atrace_begin(mTag, name); - } + inline EglScopedTrace(uint64_t tag, const char* name) : mTag(tag) { atrace_begin(mTag, name); } - inline ~EglScopedTrace() { - atrace_end(mTag); - } + inline ~EglScopedTrace() { atrace_end(mTag); } private: uint64_t mTag; diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h index 5fbffbd16c..fcc11f1b55 100644 --- a/opengl/libs/EGL/egldefs.h +++ b/opengl/libs/EGL/egldefs.h @@ -17,40 +17,32 @@ #ifndef ANDROID_EGLDEFS_H #define ANDROID_EGLDEFS_H +#include <log/log.h> + #include "../hooks.h" #include "egl_platform_entries.h" -#include <log/log.h> - #define VERSION_MAJOR 1 #define VERSION_MINOR 4 #define EGL_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch)) -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- -// EGLDisplay are global, not attached to a given thread +// EGLDisplay are global, not attached to a given thread const unsigned int NUM_DISPLAYS = 1; -// ---------------------------------------------------------------------------- +extern const char* const platform_names[]; -extern char const * const platform_names[]; - -// clang-format off struct egl_connection_t { - enum { - GLESv1_INDEX = 0, - GLESv2_INDEX = 1 - }; - - inline egl_connection_t() : dso(nullptr), - libEgl(nullptr), - libGles1(nullptr), - libGles2(nullptr), - systemDriverUnloaded(false) { - - char const* const* entries = platform_names; + enum { GLESv1_INDEX = 0, GLESv2_INDEX = 1 }; + + inline egl_connection_t() + : dso(nullptr), + libEgl(nullptr), + libGles1(nullptr), + libGles2(nullptr), + systemDriverUnloaded(false) { + const char* const* entries = platform_names; EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform); while (*entries) { const char* name = *entries; @@ -66,41 +58,34 @@ struct egl_connection_t { } } - void * dso; - gl_hooks_t * hooks[2]; - EGLint major; - EGLint minor; - EGLint driverVersion; - egl_t egl; + void* dso; + gl_hooks_t* hooks[2]; + EGLint major; + EGLint minor; + EGLint driverVersion; + egl_t egl; // Functions implemented or redirected by platform libraries - platform_impl_t platform; + platform_impl_t platform; - void* libEgl; - void* libGles1; - void* libGles2; + void* libEgl; + void* libGles1; + void* libGles2; - bool systemDriverUnloaded; - bool useAngle; // Was ANGLE successfully loaded + bool systemDriverUnloaded; + bool useAngle; // Was ANGLE successfully loaded }; -// clang-format on - -// ---------------------------------------------------------------------------- extern gl_hooks_t gHooks[2]; extern gl_hooks_t gHooksNoContext; extern pthread_key_t gGLWrapperKey; extern "C" void gl_unimplemented(); extern "C" void gl_noop(); - -extern char const * const gl_names[]; -extern char const * const gl_names_1[]; -extern char const * const egl_names[]; - +extern const char* const gl_names[]; +extern const char* const gl_names_1[]; +extern const char* const egl_names[]; extern egl_connection_t gEGLImpl; -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- #endif /* ANDROID_EGLDEFS_H */ diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp index fedc7893db..b3d6f74e86 100644 --- a/opengl/libs/EGL/getProcAddress.cpp +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -16,15 +16,12 @@ #include <ctype.h> #include <errno.h> -#include <stdlib.h> - #include <log/log.h> +#include <stdlib.h> #include "egldefs.h" -// ---------------------------------------------------------------------------- namespace android { -// ---------------------------------------------------------------------------- #undef API_ENTRY #undef CALL_GL_EXTENSION_API @@ -34,6 +31,7 @@ namespace android { #undef GL_EXTENSION_LIST #undef GET_TLS +// clang-format off #if defined(__arm__) #define GET_TLS(reg) "mrc p15, 0, " #reg ", c13, c0, 3 \n" @@ -239,13 +237,13 @@ namespace android { name(248) name(249) name(250) name(251) name(252) name(253) name(254) name(255) -GL_EXTENSION_LIST( GL_EXTENSION ) +GL_EXTENSION_LIST(GL_EXTENSION) -#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n), +#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n), +// clang-format on -extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = { - GL_EXTENSION_LIST( GL_EXTENSION_ARRAY ) - }; +extern const __eglMustCastToProperFunctionPointerType + gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = {GL_EXTENSION_LIST(GL_EXTENSION_ARRAY)}; #undef GET_TLS #undef GL_EXTENSION_LIST @@ -255,7 +253,4 @@ extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_N #undef API_ENTRY #undef CALL_GL_EXTENSION_API -// ---------------------------------------------------------------------------- }; // namespace android -// ---------------------------------------------------------------------------- - diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp index 4767406931..321bb8342d 100644 --- a/services/automotive/display/AutomotiveDisplayProxyService.cpp +++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp @@ -34,7 +34,7 @@ AutomotiveDisplayProxyService::getIGraphicBufferProducer(uint64_t id) { sp<IBinder> displayToken = nullptr; sp<SurfaceControl> surfaceControl = nullptr; if (it == mDisplays.end()) { - displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id); + displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id)); if (displayToken == nullptr) { ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id); return nullptr; @@ -145,7 +145,7 @@ Return<void> AutomotiveDisplayProxyService::getDisplayIdList(getDisplayIdList_cb auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds(); ids.resize(displayIds.size()); for (auto i = 0; i < displayIds.size(); ++i) { - ids[i] = displayIds[i]; + ids[i] = displayIds[i].value; } _cb(ids); @@ -157,7 +157,7 @@ Return<void> AutomotiveDisplayProxyService::getDisplayInfo(uint64_t id, getDispl HwDisplayConfig activeConfig; HwDisplayState activeState; - auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(id); + auto displayToken = SurfaceComposerClient::getPhysicalDisplayToken(PhysicalDisplayId(id)); if (displayToken == nullptr) { ALOGE("Given display id, 0x%lX, is invalid.", (unsigned long)id); } else { diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp index 231d0684c9..220952d892 100644 --- a/services/gpuservice/gpustats/GpuStats.cpp +++ b/services/gpuservice/gpustats/GpuStats.cpp @@ -291,24 +291,27 @@ AStatsManager_PullAtomCallbackReturn GpuStats::pullAppInfoAtom(AStatsEventList* if (data) { for (const auto& ele : mAppStats) { - AStatsEvent* event = AStatsEventList_addStatsEvent(data); - AStatsEvent_setAtomId(event, android::util::GPU_STATS_APP_INFO); - AStatsEvent_writeString(event, ele.second.appPackageName.c_str()); - AStatsEvent_writeInt64(event, ele.second.driverVersionCode); - - std::string bytes = int64VectorToProtoByteString(ele.second.glDriverLoadingTime); - AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length()); - - bytes = int64VectorToProtoByteString(ele.second.vkDriverLoadingTime); - AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length()); - - bytes = int64VectorToProtoByteString(ele.second.angleDriverLoadingTime); - AStatsEvent_writeByteArray(event, (const uint8_t*)bytes.c_str(), bytes.length()); - - AStatsEvent_writeBool(event, ele.second.cpuVulkanInUse); - AStatsEvent_writeBool(event, ele.second.falsePrerotation); - AStatsEvent_writeBool(event, ele.second.gles1InUse); - AStatsEvent_build(event); + std::string glDriverBytes = int64VectorToProtoByteString( + ele.second.glDriverLoadingTime); + std::string vkDriverBytes = int64VectorToProtoByteString( + ele.second.vkDriverLoadingTime); + std::string angleDriverBytes = int64VectorToProtoByteString( + ele.second.angleDriverLoadingTime); + + android::util::addAStatsEvent( + data, + android::util::GPU_STATS_APP_INFO, + ele.second.appPackageName.c_str(), + ele.second.driverVersionCode, + android::util::BytesField(glDriverBytes.c_str(), + glDriverBytes.length()), + android::util::BytesField(vkDriverBytes.c_str(), + vkDriverBytes.length()), + android::util::BytesField(angleDriverBytes.c_str(), + angleDriverBytes.length()), + ele.second.cpuVulkanInUse, + ele.second.falsePrerotation, + ele.second.gles1InUse); } } @@ -326,22 +329,22 @@ AStatsManager_PullAtomCallbackReturn GpuStats::pullGlobalInfoAtom(AStatsEventLis if (data) { for (const auto& ele : mGlobalStats) { - AStatsEvent* event = AStatsEventList_addStatsEvent(data); - AStatsEvent_setAtomId(event, android::util::GPU_STATS_GLOBAL_INFO); - AStatsEvent_writeString(event, ele.second.driverPackageName.c_str()); - AStatsEvent_writeString(event, ele.second.driverVersionName.c_str()); - AStatsEvent_writeInt64(event, ele.second.driverVersionCode); - AStatsEvent_writeInt64(event, ele.second.driverBuildTime); - AStatsEvent_writeInt64(event, ele.second.glLoadingCount); - AStatsEvent_writeInt64(event, ele.second.glLoadingFailureCount); - AStatsEvent_writeInt64(event, ele.second.vkLoadingCount); - AStatsEvent_writeInt64(event, ele.second.vkLoadingFailureCount); - AStatsEvent_writeInt32(event, ele.second.vulkanVersion); - AStatsEvent_writeInt32(event, ele.second.cpuVulkanVersion); - AStatsEvent_writeInt32(event, ele.second.glesVersion); - AStatsEvent_writeInt64(event, ele.second.angleLoadingCount); - AStatsEvent_writeInt64(event, ele.second.angleLoadingFailureCount); - AStatsEvent_build(event); + android::util::addAStatsEvent( + data, + android::util::GPU_STATS_GLOBAL_INFO, + ele.second.driverPackageName.c_str(), + ele.second.driverVersionName.c_str(), + ele.second.driverVersionCode, + ele.second.driverBuildTime, + ele.second.glLoadingCount, + ele.second.glLoadingFailureCount, + ele.second.vkLoadingCount, + ele.second.vkLoadingFailureCount, + ele.second.vulkanVersion, + ele.second.cpuVulkanVersion, + ele.second.glesVersion, + ele.second.angleLoadingCount, + ele.second.angleLoadingFailureCount); } } diff --git a/services/gpuservice/tracing/GpuMemTracer.cpp b/services/gpuservice/tracing/GpuMemTracer.cpp index 6366e1d8e2..000cf2747a 100644 --- a/services/gpuservice/tracing/GpuMemTracer.cpp +++ b/services/gpuservice/tracing/GpuMemTracer.cpp @@ -80,6 +80,7 @@ void GpuMemTracer::traceInitialCounters() { mGpuMem->traverseGpuMemTotals([](int64_t ts, uint32_t gpuId, uint32_t pid, uint64_t size) { GpuMemDataSource::Trace([&](GpuMemDataSource::TraceContext ctx) { auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); packet->set_timestamp(ts); auto* event = packet->set_gpu_mem_total_event(); event->set_gpu_id(gpuId); diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index f67c9d006b..96e62079c6 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -21,7 +21,13 @@ cc_defaults { "-Werror", "-Wno-unused-parameter", "-Wthread-safety", + "-Wshadow", + "-Wshadow-field-in-constructor-modified", + "-Wshadow-uncaptured-local", ], + sanitize: { + misc_undefined: ["bounds"], + }, } ///////////////////////////////////////////////// @@ -53,6 +59,9 @@ cc_defaults { "libutils", "libui", ], + static_libs: [ + "libattestation", + ], } cc_library_shared { @@ -99,6 +108,7 @@ filegroup { "InputListener.cpp", "InputReaderBase.cpp", "InputThread.cpp", + "VibrationElement.cpp" ], } @@ -110,6 +120,7 @@ cc_defaults { "libcutils", "libinput", "liblog", + "libui", "libutils", ], header_libs: [ diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index e68946d734..3d995899d0 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -31,6 +31,25 @@ namespace android { +static int32_t exceptionCodeFromStatusT(status_t status) { + switch (status) { + case OK: + return binder::Status::EX_NONE; + case INVALID_OPERATION: + return binder::Status::EX_UNSUPPORTED_OPERATION; + case BAD_VALUE: + case BAD_TYPE: + case NAME_NOT_FOUND: + return binder::Status::EX_ILLEGAL_ARGUMENT; + case NO_INIT: + return binder::Status::EX_ILLEGAL_STATE; + case PERMISSION_DENIED: + return binder::Status::EX_SECURITY; + default: + return binder::Status::EX_TRANSACTION_FAILED; + } +} + InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { @@ -93,16 +112,15 @@ sp<InputDispatcherInterface> InputManager::getDispatcher() { class BinderWindowHandle : public InputWindowHandle { public: - BinderWindowHandle(const InputWindowInfo& info) { - mInfo = info; - } + BinderWindowHandle(const InputWindowInfo& info) { mInfo = info; } bool updateInfo() override { return true; } }; -void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos, +binder::Status InputManager::setInputWindows( + const std::vector<InputWindowInfo>& infos, const sp<ISetInputWindowsListener>& setInputWindowsListener) { std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> handlesPerDisplay; @@ -116,26 +134,45 @@ void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos, if (setInputWindowsListener) { setInputWindowsListener->onSetInputWindowsFinished(); } + return binder::Status::ok(); } // Used by tests only. -void InputManager::registerInputChannel(const sp<InputChannel>& channel) { +binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { IPCThreadState* ipc = IPCThreadState::self(); const int uid = ipc->getCallingUid(); if (uid != AID_SHELL && uid != AID_ROOT) { ALOGE("Invalid attempt to register input channel over IPC" "from non shell/root entity (PID: %d)", ipc->getCallingPid()); - return; + return binder::Status::ok(); + } + + base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name); + if (!channel) { + return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()), + channel.error().message().c_str()); } - mDispatcher->registerInputChannel(channel); + (*channel)->copyTo(*outChannel); + return binder::Status::ok(); +} + +binder::Status InputManager::removeInputChannel(const sp<IBinder>& connectionToken) { + mDispatcher->removeInputChannel(connectionToken); + return binder::Status::ok(); } -void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) { - mDispatcher->unregisterInputChannel(channel); +status_t InputManager::dump(int fd, const Vector<String16>& args) { + std::string dump; + + dump += " InputFlinger dump\n"; + + ::write(fd, dump.c_str(), dump.size()); + return NO_ERROR; } -void InputManager::setMotionClassifierEnabled(bool enabled) { - mClassifier->setMotionClassifierEnabled(enabled); +binder::Status InputManager::setFocusedWindow(const FocusRequest& request) { + mDispatcher->setFocusedWindow(request); + return binder::Status::ok(); } } // namespace android diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 0158441fd1..49bea132c8 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -26,15 +26,19 @@ #include <InputDispatcherInterface.h> #include <InputDispatcherPolicyInterface.h> -#include <input/ISetInputWindowsListener.h> +#include <android/os/ISetInputWindowsListener.h> #include <input/Input.h> #include <input/InputTransport.h> -#include <input/IInputFlinger.h> +#include <android/os/BnInputFlinger.h> +#include <android/os/IInputFlinger.h> #include <utils/Errors.h> -#include <utils/Vector.h> -#include <utils/Timers.h> #include <utils/RefBase.h> +#include <utils/Timers.h> +#include <utils/Vector.h> + +using android::os::BnInputFlinger; +using android::os::ISetInputWindowsListener; namespace android { class InputChannel; @@ -43,17 +47,19 @@ class InputDispatcherThread; /* * The input manager is the core of the system event processing. * - * The input manager has two components. + * The input manager has three components. * * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies - * policy, and posts messages to a queue managed by the InputDispatcherThread. - * 2. The InputDispatcher class starts a thread that waits for new events on the - * queue and asynchronously dispatches them to applications. + * policy, and posts messages to a queue managed by the InputClassifier. + * 2. The InputClassifier class starts a thread to communicate with the device-specific + * classifiers. It then waits on the queue of events from InputReader, applies a classification + * to them, and queues them for the InputDispatcher. + * 3. The InputDispatcher class starts a thread that waits for new events on the + * previous queue and asynchronously dispatches them to applications. * - * By design, the InputReader class and InputDispatcher class do not share any - * internal state. Moreover, all communication is done one way from the InputReader - * into the InputDispatcherThread and never the reverse. Both classes may interact with the - * InputDispatchPolicy, however. + * By design, none of these classes share any internal state. Moreover, all communication is + * done one way from the InputReader to the InputDispatcher and never the reverse. All + * classes may interact with the InputDispatchPolicy, however. * * The InputManager class never makes any calls into Java itself. Instead, the * InputDispatchPolicy is responsible for performing all external interactions with the @@ -74,33 +80,37 @@ public: /* Gets the input reader. */ virtual sp<InputReaderInterface> getReader() = 0; + /* Gets the input classifier */ + virtual sp<InputClassifierInterface> getClassifier() = 0; + /* Gets the input dispatcher. */ virtual sp<InputDispatcherInterface> getDispatcher() = 0; }; class InputManager : public InputManagerInterface, public BnInputFlinger { protected: - virtual ~InputManager(); + ~InputManager() override; public: InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy); - virtual status_t start(); - virtual status_t stop(); - - virtual sp<InputReaderInterface> getReader(); - virtual sp<InputClassifierInterface> getClassifier(); - virtual sp<InputDispatcherInterface> getDispatcher(); + status_t start() override; + status_t stop() override; - virtual void setInputWindows(const std::vector<InputWindowInfo>& handles, - const sp<ISetInputWindowsListener>& setInputWindowsListener); + sp<InputReaderInterface> getReader() override; + sp<InputClassifierInterface> getClassifier() override; + sp<InputDispatcherInterface> getDispatcher() override; - virtual void registerInputChannel(const sp<InputChannel>& channel); - virtual void unregisterInputChannel(const sp<InputChannel>& channel); + status_t dump(int fd, const Vector<String16>& args) override; + binder::Status setInputWindows( + const std::vector<InputWindowInfo>& handles, + const sp<ISetInputWindowsListener>& setInputWindowsListener) override; - void setMotionClassifierEnabled(bool enabled); + binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; + binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override; + binder::Status setFocusedWindow(const FocusRequest&) override; private: sp<InputReaderInterface> mReader; diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index b2dadf8460..9cc777d450 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -19,6 +19,9 @@ //#define LOG_NDEBUG 0 #include "InputReaderBase.h" +#include "input/DisplayViewport.h" +#include "input/Input.h" +#include "input/NamedEnum.h" #include <android/log.h> #include <android-base/stringprintf.h> @@ -99,17 +102,19 @@ std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByTyp size_t count = 0; std::optional<DisplayViewport> result = std::nullopt; for (const DisplayViewport& currentViewport : mDisplays) { - // Return the first match + // Return the first match, or the default display if we're looking for the internal viewport if (currentViewport.type == type) { - if (!result) { + if (!result || + (type == ViewportType::INTERNAL && + currentViewport.displayId == ADISPLAY_ID_DEFAULT)) { result = std::make_optional(currentViewport); } count++; } } if (count > 1) { - ALOGE("Found %zu viewports with type %s, but expected 1 at most", - count, viewportTypeToString(type)); + ALOGW("Found %zu viewports with type %s, but expected 1 at most", count, + NamedEnum::string(type).c_str()); } return result; } diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING new file mode 100644 index 0000000000..33520b2ea1 --- /dev/null +++ b/services/inputflinger/TEST_MAPPING @@ -0,0 +1,24 @@ +{ + "presubmit": [ + { + "name": "CtsWindowManagerDeviceTestCases", + "options": [ + { + "include-filter": "android.server.wm.WindowInputTests" + } + ] + }, + { + "name": "libinput_tests" + }, + { + "name": "inputflinger_tests" + }, + { + "name": "InputTests" + }, + { + "name": "libinputservice_test" + } + ] +} diff --git a/services/inputflinger/VibrationElement.cpp b/services/inputflinger/VibrationElement.cpp new file mode 100644 index 0000000000..aaf583401a --- /dev/null +++ b/services/inputflinger/VibrationElement.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "VibrationElement.h" + +#include <android-base/stringprintf.h> + +#include <algorithm> +#include <cinttypes> + +using android::base::StringPrintf; + +namespace android { + +const std::string VibrationElement::toString() const { + std::string dump; + dump += StringPrintf("[duration=%lldms, channels=[", duration.count()); + + for (auto it = channels.begin(); it != channels.end(); ++it) { + dump += std::to_string(*it); + if (std::next(it) != channels.end()) { + dump += ", "; + } + } + + dump += "]]"; + return dump; +} + +uint16_t VibrationElement::getMagnitude(size_t channelIdx) const { + if (channelIdx >= channels.size()) { + return 0; + } + // convert range [0,255] to [0,65535] (android framework to linux ff ranges) + return static_cast<uint16_t>(channels[channelIdx]) << 8; +} + +bool VibrationElement::isOn() const { + return std::any_of(channels.begin(), channels.end(), + [](uint16_t channel) { return channel != 0; }); +} + +} // namespace android diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index 066a816069..9abf8b179f 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -18,6 +18,7 @@ cc_benchmark { "libutils", ], static_libs: [ + "libattestation", "libinputdispatcher", ], } diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 5a14133eb2..c2d165e0f4 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -19,6 +19,9 @@ #include <binder/Binder.h> #include "../dispatcher/InputDispatcher.h" +using android::os::InputEventInjectionResult; +using android::os::InputEventInjectionSync; + namespace android::inputdispatcher { // An arbitrary device id. @@ -45,49 +48,47 @@ protected: virtual ~FakeInputDispatcherPolicy() {} private: - virtual void notifyConfigurationChanged(nsecs_t) override {} + void notifyConfigurationChanged(nsecs_t) override {} - virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>&, const sp<IBinder>&, - const std::string& name) override { + std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>&, + const sp<IBinder>&, const std::string& name) override { ALOGE("The window is not responding : %s", name.c_str()); - return 0; + return 0s; } - virtual void notifyInputChannelBroken(const sp<IBinder>&) override {} + void notifyInputChannelBroken(const sp<IBinder>&) override {} + + void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {} - virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {} + void notifyUntrustedTouch(const std::string& obscuringPackage) override {} - virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override { + void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override { *outConfig = mConfig; } - virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override { + bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override { return true; } - virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {} + void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {} - virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} + void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {} - virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, - uint32_t) override { + nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*, uint32_t) override { return 0; } - virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, - KeyEvent*) override { + bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t, KeyEvent*) override { return false; } - virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {} + void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {} - virtual void pokeUserActivity(nsecs_t, int32_t) override {} + void pokeUserActivity(nsecs_t, int32_t) override {} - virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { - return false; - } + bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override { return false; } - virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {} + void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {} InputDispatcherConfiguration mConfig; }; @@ -98,7 +99,8 @@ public: virtual ~FakeApplicationHandle() {} virtual bool updateInfo() { - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); + mInfo.dispatchingTimeoutMillis = + std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count(); return true; } }; @@ -106,7 +108,7 @@ public: class FakeInputReceiver { public: void consumeEvent() { - uint32_t consumeSeq; + uint32_t consumeSeq = 0; InputEvent* event; std::chrono::time_point start = std::chrono::steady_clock::now(); @@ -132,14 +134,14 @@ public: protected: explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name) : mDispatcher(dispatcher) { - InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel); + mClientChannel = *mDispatcher->createInputChannel(name); mConsumer = std::make_unique<InputConsumer>(mClientChannel); } virtual ~FakeInputReceiver() {} sp<InputDispatcher> mDispatcher; - sp<InputChannel> mServerChannel, mClientChannel; + std::shared_ptr<InputChannel> mClientChannel; std::unique_ptr<InputConsumer> mConsumer; PreallocatedInputEventFactory mEventFactory; }; @@ -149,21 +151,18 @@ public: static const int32_t WIDTH = 200; static const int32_t HEIGHT = 200; - FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle, + FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, const sp<InputDispatcher>& dispatcher, const std::string name) : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) { - mDispatcher->registerInputChannel(mServerChannel); - inputApplicationHandle->updateInfo(); mInfo.applicationInfo = *inputApplicationHandle->getInfo(); } virtual bool updateInfo() override { - mInfo.token = mServerChannel->getConnectionToken(); + mInfo.token = mClientChannel->getConnectionToken(); mInfo.name = "FakeWindowHandle"; - mInfo.layoutParamsFlags = 0; - mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); + mInfo.type = InputWindowInfo::Type::APPLICATION; + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; mInfo.frameLeft = mFrame.left; mInfo.frameTop = mFrame.top; mInfo.frameRight = mFrame.right; @@ -172,13 +171,11 @@ public: mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(mFrame); mInfo.visible = true; - mInfo.canReceiveKeys = true; - mInfo.hasFocus = true; + mInfo.focusable = true; mInfo.hasWallpaper = false; mInfo.paused = false; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; - mInfo.inputFeatures = 0; mInfo.displayId = ADISPLAY_ID_DEFAULT; return true; @@ -202,13 +199,13 @@ static MotionEvent generateMotionEvent() { const nsecs_t currentTime = now(); + ui::Transform identityTransform; MotionEvent event; event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - 1 /* xScale */, 1 /* yScale */, - /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, + identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); @@ -249,7 +246,7 @@ static void benchmarkNotifyMotion(benchmark::State& state) { dispatcher->start(); // Create a window that will receive motion events - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window"); dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -285,7 +282,7 @@ static void benchmarkInjectMotion(benchmark::State& state) { dispatcher->start(); // Create a window that will receive motion events - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window"); dispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -294,13 +291,13 @@ static void benchmarkInjectMotion(benchmark::State& state) { MotionEvent event = generateMotionEvent(); // Send ACTION_DOWN dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); // Send ACTION_UP event.setAction(AMOTION_EVENT_ACTION_UP); dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::NONE, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); window->consumeEvent(); diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 390c6b8506..ff9aac9961 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -48,6 +48,9 @@ cc_defaults { "libui", "libutils", ], + static_libs: [ + "libattestation", + ], header_libs: [ "libinputdispatcher_headers", ], @@ -68,4 +71,5 @@ cc_library_static { export_header_lib_headers: [ "libinputdispatcher_headers", ], + logtags: ["EventLogTags.logtags"], } diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index f5ea563311..cee9c39abd 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -20,7 +20,7 @@ namespace android::inputdispatcher { -Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor, +Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator) : status(STATUS_NORMAL), inputChannel(inputChannel), diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index 3b33f29dff..c4262ad2d8 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -42,7 +42,7 @@ public: }; Status status; - sp<InputChannel> inputChannel; // never null + std::shared_ptr<InputChannel> inputChannel; // never null bool monitor; InputPublisher inputPublisher; InputState inputState; @@ -59,7 +59,8 @@ public: // yet received a "finished" response from the application. std::deque<DispatchEntry*> waitQueue; - Connection(const sp<InputChannel>& inputChannel, bool monitor, const IdGenerator& idGenerator); + Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, + const IdGenerator& idGenerator); inline const std::string getInputChannelName() const { return inputChannel->getName(); } diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index fdbb1d1b55..29df00b1b9 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -59,7 +59,6 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags) : id(id), - refCount(1), type(type), eventTime(eventTime), policyFlags(policyFlags), @@ -70,21 +69,6 @@ EventEntry::~EventEntry() { releaseInjectionState(); } -std::string EventEntry::getDescription() const { - std::string result; - appendDescription(result); - return result; -} - -void EventEntry::release() { - refCount -= 1; - if (refCount == 0) { - delete this; - } else { - ALOG_ASSERT(refCount > 0); - } -} - void EventEntry::releaseInjectionState() { if (injectionState) { injectionState->release(); @@ -99,8 +83,8 @@ ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTi ConfigurationChangedEntry::~ConfigurationChangedEntry() {} -void ConfigurationChangedEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); +std::string ConfigurationChangedEntry::getDescription() const { + return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); } // --- DeviceResetEntry --- @@ -110,22 +94,24 @@ DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t device DeviceResetEntry::~DeviceResetEntry() {} -void DeviceResetEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); +std::string DeviceResetEntry::getDescription() const { + return StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); } // --- FocusEntry --- // Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries -FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus) +FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus, + std::string_view reason) : EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER), connectionToken(connectionToken), - hasFocus(hasFocus) {} + hasFocus(hasFocus), + reason(reason) {} FocusEntry::~FocusEntry() {} -void FocusEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false"); +std::string FocusEntry::getDescription() const { + return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false"); } // --- KeyEntry --- @@ -151,16 +137,16 @@ KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t sou KeyEntry::~KeyEntry() {} -void KeyEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("KeyEvent"); +std::string KeyEntry::getDescription() const { if (!GetBoolProperty("ro.debuggable", false)) { - return; + return "KeyEvent"; } - msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " + return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 + ", source=0x%08x, displayId=%" PRId32 ", action=%s, " "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", - deviceId, source, displayId, KeyEvent::actionToString(action), flags, - keyCode, scanCode, metaState, repeatCount, policyFlags); + deviceId, eventTime, source, displayId, KeyEvent::actionToString(action), + flags, keyCode, scanCode, metaState, repeatCount, policyFlags); } void KeyEntry::recycle() { @@ -183,7 +169,6 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xOffset, float yOffset) : EventEntry(id, Type::MOTION, eventTime, policyFlags), - eventTime(eventTime), deviceId(deviceId), source(source), displayId(displayId), @@ -211,20 +196,21 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 MotionEntry::~MotionEntry() {} -void MotionEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("MotionEvent"); +std::string MotionEntry::getDescription() const { if (!GetBoolProperty("ro.debuggable", false)) { - return; + return "MotionEvent"; } - msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 + std::string msg; + msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64 + ", source=0x%08x, displayId=%" PRId32 ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " "buttonState=0x%08x, " "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", - deviceId, source, displayId, MotionEvent::actionToString(action), - actionButton, flags, metaState, buttonState, - motionClassificationToString(classification), edgeFlags, xPrecision, - yPrecision, xCursorPosition, yCursorPosition); + deviceId, eventTime, source, displayId, + MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState, + buttonState, motionClassificationToString(classification), edgeFlags, + xPrecision, yPrecision, xCursorPosition, yCursorPosition); for (uint32_t i = 0; i < pointerCount; i++) { if (i) { @@ -234,32 +220,23 @@ void MotionEntry::appendDescription(std::string& msg) const { pointerCoords[i].getY()); } msg += StringPrintf("]), policyFlags=0x%08x", policyFlags); + return msg; } // --- DispatchEntry --- volatile int32_t DispatchEntry::sNextSeqAtomic; -DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, - float yOffset, float globalScaleFactor, float windowXScale, - float windowYScale) +DispatchEntry::DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, + ui::Transform transform, float globalScaleFactor) : seq(nextSeq()), - eventEntry(eventEntry), + eventEntry(std::move(eventEntry)), targetFlags(targetFlags), - xOffset(xOffset), - yOffset(yOffset), + transform(transform), globalScaleFactor(globalScaleFactor), - windowXScale(windowXScale), - windowYScale(windowYScale), deliveryTime(0), resolvedAction(0), - resolvedFlags(0) { - eventEntry->refCount += 1; -} - -DispatchEntry::~DispatchEntry() { - eventEntry->release(); -} + resolvedFlags(0) {} uint32_t DispatchEntry::nextSeq() { // Sequence number 0 is reserved and will never be returned. diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 6b7697dde6..2b18180242 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -54,7 +54,6 @@ struct EventEntry { } int32_t id; - mutable int32_t refCount; Type type; nsecs_t eventTime; uint32_t policyFlags; @@ -79,23 +78,19 @@ struct EventEntry { return isInjected() || IdGenerator::getSource(id) != IdGenerator::Source::INPUT_READER; } - void release(); + virtual std::string getDescription() const = 0; - virtual void appendDescription(std::string& msg) const = 0; - - std::string getDescription() const; - -protected: EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); + +protected: void releaseInjectionState(); }; struct ConfigurationChangedEntry : EventEntry { explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime); - virtual void appendDescription(std::string& msg) const; + std::string getDescription() const override; -protected: virtual ~ConfigurationChangedEntry(); }; @@ -103,20 +98,20 @@ struct DeviceResetEntry : EventEntry { int32_t deviceId; DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId); - virtual void appendDescription(std::string& msg) const; + std::string getDescription() const override; -protected: virtual ~DeviceResetEntry(); }; struct FocusEntry : EventEntry { sp<IBinder> connectionToken; bool hasFocus; + std::string_view reason; - FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus); - virtual void appendDescription(std::string& msg) const; + FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus, + std::string_view reason); + std::string getDescription() const override; -protected: virtual ~FocusEntry(); }; @@ -146,15 +141,13 @@ struct KeyEntry : EventEntry { KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime); - virtual void appendDescription(std::string& msg) const; + std::string getDescription() const override; void recycle(); -protected: virtual ~KeyEntry(); }; struct MotionEntry : EventEntry { - nsecs_t eventTime; int32_t deviceId; uint32_t source; int32_t displayId; @@ -181,9 +174,8 @@ struct MotionEntry : EventEntry { float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xOffset, float yOffset); - virtual void appendDescription(std::string& msg) const; + std::string getDescription() const override; -protected: virtual ~MotionEntry(); }; @@ -191,13 +183,10 @@ protected: struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 - EventEntry* eventEntry; // the event to dispatch + std::shared_ptr<EventEntry> eventEntry; // the event to dispatch int32_t targetFlags; - float xOffset; - float yOffset; + ui::Transform transform; float globalScaleFactor; - float windowXScale = 1.0f; - float windowYScale = 1.0f; // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app, // and will be undefined before that. nsecs_t deliveryTime; // time when the event was actually delivered @@ -209,9 +198,8 @@ struct DispatchEntry { int32_t resolvedAction; int32_t resolvedFlags; - DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset, - float globalScaleFactor, float windowXScale, float windowYScale); - ~DispatchEntry(); + DispatchEntry(std::shared_ptr<EventEntry> eventEntry, int32_t targetFlags, + ui::Transform transform, float globalScaleFactor); inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } @@ -256,15 +244,16 @@ struct CommandEntry { // parameters for the command (usage varies by command) sp<Connection> connection; nsecs_t eventTime; - KeyEntry* keyEntry; - sp<InputApplicationHandle> inputApplicationHandle; + std::shared_ptr<KeyEntry> keyEntry; + std::shared_ptr<InputApplicationHandle> inputApplicationHandle; std::string reason; int32_t userActivityEventType; uint32_t seq; bool handled; - sp<InputChannel> inputChannel; + std::shared_ptr<InputChannel> inputChannel; sp<IBinder> oldToken; sp<IBinder> newToken; + std::string obscuringPackage; }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/EventLogTags.logtags b/services/inputflinger/dispatcher/EventLogTags.logtags new file mode 100644 index 0000000000..283646793c --- /dev/null +++ b/services/inputflinger/dispatcher/EventLogTags.logtags @@ -0,0 +1,42 @@ +# The entries in this file map a sparse set of log tag numbers to tag names. +# This is installed on the device, in /system/etc, and parsed by logcat. +# +# Tag numbers are decimal integers, from 0 to 2^31. (Let's leave the +# negative values alone for now.) +# +# Tag names are one or more ASCII letters and numbers or underscores, i.e. +# "[A-Z][a-z][0-9]_". Do not include spaces or punctuation (the former +# impacts log readability, the latter makes regex searches more annoying). +# +# Tag numbers and names are separated by whitespace. Blank lines and lines +# starting with '#' are ignored. +# +# Optionally, after the tag names can be put a description for the value(s) +# of the tag. Description are in the format +# (<name>|data type[|data unit]) +# Multiple values are separated by commas. +# +# The data type is a number from the following values: +# 1: int +# 2: long +# 3: string +# 4: list +# +# The data unit is a number taken from the following list: +# 1: Number of objects +# 2: Number of bytes +# 3: Number of milliseconds +# 4: Number of allocations +# 5: Id +# 6: Percent +# Default value for data of type int/long is 2 (bytes). +# +# See system/core/logcat/event.logtags for the master copy of the tags. + +# 62000 - 62199 reserved for inputflinger + +62000 input_interaction (windows|4) +62001 input_focus (window|3),(reason|3) + +# NOTE - the range 1000000-2000000 is reserved for partners and others who +# want to define their own log tags without conflicting with the core platform.
\ No newline at end of file diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp index b2d0a26a37..c8024a61a9 100644 --- a/services/inputflinger/dispatcher/InjectionState.cpp +++ b/services/inputflinger/dispatcher/InjectionState.cpp @@ -24,7 +24,7 @@ InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) : refCount(1), injectorPid(injectorPid), injectorUid(injectorUid), - injectionResult(INPUT_EVENT_INJECTION_PENDING), + injectionResult(android::os::InputEventInjectionResult::PENDING), injectionIsAsync(false), pendingForegroundDispatches(0) {} diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h index 311a0f11ef..0bfafb1d19 100644 --- a/services/inputflinger/dispatcher/InjectionState.h +++ b/services/inputflinger/dispatcher/InjectionState.h @@ -17,34 +17,19 @@ #ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H #define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H -#include "InputDispatcherInterface.h" - #include <stdint.h> +#include "InputDispatcherInterface.h" -namespace android::inputdispatcher { - -/* - * Constants used to determine the input event injection synchronization mode. - */ -enum { - /* Injection is asynchronous and is assumed always to be successful. */ - INPUT_EVENT_INJECTION_SYNC_NONE = 0, - - /* Waits for previous events to be dispatched so that the input dispatcher can determine - * whether input event injection willbe permitted based on the current input focus. - * Does not wait for the input event to finish processing. */ - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, +namespace android { - /* Waits for the input event to be completely processed. */ - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, -}; +namespace inputdispatcher { struct InjectionState { mutable int32_t refCount; int32_t injectorPid; int32_t injectorUid; - int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING + android::os::InputEventInjectionResult injectionResult; // initially PENDING bool injectionIsAsync; // set to true if injection is not waiting for the result int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress @@ -55,6 +40,7 @@ private: ~InjectionState(); }; -} // namespace android::inputdispatcher +} // namespace inputdispatcher +} // namespace android #endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 2517060613..719148798e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -28,8 +28,8 @@ // Log debug messages about the dispatch cycle. #define DEBUG_DISPATCH_CYCLE 0 -// Log debug messages about registrations. -#define DEBUG_REGISTRATION 0 +// Log debug messages about channel creation +#define DEBUG_CHANNEL_CREATION 0 // Log debug messages about input event injection. #define DEBUG_INJECTION 0 @@ -37,6 +37,10 @@ // Log debug messages about input focus tracking. static constexpr bool DEBUG_FOCUS = false; +// Log debug messages about touch occlusion +// STOPSHIP(b/169067926): Set to false +static constexpr bool DEBUG_TOUCH_OCCLUSION = true; + // Log debug messages about the app switch latency optimization. #define DEBUG_APP_SWITCH 0 @@ -45,40 +49,45 @@ static constexpr bool DEBUG_FOCUS = false; #include "InputDispatcher.h" -#include "Connection.h" - -#include <errno.h> -#include <inttypes.h> -#include <limits.h> -#include <statslog.h> -#include <stddef.h> -#include <time.h> -#include <unistd.h> -#include <queue> -#include <sstream> - #include <android-base/chrono_utils.h> #include <android-base/stringprintf.h> +#include <android/os/IInputConstants.h> #include <binder/Binder.h> #include <input/InputDevice.h> +#include <input/InputWindow.h> #include <log/log.h> -#include <openssl/hmac.h> -#include <openssl/rand.h> +#include <log/log_event_list.h> #include <powermanager/PowerManager.h> +#include <statslog.h> +#include <unistd.h> #include <utils/Trace.h> +#include <cerrno> +#include <cinttypes> +#include <climits> +#include <cstddef> +#include <ctime> +#include <queue> +#include <sstream> + +#include "Connection.h" + #define INDENT " " #define INDENT2 " " #define INDENT3 " " #define INDENT4 " " using android::base::StringPrintf; +using android::os::BlockUntrustedTouchesMode; +using android::os::InputEventInjectionResult; +using android::os::InputEventInjectionSync; namespace android::inputdispatcher { // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. -constexpr std::chrono::nanoseconds DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5s; +constexpr std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = + std::chrono::milliseconds(android::os::IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS); // Amount of time to allow for all pending events to be processed when an app switch // key is on the way. This is used to preempt input dispatch and drop input events @@ -103,6 +112,10 @@ constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms; // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; +// Event log tags. See EventLogTags.logtags for reference +constexpr int LOGTAG_INPUT_INTERACTION = 62000; +constexpr int LOGTAG_INPUT_FOCUS = 62001; + static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } @@ -159,6 +172,10 @@ static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t po } } +static int64_t millis(std::chrono::nanoseconds t) { + return std::chrono::duration_cast<std::chrono::milliseconds>(t).count(); +} + static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount, const PointerProperties* pointerProperties) { if (!isValidMotionAction(action, actionButton, pointerCount)) { @@ -187,12 +204,12 @@ static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t poi return true; } -static void dumpRegion(std::string& dump, const Region& region) { +static std::string dumpRegion(const Region& region) { if (region.isEmpty()) { - dump += "<empty>"; - return; + return "<empty>"; } + std::string dump; bool first = true; Region::const_iterator cur = region.begin(); Region::const_iterator const tail = region.end(); @@ -205,6 +222,37 @@ static void dumpRegion(std::string& dump, const Region& region) { dump += StringPrintf("[%d,%d][%d,%d]", cur->left, cur->top, cur->right, cur->bottom); cur++; } + return dump; +} + +static std::string dumpQueue(const std::deque<DispatchEntry*>& queue, nsecs_t currentTime) { + constexpr size_t maxEntries = 50; // max events to print + constexpr size_t skipBegin = maxEntries / 2; + const size_t skipEnd = queue.size() - maxEntries / 2; + // skip from maxEntries / 2 ... size() - maxEntries/2 + // only print from 0 .. skipBegin and then from skipEnd .. size() + + std::string dump; + for (size_t i = 0; i < queue.size(); i++) { + const DispatchEntry& entry = *queue[i]; + if (i >= skipBegin && i < skipEnd) { + dump += StringPrintf(INDENT4 "<skipped %zu entries>\n", skipEnd - skipBegin); + i = skipEnd - 1; // it will be incremented to "skipEnd" by 'continue' + continue; + } + dump.append(INDENT4); + dump += entry.eventEntry->getDescription(); + dump += StringPrintf(", seq=%" PRIu32 + ", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 "ms", + entry.seq, entry.targetFlags, entry.resolvedAction, + ns2ms(currentTime - entry.eventEntry->eventTime)); + if (entry.deliveryTime != 0) { + // This entry was delivered, so add information on how long we've been waiting + dump += StringPrintf(", wait=%" PRId64 "ms", ns2ms(currentTime - entry.deliveryTime)); + } + dump.append("\n"); + } + return dump; } /** @@ -242,6 +290,15 @@ static bool removeByValue(std::unordered_map<K, V>& map, const V& value) { return removed; } +/** + * Find the entry in std::unordered_map by key and return the value as an optional. + */ +template <typename K, typename V> +static std::optional<V> getOptionalValueByKey(const std::unordered_map<K, V>& map, K key) { + auto it = map.find(key); + return it != map.end() ? std::optional<V>{it->second} : std::nullopt; +} + static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) { if (first == second) { return true; @@ -259,58 +316,54 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { } static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, - EventEntry* eventEntry, + std::shared_ptr<EventEntry> eventEntry, int32_t inputTargetFlags) { - if (inputTarget.useDefaultPointerInfo()) { - const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo(); - return std::make_unique<DispatchEntry>(eventEntry, // increments ref - inputTargetFlags, pointerInfo.xOffset, - pointerInfo.yOffset, inputTarget.globalScaleFactor, - pointerInfo.windowXScale, pointerInfo.windowYScale); + if (inputTarget.useDefaultPointerTransform()) { + const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); + return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, + inputTarget.globalScaleFactor); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry); - PointerCoords pointerCoords[motionEntry.pointerCount]; + std::vector<PointerCoords> pointerCoords; + pointerCoords.resize(motionEntry.pointerCount); // Use the first pointer information to normalize all other pointers. This could be any pointer // as long as all other pointers are normalized to the same value and the final DispatchEntry - // uses the offset and scale for the normalized pointer. - const PointerInfo& firstPointerInfo = - inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()]; + // uses the transform for the normalized pointer. + const ui::Transform& firstPointerTransform = + inputTarget.pointerTransforms[inputTarget.pointerIds.firstMarkedBit()]; + ui::Transform inverseFirstTransform = firstPointerTransform.inverse(); // Iterate through all pointers in the event to normalize against the first. for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) { const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex]; uint32_t pointerId = uint32_t(pointerProperties.id); - const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId]; - - // The scale factor is the ratio of the current pointers scale to the normalized scale. - float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale; - float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale; + const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId]; pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]); - // First apply the current pointers offset to set the window at 0,0 - pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset); - // Next scale the coordinates. - pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff); - // Lastly, offset the coordinates so they're in the normalized pointer's frame. - pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset, - -firstPointerInfo.yOffset); - } - - MotionEntry* combinedMotionEntry = - new MotionEntry(motionEntry.id, motionEntry.eventTime, motionEntry.deviceId, - motionEntry.source, motionEntry.displayId, motionEntry.policyFlags, - motionEntry.action, motionEntry.actionButton, motionEntry.flags, - motionEntry.metaState, motionEntry.buttonState, - motionEntry.classification, motionEntry.edgeFlags, - motionEntry.xPrecision, motionEntry.yPrecision, - motionEntry.xCursorPosition, motionEntry.yCursorPosition, - motionEntry.downTime, motionEntry.pointerCount, - motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */, - 0 /* yOffset */); + // First, apply the current pointer's transform to update the coordinates into + // window space. + pointerCoords[pointerIndex].transform(currTransform); + // Next, apply the inverse transform of the normalized coordinates so the + // current coordinates are transformed into the normalized coordinate space. + pointerCoords[pointerIndex].transform(inverseFirstTransform); + } + + std::unique_ptr<MotionEntry> combinedMotionEntry = + std::make_unique<MotionEntry>(motionEntry.id, motionEntry.eventTime, + motionEntry.deviceId, motionEntry.source, + motionEntry.displayId, motionEntry.policyFlags, + motionEntry.action, motionEntry.actionButton, + motionEntry.flags, motionEntry.metaState, + motionEntry.buttonState, motionEntry.classification, + motionEntry.edgeFlags, motionEntry.xPrecision, + motionEntry.yPrecision, motionEntry.xCursorPosition, + motionEntry.yCursorPosition, motionEntry.downTime, + motionEntry.pointerCount, motionEntry.pointerProperties, + pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */); if (motionEntry.injectionState) { combinedMotionEntry->injectionState = motionEntry.injectionState; @@ -318,12 +371,8 @@ static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inp } std::unique_ptr<DispatchEntry> dispatchEntry = - std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref - inputTargetFlags, firstPointerInfo.xOffset, - firstPointerInfo.yOffset, inputTarget.globalScaleFactor, - firstPointerInfo.windowXScale, - firstPointerInfo.windowYScale); - combinedMotionEntry->release(); + std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, + firstPointerTransform, inputTarget.globalScaleFactor); return dispatchEntry; } @@ -339,51 +388,38 @@ static void addGestureMonitors(const std::vector<Monitor>& monitors, } } -static std::array<uint8_t, 128> getRandomKey() { - std::array<uint8_t, 128> key; - if (RAND_bytes(key.data(), key.size()) != 1) { - LOG_ALWAYS_FATAL("Can't generate HMAC key"); - } - return key; -} - -// --- HmacKeyManager --- +static status_t openInputChannelPair(const std::string& name, + std::shared_ptr<InputChannel>& serverChannel, + std::unique_ptr<InputChannel>& clientChannel) { + std::unique_ptr<InputChannel> uniqueServerChannel; + status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel); -HmacKeyManager::HmacKeyManager() : mHmacKey(getRandomKey()) {} + serverChannel = std::move(uniqueServerChannel); + return result; +} -std::array<uint8_t, 32> HmacKeyManager::sign(const VerifiedInputEvent& event) const { - size_t size; - switch (event.type) { - case VerifiedInputEvent::Type::KEY: { - size = sizeof(VerifiedKeyEvent); - break; - } - case VerifiedInputEvent::Type::MOTION: { - size = sizeof(VerifiedMotionEvent); - break; - } +const char* InputDispatcher::typeToString(InputDispatcher::FocusResult result) { + switch (result) { + case InputDispatcher::FocusResult::OK: + return "Ok"; + case InputDispatcher::FocusResult::NO_WINDOW: + return "Window not found"; + case InputDispatcher::FocusResult::NOT_FOCUSABLE: + return "Window not focusable"; + case InputDispatcher::FocusResult::NOT_VISIBLE: + return "Window not visible"; } - const uint8_t* start = reinterpret_cast<const uint8_t*>(&event); - return sign(start, size); } -std::array<uint8_t, 32> HmacKeyManager::sign(const uint8_t* data, size_t size) const { - // SHA256 always generates 32-bytes result - std::array<uint8_t, 32> hash; - unsigned int hashLen = 0; - uint8_t* result = - HMAC(EVP_sha256(), mHmacKey.data(), mHmacKey.size(), data, size, hash.data(), &hashLen); - if (result == nullptr) { - ALOGE("Could not sign the data using HMAC"); - return INVALID_HMAC; +template <typename T> +static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) { + if (lhs == nullptr && rhs == nullptr) { + return true; } - - if (hashLen != hash.size()) { - ALOGE("HMAC-SHA256 has unexpected length"); - return INVALID_HMAC; + if (lhs == nullptr || rhs == nullptr) { + return false; } - - return hash; + return *lhs == *rhs; } // --- InputDispatcher --- @@ -403,6 +439,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic // To avoid leaking stack in case that call never comes, and for tests, // initialize it here anyways. mInTouchMode(true), + mMaximumObscuringOpacityForTouch(1.0f), mFocusedDisplayId(ADISPLAY_ID_DEFAULT) { mLooper = new Looper(false); mReporter = createInputReporter(); @@ -423,7 +460,7 @@ InputDispatcher::~InputDispatcher() { while (!mConnectionsByFd.empty()) { sp<Connection> connection = mConnectionsByFd.begin()->second; - unregisterInputChannel(connection->inputChannel); + removeInputChannel(connection->inputChannel->getConnectionToken()); } } @@ -489,7 +526,7 @@ void InputDispatcher::dispatchOnce() { */ void InputDispatcher::processNoFocusedWindowAnrLocked() { // Check if the application that we are waiting for is still focused. - sp<InputApplicationHandle> focusedApplication = + std::shared_ptr<InputApplicationHandle> focusedApplication = getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId); if (focusedApplication == nullptr || focusedApplication->getApplicationToken() != @@ -520,13 +557,11 @@ nsecs_t InputDispatcher::processAnrsLocked() { if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) { if (currentTime >= *mNoFocusedWindowTimeoutTime) { processNoFocusedWindowAnrLocked(); - mAwaitedFocusedApplication.clear(); + mAwaitedFocusedApplication.reset(); mNoFocusedWindowTimeoutTime = std::nullopt; return LONG_LONG_MIN; } else { - // Keep waiting - const nsecs_t millisRemaining = ns2ms(*mNoFocusedWindowTimeoutTime - currentTime); - ALOGW("Still no focused window. Will drop the event in %" PRId64 "ms", millisRemaining); + // Keep waiting. We will drop the event when mNoFocusedWindowTimeoutTime comes. nextAnrCheck = *mNoFocusedWindowTimeoutTime; } } @@ -550,12 +585,12 @@ nsecs_t InputDispatcher::processAnrsLocked() { return LONG_LONG_MIN; } -nsecs_t InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) { +std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) { sp<InputWindowHandle> window = getWindowHandleLocked(token); if (window != nullptr) { - return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT).count(); + return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } - return DEFAULT_INPUT_DISPATCHING_TIMEOUT.count(); + return DEFAULT_INPUT_DISPATCHING_TIMEOUT; } void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { @@ -640,22 +675,24 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { switch (mPendingEvent->type) { case EventEntry::Type::CONFIGURATION_CHANGED: { - ConfigurationChangedEntry* typedEntry = - static_cast<ConfigurationChangedEntry*>(mPendingEvent); + const ConfigurationChangedEntry& typedEntry = + static_cast<const ConfigurationChangedEntry&>(*mPendingEvent); done = dispatchConfigurationChangedLocked(currentTime, typedEntry); dropReason = DropReason::NOT_DROPPED; // configuration changes are never dropped break; } case EventEntry::Type::DEVICE_RESET: { - DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent); + const DeviceResetEntry& typedEntry = + static_cast<const DeviceResetEntry&>(*mPendingEvent); done = dispatchDeviceResetLocked(currentTime, typedEntry); dropReason = DropReason::NOT_DROPPED; // device resets are never dropped break; } case EventEntry::Type::FOCUS: { - FocusEntry* typedEntry = static_cast<FocusEntry*>(mPendingEvent); + std::shared_ptr<FocusEntry> typedEntry = + std::static_pointer_cast<FocusEntry>(mPendingEvent); dispatchFocusLocked(currentTime, typedEntry); done = true; dropReason = DropReason::NOT_DROPPED; // focus events are never dropped @@ -663,37 +700,38 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } case EventEntry::Type::KEY: { - KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); + std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent); if (isAppSwitchDue) { - if (isAppSwitchKeyEvent(*typedEntry)) { + if (isAppSwitchKeyEvent(*keyEntry)) { resetPendingAppSwitchLocked(true); isAppSwitchDue = false; } else if (dropReason == DropReason::NOT_DROPPED) { dropReason = DropReason::APP_SWITCH; } } - if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) { + if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) { dropReason = DropReason::STALE; } if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { dropReason = DropReason::BLOCKED; } - done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); + done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime); break; } case EventEntry::Type::MOTION: { - MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); + std::shared_ptr<MotionEntry> motionEntry = + std::static_pointer_cast<MotionEntry>(mPendingEvent); if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { dropReason = DropReason::APP_SWITCH; } - if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *typedEntry)) { + if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) { dropReason = DropReason::STALE; } if (dropReason == DropReason::NOT_DROPPED && mNextUnblockedEvent) { dropReason = DropReason::BLOCKED; } - done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); + done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime); break; } } @@ -769,17 +807,18 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt return false; } -bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { +bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newEntry) { bool needWake = mInboundQueue.empty(); - mInboundQueue.push_back(entry); + mInboundQueue.push_back(std::move(newEntry)); + EventEntry& entry = *(mInboundQueue.back()); traceInboundQueueLengthLocked(); - switch (entry->type) { + switch (entry.type) { case EventEntry::Type::KEY: { // Optimize app switch latency. // If the application takes too long to catch up then we drop all events preceding // the app switch key. - const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*entry); + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry); if (isAppSwitchKeyEvent(keyEntry)) { if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) { mAppSwitchSawKeyDown = true; @@ -798,8 +837,8 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { } case EventEntry::Type::MOTION: { - if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(*entry))) { - mNextUnblockedEvent = entry; + if (shouldPruneInboundQueueLocked(static_cast<MotionEntry&>(entry))) { + mNextUnblockedEvent = mInboundQueue.back(); needWake = true; } break; @@ -818,11 +857,9 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { return needWake; } -void InputDispatcher::addRecentEventLocked(EventEntry* entry) { - entry->refCount += 1; +void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) { mRecentQueue.push_back(entry); if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) { - mRecentQueue.front()->release(); mRecentQueue.pop_front(); } } @@ -836,17 +873,16 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display "Must provide a valid touch state if adding portal windows or outside targets"); } // Traverse windows from front to back to find touched window. - const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& windowHandle : windowHandles) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); if (windowInfo->displayId == displayId) { - int32_t flags = windowInfo->layoutParamsFlags; + auto flags = windowInfo->flags; if (windowInfo->visible) { - if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { - bool isTouchModal = (flags & - (InputWindowInfo::FLAG_NOT_FOCUSABLE | - InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0; + if (!flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) { + bool isTouchModal = !flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE) && + !flags.test(InputWindowInfo::Flag::NOT_TOUCH_MODAL); if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { int32_t portalToDisplayId = windowInfo->portalToDisplayId; if (portalToDisplayId != ADISPLAY_ID_NONE && @@ -863,7 +899,7 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display } } - if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) { + if (addOutsideTargets && flags.test(InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) { touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0)); @@ -1003,7 +1039,7 @@ void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEnt void InputDispatcher::drainInboundQueueLocked() { while (!mInboundQueue.empty()) { - EventEntry* entry = mInboundQueue.front(); + std::shared_ptr<EventEntry> entry = mInboundQueue.front(); mInboundQueue.pop_front(); releaseInboundEventLocked(entry); } @@ -1017,66 +1053,48 @@ void InputDispatcher::releasePendingEventLocked() { } } -void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { +void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) { InjectionState* injectionState = entry->injectionState; - if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) { + if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) { #if DEBUG_DISPATCH_CYCLE ALOGD("Injected inbound event was dropped."); #endif - setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED); + setInjectionResult(*entry, InputEventInjectionResult::FAILED); } if (entry == mNextUnblockedEvent) { mNextUnblockedEvent = nullptr; } addRecentEventLocked(entry); - entry->release(); } void InputDispatcher::resetKeyRepeatLocked() { if (mKeyRepeatState.lastKeyEntry) { - mKeyRepeatState.lastKeyEntry->release(); mKeyRepeatState.lastKeyEntry = nullptr; } } -KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { - KeyEntry* entry = mKeyRepeatState.lastKeyEntry; +std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { + std::shared_ptr<KeyEntry> entry = mKeyRepeatState.lastKeyEntry; - // Reuse the repeated key entry if it is otherwise unreferenced. uint32_t policyFlags = entry->policyFlags & (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED); - if (entry->refCount == 1) { - entry->recycle(); - entry->id = mIdGenerator.nextId(); - entry->eventTime = currentTime; - entry->policyFlags = policyFlags; - entry->repeatCount += 1; - } else { - KeyEntry* newEntry = - new KeyEntry(mIdGenerator.nextId(), currentTime, entry->deviceId, entry->source, - entry->displayId, policyFlags, entry->action, entry->flags, - entry->keyCode, entry->scanCode, entry->metaState, - entry->repeatCount + 1, entry->downTime); - - mKeyRepeatState.lastKeyEntry = newEntry; - entry->release(); - - entry = newEntry; - } - entry->syntheticRepeat = true; - // Increment reference count since we keep a reference to the event in - // mKeyRepeatState.lastKeyEntry in addition to the one we return. - entry->refCount += 1; + std::shared_ptr<KeyEntry> newEntry = + std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, entry->deviceId, + entry->source, entry->displayId, policyFlags, entry->action, + entry->flags, entry->keyCode, entry->scanCode, + entry->metaState, entry->repeatCount + 1, entry->downTime); + newEntry->syntheticRepeat = true; + mKeyRepeatState.lastKeyEntry = newEntry; mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay; - return entry; + return newEntry; } bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime, - ConfigurationChangedEntry* entry) { + const ConfigurationChangedEntry& entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime); + ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry.eventTime); #endif // Reset key repeating in case a keyboard device was added or removed or something. @@ -1085,24 +1103,26 @@ bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime, // Enqueue a command to run outside the lock to tell the policy that the configuration changed. std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); - commandEntry->eventTime = entry->eventTime; + commandEntry->eventTime = entry.eventTime; postCommandLocked(std::move(commandEntry)); return true; } -bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) { +bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, + const DeviceResetEntry& entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime, - entry->deviceId); + ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry.eventTime, + entry.deviceId); #endif CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset"); - options.deviceId = entry->deviceId; + options.deviceId = entry.deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); return true; } -void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) { +void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus, + std::string_view reason) { if (mPendingEvent != nullptr) { // Move the pending event to the front of the queue. This will give the chance // for the pending event to get dispatched to the newly focused window @@ -1110,21 +1130,24 @@ void InputDispatcher::enqueueFocusEventLocked(const InputWindowHandle& window, b mPendingEvent = nullptr; } - FocusEntry* focusEntry = - new FocusEntry(mIdGenerator.nextId(), now(), window.getToken(), hasFocus); + std::unique_ptr<FocusEntry> focusEntry = + std::make_unique<FocusEntry>(mIdGenerator.nextId(), now(), windowToken, hasFocus, + reason); // This event should go to the front of the queue, but behind all other focus events // Find the last focus event, and insert right after it - std::deque<EventEntry*>::reverse_iterator it = + std::deque<std::shared_ptr<EventEntry>>::reverse_iterator it = std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(), - [](EventEntry* event) { return event->type == EventEntry::Type::FOCUS; }); + [](const std::shared_ptr<EventEntry>& event) { + return event->type == EventEntry::Type::FOCUS; + }); // Maintain the order of focus events. Insert the entry after all other focus events. - mInboundQueue.insert(it.base(), focusEntry); + mInboundQueue.insert(it.base(), std::move(focusEntry)); } -void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) { - sp<InputChannel> channel = getInputChannelLocked(entry->connectionToken); +void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) { + std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken); if (channel == nullptr) { return; // Window has gone away } @@ -1132,11 +1155,14 @@ void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry target.inputChannel = channel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; entry->dispatchInProgress = true; - + std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") + + channel->getName(); + std::string reason = std::string("reason=").append(entry->reason); + android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS; dispatchEventLocked(currentTime, entry, {target}); } -bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, +bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. if (!entry->dispatchInProgress) { @@ -1144,11 +1170,17 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, (entry->policyFlags & POLICY_FLAG_TRUSTED) && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) { if (mKeyRepeatState.lastKeyEntry && - mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { + mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode && // We have seen two identical key downs in a row which indicates that the device // driver is automatically generating key repeats itself. We take note of the // repeat here, but we disable our own next key repeat timer since it is clear that // we will not need to synthesize key repeats ourselves. + mKeyRepeatState.lastKeyEntry->deviceId == entry->deviceId) { + // Make sure we don't get key down from a different device. If a different + // device Id has same key pressed down, the new device Id will replace the + // current one to hold the key repeat with repeat count reset. + // In the future when got a KEY_UP on the device id, drop it and do not + // stop the key repeat on current device. entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; resetKeyRepeatLocked(); mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves @@ -1158,7 +1190,12 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout; } mKeyRepeatState.lastKeyEntry = entry; - entry->refCount += 1; + } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry && + mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) { + // The key on device 'deviceId' is still down, do not stop key repeat +#if DEBUG_INBOUND_EVENT_DETAILS + ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId); +#endif } else if (!entry->syntheticRepeat) { resetKeyRepeatLocked(); } @@ -1191,14 +1228,13 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); - sp<InputWindowHandle> focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(*entry)); - if (focusedWindowHandle != nullptr) { - commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken()); + sp<IBinder> focusedWindowToken = + getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry)); + if (focusedWindowToken != nullptr) { + commandEntry->inputChannel = getInputChannelLocked(focusedWindowToken); } commandEntry->keyEntry = entry; postCommandLocked(std::move(commandEntry)); - entry->refCount += 1; return false; // wait for the command to run } else { entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; @@ -1211,23 +1247,23 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, // Clean up if dropping the event. if (*dropReason != DropReason::NOT_DROPPED) { - setInjectionResult(entry, - *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED - : INPUT_EVENT_INJECTION_FAILED); + setInjectionResult(*entry, + *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED + : InputEventInjectionResult::FAILED); mReporter->reportDroppedKey(entry->id); return true; } // Identify targets. std::vector<InputTarget> inputTargets; - int32_t injectionResult = + InputEventInjectionResult injectionResult = findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime); - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + if (injectionResult == InputEventInjectionResult::PENDING) { return false; } - setInjectionResult(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + setInjectionResult(*entry, injectionResult); + if (injectionResult != InputEventInjectionResult::SUCCEEDED) { return true; } @@ -1250,7 +1286,7 @@ void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& #endif } -bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, +bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ATRACE_CALL(); // Preprocessing. @@ -1262,9 +1298,9 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent // Clean up if dropping the event. if (*dropReason != DropReason::NOT_DROPPED) { - setInjectionResult(entry, - *dropReason == DropReason::POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED - : INPUT_EVENT_INJECTION_FAILED); + setInjectionResult(*entry, + *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED + : InputEventInjectionResult::FAILED); return true; } @@ -1274,7 +1310,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent std::vector<InputTarget> inputTargets; bool conflictingPointerActions = false; - int32_t injectionResult; + InputEventInjectionResult injectionResult; if (isPointerEvent) { // Pointer event. (eg. touchscreen) injectionResult = @@ -1285,16 +1321,16 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* ent injectionResult = findFocusedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime); } - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + if (injectionResult == InputEventInjectionResult::PENDING) { return false; } - setInjectionResult(entry, injectionResult); - if (injectionResult == INPUT_EVENT_INJECTION_PERMISSION_DENIED) { + setInjectionResult(*entry, injectionResult); + if (injectionResult == InputEventInjectionResult::PERMISSION_DENIED) { ALOGW("Permission denied, dropping the motion (isPointer=%s)", toString(isPointerEvent)); return true; } - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + if (injectionResult != InputEventInjectionResult::SUCCEEDED) { CancelationOptions::Mode mode(isPointerEvent ? CancelationOptions::CANCEL_POINTER_EVENTS : CancelationOptions::CANCEL_NON_POINTER_EVENTS); @@ -1363,13 +1399,16 @@ void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionE #endif } -void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, +void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, + std::shared_ptr<EventEntry> eventEntry, const std::vector<InputTarget>& inputTargets) { ATRACE_CALL(); #if DEBUG_DISPATCH_CYCLE ALOGD("dispatchEventToCurrentInputTargets"); #endif + updateInteractionTokensLocked(*eventEntry, inputTargets); + ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true pokeUserActivityLocked(*eventEntry); @@ -1411,7 +1450,7 @@ void InputDispatcher::resetNoFocusedWindowTimeoutLocked() { // Reset input target wait timeout. mNoFocusedWindowTimeoutTime = std::nullopt; - mAwaitedFocusedApplication.clear(); + mAwaitedFocusedApplication.reset(); } /** @@ -1455,7 +1494,9 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, ALOGD("Waiting to send key to %s because there are unprocessed events that may cause " "focus to change", focusedWindowName); - mKeyIsWaitingForEventsTimeout = currentTime + KEY_WAITING_FOR_EVENTS_TIMEOUT.count(); + mKeyIsWaitingForEventsTimeout = currentTime + + std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT) + .count(); return true; } @@ -1472,16 +1513,14 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, return false; } -int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, - const EventEntry& entry, - std::vector<InputTarget>& inputTargets, - nsecs_t* nextWakeupTime) { +InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( + nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets, + nsecs_t* nextWakeupTime) { std::string reason; int32_t displayId = getTargetDisplayId(entry); - sp<InputWindowHandle> focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - sp<InputApplicationHandle> focusedApplicationHandle = + sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); + std::shared_ptr<InputApplicationHandle> focusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); // If there is no currently focused window and no focused application @@ -1490,7 +1529,7 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, ALOGI("Dropping %s event because there is no focused window or focused application in " "display %" PRId32 ".", EventEntry::typeToString(entry.type), displayId); - return INPUT_EVENT_INJECTION_FAILED; + return InputEventInjectionResult::FAILED; } // Compatibility behavior: raise ANR if there is a focused application, but no focused window. @@ -1501,24 +1540,24 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, if (focusedWindowHandle == nullptr && focusedApplicationHandle != nullptr) { if (!mNoFocusedWindowTimeoutTime.has_value()) { // We just discovered that there's no focused window. Start the ANR timer - const nsecs_t timeout = focusedApplicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT.count()); - mNoFocusedWindowTimeoutTime = currentTime + timeout; + std::chrono::nanoseconds timeout = focusedApplicationHandle->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT); + mNoFocusedWindowTimeoutTime = currentTime + timeout.count(); mAwaitedFocusedApplication = focusedApplicationHandle; mAwaitedApplicationDisplayId = displayId; ALOGW("Waiting because no window has focus but %s may eventually add a " "window when it finishes starting up. Will wait for %" PRId64 "ms", - mAwaitedFocusedApplication->getName().c_str(), ns2ms(timeout)); + mAwaitedFocusedApplication->getName().c_str(), millis(timeout)); *nextWakeupTime = *mNoFocusedWindowTimeoutTime; - return INPUT_EVENT_INJECTION_PENDING; + return InputEventInjectionResult::PENDING; } else if (currentTime > *mNoFocusedWindowTimeoutTime) { // Already raised ANR. Drop the event ALOGE("Dropping %s event because there is no focused window", EventEntry::typeToString(entry.type)); - return INPUT_EVENT_INJECTION_FAILED; + return InputEventInjectionResult::FAILED; } else { // Still waiting for the focused window - return INPUT_EVENT_INJECTION_PENDING; + return InputEventInjectionResult::PENDING; } } @@ -1527,12 +1566,12 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // Check permissions. if (!checkInjectionPermission(focusedWindowHandle, entry.injectionState)) { - return INPUT_EVENT_INJECTION_PERMISSION_DENIED; + return InputEventInjectionResult::PERMISSION_DENIED; } if (focusedWindowHandle->getInfo()->paused) { ALOGI("Waiting because %s is paused", focusedWindowHandle->getName().c_str()); - return INPUT_EVENT_INJECTION_PENDING; + return InputEventInjectionResult::PENDING; } // If the event is a key event, then we must wait for all previous events to @@ -1549,7 +1588,7 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, if (entry.type == EventEntry::Type::KEY) { if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { *nextWakeupTime = *mKeyIsWaitingForEventsTimeout; - return INPUT_EVENT_INJECTION_PENDING; + return InputEventInjectionResult::PENDING; } } @@ -1559,7 +1598,7 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, BitSet32(0), inputTargets); // Done. - return INPUT_EVENT_INJECTION_SUCCEEDED; + return InputEventInjectionResult::SUCCEEDED; } /** @@ -1588,11 +1627,9 @@ std::vector<TouchedMonitor> InputDispatcher::selectResponsiveMonitorsLocked( return responsiveMonitors; } -int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry& entry, - std::vector<InputTarget>& inputTargets, - nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions) { +InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( + nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, + nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) { ATRACE_CALL(); enum InjectionPermission { INJECTION_PERMISSION_UNKNOWN, @@ -1607,9 +1644,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; // Update the touch state as needed based on the properties of the touch event. - int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; + InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING; InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; - sp<InputWindowHandle> newHoverWindowHandle; + sp<InputWindowHandle> newHoverWindowHandle(mLastHoverWindowHandle); + sp<InputWindowHandle> newTouchedWindowHandle; // Copy current touch state into tempTouchState. // This state will be used to update mTouchStatesByDisplay at the end of this function. @@ -1641,7 +1679,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, "in display %" PRId32, displayId); // TODO: test multiple simultaneous input streams. - injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionResult = InputEventInjectionResult::FAILED; switchedDevice = false; wrongDevice = true; goto Failed; @@ -1657,7 +1695,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, "in display %" PRId32, displayId); // TODO: test multiple simultaneous input streams. - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + injectionResult = InputEventInjectionResult::PERMISSION_DENIED; switchedDevice = false; wrongDevice = true; goto Failed; @@ -1678,7 +1716,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); } bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; - sp<InputWindowHandle> newTouchedWindowHandle = + newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isDown /*addOutsideTargets*/, true /*addPortalWindows*/); @@ -1709,20 +1747,37 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, newTouchedWindowHandle = nullptr; } + // Ensure the window has a connection and the connection is responsive if (newTouchedWindowHandle != nullptr) { - sp<Connection> connection = getConnectionLocked(newTouchedWindowHandle->getToken()); - if (connection == nullptr) { - ALOGI("Could not find connection for %s", - newTouchedWindowHandle->getName().c_str()); - newTouchedWindowHandle = nullptr; - } else if (!connection->responsive) { - // don't send the new touch to an unresponsive window - ALOGW("Unresponsive window %s will not get the new gesture at %" PRIu64, + const bool isResponsive = hasResponsiveConnectionLocked(*newTouchedWindowHandle); + if (!isResponsive) { + ALOGW("%s will not receive the new gesture at %" PRIu64, newTouchedWindowHandle->getName().c_str(), entry.eventTime); newTouchedWindowHandle = nullptr; } } + // Drop events that can't be trusted due to occlusion + if (newTouchedWindowHandle != nullptr && + mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) { + TouchOcclusionInfo occlusionInfo = + computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y); + if (!isTouchTrustedLocked(occlusionInfo)) { + if (DEBUG_TOUCH_OCCLUSION) { + ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y); + for (const auto& log : occlusionInfo.debugInfo) { + ALOGD("%s", log.c_str()); + } + } + onUntrustedTouchLocked(occlusionInfo.obscuringPackage); + if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) { + ALOGW("Dropping untrusted touch event due to %s/%d", + occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid); + newTouchedWindowHandle = nullptr; + } + } + } + // Also don't send the new touch event to unresponsive gesture monitors newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors); @@ -1730,7 +1785,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, ALOGI("Dropping event because there is no touchable window or gesture monitor at " "(%d, %d) in display %" PRId32 ".", x, y, displayId); - injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionResult = InputEventInjectionResult::FAILED; goto Failed; } @@ -1747,10 +1802,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Update hover state. - if (isHoverAction) { + if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) { + newHoverWindowHandle = nullptr; + } else if (isHoverAction) { newHoverWindowHandle = newTouchedWindowHandle; - } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { - newHoverWindowHandle = mLastHoverWindowHandle; } // Update the temporary touch state. @@ -1773,7 +1828,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, "dropped the pointer down event in display %" PRId32, displayId); } - injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionResult = InputEventInjectionResult::FAILED; goto Failed; } @@ -1785,8 +1840,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, sp<InputWindowHandle> oldTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); - sp<InputWindowHandle> newTouchedWindowHandle = - findTouchedWindowAtLocked(displayId, x, y, &tempTouchState); + newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState); if (oldTouchedWindowHandle != newTouchedWindowHandle && oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { if (DEBUG_FOCUS) { @@ -1823,8 +1877,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } if (newHoverWindowHandle != mLastHoverWindowHandle) { - // Let the previous window know that the hover sequence is over. - if (mLastHoverWindowHandle != nullptr) { + // Let the previous window know that the hover sequence is over, unless we already did it + // when dispatching it as is to newTouchedWindowHandle. + if (mLastHoverWindowHandle != nullptr && + (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT || + mLastHoverWindowHandle != newTouchedWindowHandle)) { #if DEBUG_HOVER ALOGD("Sending hover exit event to window %s.", mLastHoverWindowHandle->getName().c_str()); @@ -1833,8 +1890,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); } - // Let the new window know that the hover sequence is starting. - if (newHoverWindowHandle != nullptr) { + // Let the new window know that the hover sequence is starting, unless we already did it + // when dispatching it as is to newTouchedWindowHandle. + if (newHoverWindowHandle != nullptr && + (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER || + newHoverWindowHandle != newTouchedWindowHandle)) { #if DEBUG_HOVER ALOGD("Sending hover enter event to window %s.", newHoverWindowHandle->getName().c_str()); @@ -1853,7 +1913,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { haveForegroundWindow = true; if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) { - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + injectionResult = InputEventInjectionResult::PERMISSION_DENIED; injectionPermission = INJECTION_PERMISSION_DENIED; goto Failed; } @@ -1864,7 +1924,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, ALOGI("Dropping event because there is no touched foreground window in display " "%" PRId32 " or gesture monitor to receive it.", displayId); - injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionResult = InputEventInjectionResult::FAILED; goto Failed; } @@ -1902,12 +1962,12 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, sp<InputWindowHandle> foregroundWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); if (foregroundWindowHandle && foregroundWindowHandle->getInfo()->hasWallpaper) { - const std::vector<sp<InputWindowHandle>> windowHandles = + const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& windowHandle : windowHandles) { const InputWindowInfo* info = windowHandle->getInfo(); if (info->displayId == displayId && - windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) { + windowHandle->getInfo()->type == InputWindowInfo::Type::WALLPAPER) { tempTouchState .addOrUpdateWindow(windowHandle, InputTarget::FLAG_WINDOW_IS_OBSCURED | @@ -1921,7 +1981,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } // Success! Output targets. - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + injectionResult = InputEventInjectionResult::SUCCEEDED; for (const TouchedWindow& touchedWindow : tempTouchState.windows) { addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, @@ -2039,7 +2099,8 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH if (it == inputTargets.end()) { InputTarget inputTarget; - sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); + std::shared_ptr<InputChannel> inputChannel = + getInputChannelLocked(windowHandle->getToken()); if (inputChannel == nullptr) { ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); return; @@ -2054,8 +2115,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH ALOG_ASSERT(it->flags == targetFlags); ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor); - it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); + it->addPointers(pointerIds, windowInfo->transform); } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, @@ -2078,7 +2138,9 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */); + ui::Transform t; + t.set(xOffset, yOffset); + target.setDefaultPointerTransform(t); inputTargets.push_back(target); } @@ -2117,11 +2179,20 @@ static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle, auto otherInfo = otherHandle->getInfo(); if (!otherInfo->visible) { return false; - } else if (info->ownerPid == otherInfo->ownerPid) { - // If ownerPid is the same we don't generate occlusion events as there - // is no in-process security boundary. + } else if (otherInfo->alpha == 0 && + otherInfo->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE)) { + // Those act as if they were invisible, so we don't need to flag them. + // We do want to potentially flag touchable windows even if they have 0 + // opacity, since they can consume touches and alter the effects of the + // user interaction (eg. apps that rely on + // FLAG_WINDOW_IS_PARTIALLY_OBSCURED should still be told about those + // windows), hence we also check for FLAG_NOT_TOUCHABLE. + return false; + } else if (info->ownerUid == otherInfo->ownerUid) { + // If ownerUid is the same we don't generate occlusion events as there + // is no security boundary within an uid. return false; - } else if (otherInfo->isTrustedOverlay()) { + } else if (otherInfo->trustedOverlay) { return false; } else if (otherInfo->displayId != info->displayId) { return false; @@ -2129,16 +2200,118 @@ static bool canBeObscuredBy(const sp<InputWindowHandle>& windowHandle, return true; } +/** + * Returns touch occlusion information in the form of TouchOcclusionInfo. To check if the touch is + * untrusted, one should check: + * + * 1. If result.hasBlockingOcclusion is true. + * If it's, it means the touch should be blocked due to a window with occlusion mode of + * BLOCK_UNTRUSTED. + * + * 2. If result.obscuringOpacity > mMaximumObscuringOpacityForTouch. + * If it is (and 1 is false), then the touch should be blocked because a stack of windows + * (possibly only one) with occlusion mode of USE_OPACITY from one UID resulted in a composed + * obscuring opacity above the threshold. Note that if there was no window of occlusion mode + * USE_OPACITY, result.obscuringOpacity would've been 0 and since + * mMaximumObscuringOpacityForTouch >= 0, the condition above would never be true. + * + * If neither of those is true, then it means the touch can be allowed. + */ +InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked( + const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const { + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + int32_t displayId = windowInfo->displayId; + const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); + TouchOcclusionInfo info; + info.hasBlockingOcclusion = false; + info.obscuringOpacity = 0; + info.obscuringUid = -1; + std::map<int32_t, float> opacityByUid; + for (const sp<InputWindowHandle>& otherHandle : windowHandles) { + if (windowHandle == otherHandle) { + break; // All future windows are below us. Exit early. + } + const InputWindowInfo* otherInfo = otherHandle->getInfo(); + if (canBeObscuredBy(windowHandle, otherHandle) && + windowInfo->ownerUid != otherInfo->ownerUid && otherInfo->frameContainsPoint(x, y)) { + if (DEBUG_TOUCH_OCCLUSION) { + info.debugInfo.push_back( + dumpWindowForTouchOcclusion(otherInfo, /* isTouchedWindow */ false)); + } + // canBeObscuredBy() has returned true above, which means this window is untrusted, so + // we perform the checks below to see if the touch can be propagated or not based on the + // window's touch occlusion mode + if (otherInfo->touchOcclusionMode == TouchOcclusionMode::BLOCK_UNTRUSTED) { + info.hasBlockingOcclusion = true; + info.obscuringUid = otherInfo->ownerUid; + info.obscuringPackage = otherInfo->packageName; + break; + } + if (otherInfo->touchOcclusionMode == TouchOcclusionMode::USE_OPACITY) { + uint32_t uid = otherInfo->ownerUid; + float opacity = + (opacityByUid.find(uid) == opacityByUid.end()) ? 0 : opacityByUid[uid]; + // Given windows A and B: + // opacity(A, B) = 1 - [1 - opacity(A)] * [1 - opacity(B)] + opacity = 1 - (1 - opacity) * (1 - otherInfo->alpha); + opacityByUid[uid] = opacity; + if (opacity > info.obscuringOpacity) { + info.obscuringOpacity = opacity; + info.obscuringUid = uid; + info.obscuringPackage = otherInfo->packageName; + } + } + } + } + if (DEBUG_TOUCH_OCCLUSION) { + info.debugInfo.push_back( + dumpWindowForTouchOcclusion(windowInfo, /* isTouchedWindow */ true)); + } + return info; +} + +std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info, + bool isTouchedWindow) const { + return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", mode=%s, alpha=%.2f, " + "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32 + "], touchableRegion=%s, window={%s}, applicationInfo=%s, " + "flags={%s}, inputFeatures={%s}, hasToken=%s\n", + (isTouchedWindow) ? "[TOUCHED] " : "", + NamedEnum::string(info->type, "%" PRId32).c_str(), + info->packageName.c_str(), info->ownerUid, + toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft, + info->frameTop, info->frameRight, info->frameBottom, + dumpRegion(info->touchableRegion).c_str(), info->name.c_str(), + info->applicationInfo.name.c_str(), info->flags.string().c_str(), + info->inputFeatures.string().c_str(), toString(info->token != nullptr)); +} + +bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const { + if (occlusionInfo.hasBlockingOcclusion) { + ALOGW("Untrusted touch due to occlusion by %s/%d", occlusionInfo.obscuringPackage.c_str(), + occlusionInfo.obscuringUid); + return false; + } + if (occlusionInfo.obscuringOpacity > mMaximumObscuringOpacityForTouch) { + ALOGW("Untrusted touch due to occlusion by %s/%d (obscuring opacity = " + "%.2f, maximum allowed = %.2f)", + occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid, + occlusionInfo.obscuringOpacity, mMaximumObscuringOpacityForTouch); + return false; + } + return true; +} + bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const { int32_t displayId = windowHandle->getInfo()->displayId; - const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& otherHandle : windowHandles) { if (windowHandle == otherHandle) { break; // All future windows are below us. Exit early. } const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (canBeObscuredBy(windowHandle, otherHandle) && + if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y)) { return true; } @@ -2148,13 +2321,12 @@ bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const { int32_t displayId = windowHandle->getInfo()->displayId; - const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); + const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); const InputWindowInfo* windowInfo = windowHandle->getInfo(); for (const sp<InputWindowHandle>& otherHandle : windowHandles) { if (windowHandle == otherHandle) { break; // All future windows are below us. Exit early. } - const InputWindowInfo* otherInfo = otherHandle->getInfo(); if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->overlaps(windowInfo)) { @@ -2165,7 +2337,7 @@ bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& window } std::string InputDispatcher::getApplicationWindowLabel( - const sp<InputApplicationHandle>& applicationHandle, + const std::shared_ptr<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle) { if (applicationHandle != nullptr) { if (windowHandle != nullptr) { @@ -2186,11 +2358,10 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { return; } int32_t displayId = getTargetDisplayId(eventEntry); - sp<InputWindowHandle> focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + sp<InputWindowHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId); if (focusedWindowHandle != nullptr) { const InputWindowInfo* info = focusedWindowHandle->getInfo(); - if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) { + if (info->inputFeatures.test(InputWindowInfo::Feature::DISABLE_USER_ACTIVITY)) { #if DEBUG_DISPATCH_CYCLE ALOGD("Not poking user activity: disabled by window '%s'.", info->name.c_str()); #endif @@ -2237,7 +2408,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - EventEntry* eventEntry, + std::shared_ptr<EventEntry> eventEntry, const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = @@ -2271,7 +2442,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry); if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { - MotionEntry* splitMotionEntry = + std::unique_ptr<MotionEntry> splitMotionEntry = splitMotionEvent(originalMotionEntry, inputTarget.pointerIds); if (!splitMotionEntry) { return; // split event was dropped @@ -2281,8 +2452,8 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName().c_str()); logOutboundMotionDetails(" ", *splitMotionEntry); } - enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); - splitMotionEntry->release(); + enqueueDispatchEntriesLocked(currentTime, connection, std::move(splitMotionEntry), + inputTarget); return; } } @@ -2293,7 +2464,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, - EventEntry* eventEntry, + std::shared_ptr<EventEntry> eventEntry, const InputTarget& inputTarget) { if (ATRACE_ENABLED()) { std::string message = @@ -2325,7 +2496,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, } void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, - EventEntry* eventEntry, + std::shared_ptr<EventEntry> eventEntry, const InputTarget& inputTarget, int32_t dispatchMode) { if (ATRACE_ENABLED()) { @@ -2347,11 +2518,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a // different EventEntry than what was passed in. - EventEntry* newEntry = dispatchEntry->eventEntry; + EventEntry& newEntry = *(dispatchEntry->eventEntry); // Apply target flags and update the connection's input state. - switch (newEntry->type) { + switch (newEntry.type) { case EventEntry::Type::KEY: { - const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*newEntry); + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(newEntry); dispatchEntry->resolvedEventId = keyEntry.id; dispatchEntry->resolvedAction = keyEntry.action; dispatchEntry->resolvedFlags = keyEntry.flags; @@ -2368,7 +2539,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio } case EventEntry::Type::MOTION: { - const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*newEntry); + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(newEntry); // Assign a default value to dispatchEntry that will never be generated by InputReader, // and assign a InputDispatcher value if it doesn't change in the if-else chain below. constexpr int32_t DEFAULT_RESOLVED_EVENT_ID = @@ -2439,7 +2610,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", - EventEntry::typeToString(newEntry->type)); + EventEntry::typeToString(newEntry.type)); break; } } @@ -2454,31 +2625,92 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio traceOutboundQueueLength(connection); } +/** + * This function is purely for debugging. It helps us understand where the user interaction + * was taking place. For example, if user is touching launcher, we will see a log that user + * started interacting with launcher. In that example, the event would go to the wallpaper as well. + * We will see both launcher and wallpaper in that list. + * Once the interaction with a particular set of connections starts, no new logs will be printed + * until the set of interacted connections changes. + * + * The following items are skipped, to reduce the logspam: + * ACTION_OUTSIDE: any windows that are receiving ACTION_OUTSIDE are not logged + * ACTION_UP: any windows that receive ACTION_UP are not logged (for both keys and motions). + * 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) { + // Skip ACTION_UP events, and all events other than keys and motions + if (entry.type == EventEntry::Type::KEY) { + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry); + if (keyEntry.action == AKEY_EVENT_ACTION_UP) { + return; + } + } else if (entry.type == EventEntry::Type::MOTION) { + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry); + if (motionEntry.action == AMOTION_EVENT_ACTION_UP || + motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) { + return; + } + } else { + return; // Not a key or a motion + } + + std::unordered_set<sp<IBinder>, IBinderHash> newConnectionTokens; + std::vector<sp<Connection>> newConnections; + for (const InputTarget& target : targets) { + if ((target.flags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) == + InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + continue; // Skip windows that receive ACTION_OUTSIDE + } + + 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. + } + newConnectionTokens.insert(std::move(token)); + newConnections.emplace_back(connection); + } + if (newConnectionTokens == mInteractionConnectionTokens) { + return; // no change + } + mInteractionConnectionTokens = newConnectionTokens; + + std::string windowList; + for (const sp<Connection>& connection : newConnections) { + windowList += connection->getWindowName() + ", "; + } + std::string message = "Interaction with windows: " + windowList; + if (windowList.empty()) { + message += "<none>"; + } + android_log_event_list(LOGTAG_INPUT_INTERACTION) << message << LOG_ID_EVENTS; +} + void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, - const sp<IBinder>& newToken) { + const sp<IBinder>& token) { int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK; if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) { return; } - sp<InputWindowHandle> inputWindowHandle = getWindowHandleLocked(newToken); - if (inputWindowHandle == nullptr) { - return; - } - - sp<InputWindowHandle> focusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); - - bool hasFocusChanged = !focusedWindowHandle || focusedWindowHandle->getToken() != newToken; - - if (!hasFocusChanged) { + sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId); + if (focusedToken == token) { + // ignore since token is focused return; } std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); - commandEntry->newToken = newToken; + commandEntry->newToken = token; postCommandLocked(std::move(commandEntry)); } @@ -2496,51 +2728,44 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; - const nsecs_t timeout = + const std::chrono::nanoseconds timeout = getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken()); - dispatchEntry->timeoutTime = currentTime + timeout; + dispatchEntry->timeoutTime = currentTime + timeout.count(); // Publish the event. status_t status; - EventEntry* eventEntry = dispatchEntry->eventEntry; - switch (eventEntry->type) { + const EventEntry& eventEntry = *(dispatchEntry->eventEntry); + switch (eventEntry.type) { case EventEntry::Type::KEY: { - const KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); - std::array<uint8_t, 32> hmac = getSignature(*keyEntry, *dispatchEntry); + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry); + std::array<uint8_t, 32> hmac = getSignature(keyEntry, *dispatchEntry); // Publish the key event. - status = - connection->inputPublisher - .publishKeyEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId, - keyEntry->deviceId, keyEntry->source, - keyEntry->displayId, std::move(hmac), - dispatchEntry->resolvedAction, - dispatchEntry->resolvedFlags, keyEntry->keyCode, - keyEntry->scanCode, keyEntry->metaState, - keyEntry->repeatCount, keyEntry->downTime, - keyEntry->eventTime); + status = connection->inputPublisher + .publishKeyEvent(dispatchEntry->seq, + dispatchEntry->resolvedEventId, keyEntry.deviceId, + keyEntry.source, keyEntry.displayId, + std::move(hmac), dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags, keyEntry.keyCode, + keyEntry.scanCode, keyEntry.metaState, + keyEntry.repeatCount, keyEntry.downTime, + keyEntry.eventTime); break; } case EventEntry::Type::MOTION: { - MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); + const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry); PointerCoords scaledCoords[MAX_POINTERS]; - const PointerCoords* usingCoords = motionEntry->pointerCoords; + const PointerCoords* usingCoords = motionEntry.pointerCoords; // Set the X and Y offset and X and Y scale depending on the input source. - float xOffset = 0.0f, yOffset = 0.0f; - float xScale = 1.0f, yScale = 1.0f; - if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && + if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { float globalScaleFactor = dispatchEntry->globalScaleFactor; - xScale = dispatchEntry->windowXScale; - yScale = dispatchEntry->windowYScale; - xOffset = dispatchEntry->xOffset * xScale; - yOffset = dispatchEntry->yOffset * yScale; if (globalScaleFactor != 1.0f) { - for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i] = motionEntry->pointerCoords[i]; + for (uint32_t i = 0; i < motionEntry.pointerCount; i++) { + scaledCoords[i] = motionEntry.pointerCoords[i]; // Don't apply window scale here since we don't want scale to affect raw // coordinates. The scale will be sent back to the client and applied // later when requesting relative coordinates. @@ -2552,42 +2777,42 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } else { // We don't want the dispatch target to know. if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { - for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { + for (uint32_t i = 0; i < motionEntry.pointerCount; i++) { scaledCoords[i].clear(); } usingCoords = scaledCoords; } } - std::array<uint8_t, 32> hmac = getSignature(*motionEntry, *dispatchEntry); + std::array<uint8_t, 32> hmac = getSignature(motionEntry, *dispatchEntry); // Publish the motion event. status = connection->inputPublisher .publishMotionEvent(dispatchEntry->seq, dispatchEntry->resolvedEventId, - motionEntry->deviceId, motionEntry->source, - motionEntry->displayId, std::move(hmac), + motionEntry.deviceId, motionEntry.source, + motionEntry.displayId, std::move(hmac), dispatchEntry->resolvedAction, - motionEntry->actionButton, + motionEntry.actionButton, dispatchEntry->resolvedFlags, - motionEntry->edgeFlags, motionEntry->metaState, - motionEntry->buttonState, - motionEntry->classification, xScale, yScale, - xOffset, yOffset, motionEntry->xPrecision, - motionEntry->yPrecision, - motionEntry->xCursorPosition, - motionEntry->yCursorPosition, - motionEntry->downTime, motionEntry->eventTime, - motionEntry->pointerCount, - motionEntry->pointerProperties, usingCoords); - reportTouchEventForStatistics(*motionEntry); + motionEntry.edgeFlags, motionEntry.metaState, + motionEntry.buttonState, + motionEntry.classification, + dispatchEntry->transform, + motionEntry.xPrecision, motionEntry.yPrecision, + motionEntry.xCursorPosition, + motionEntry.yCursorPosition, + motionEntry.downTime, motionEntry.eventTime, + motionEntry.pointerCount, + motionEntry.pointerProperties, usingCoords); + reportTouchEventForStatistics(motionEntry); break; } case EventEntry::Type::FOCUS: { - FocusEntry* focusEntry = static_cast<FocusEntry*>(eventEntry); + const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry); status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq, - focusEntry->id, - focusEntry->hasFocus, + focusEntry.id, + focusEntry.hasFocus, mInTouchMode); break; } @@ -2595,7 +2820,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events", - EventEntry::typeToString(eventEntry->type)); + EventEntry::typeToString(eventEntry.type)); return; } } @@ -2642,6 +2867,22 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } } +std::array<uint8_t, 32> InputDispatcher::sign(const VerifiedInputEvent& event) const { + size_t size; + switch (event.type) { + case VerifiedInputEvent::Type::KEY: { + size = sizeof(VerifiedKeyEvent); + break; + } + case VerifiedInputEvent::Type::MOTION: { + size = sizeof(VerifiedMotionEvent); + break; + } + } + const uint8_t* start = reinterpret_cast<const uint8_t*>(&event); + return mHmacKeyManager.sign(start, size); +} + const std::array<uint8_t, 32> InputDispatcher::getSignature( const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const { int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK; @@ -2651,7 +2892,7 @@ const std::array<uint8_t, 32> InputDispatcher::getSignature( VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry); verifiedEvent.actionMasked = actionMasked; verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS; - return mHmacKeyManager.sign(verifiedEvent); + return sign(verifiedEvent); } return INVALID_HMAC; } @@ -2661,7 +2902,7 @@ const std::array<uint8_t, 32> InputDispatcher::getSignature( VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEntry(keyEntry); verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_KEY_EVENT_FLAGS; verifiedEvent.action = dispatchEntry.resolvedAction; - return mHmacKeyManager.sign(verifiedEvent); + return sign(verifiedEvent); } void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, @@ -2717,7 +2958,7 @@ void InputDispatcher::drainDispatchQueue(std::deque<DispatchEntry*>& queue) { void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) { if (dispatchEntry->hasForegroundTarget()) { - decrementPendingForegroundDispatches(dispatchEntry->eventEntry); + decrementPendingForegroundDispatches(*(dispatchEntry->eventEntry)); } delete dispatchEntry; } @@ -2785,8 +3026,8 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { } } - // Unregister the channel. - d->unregisterInputChannelLocked(connection->inputChannel, notify); + // Remove the channel. + d->removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify); return 0; // remove the callback } // release lock } @@ -2816,7 +3057,7 @@ void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( } void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( - const sp<InputChannel>& channel, const CancelationOptions& options) { + const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) { sp<Connection> connection = getConnectionLocked(channel->getConnectionToken()); if (connection == nullptr) { return; @@ -2833,7 +3074,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( nsecs_t currentTime = now(); - std::vector<EventEntry*> cancelationEvents = + std::vector<std::unique_ptr<EventEntry>> cancelationEvents = connection->inputState.synthesizeCancelationEvents(currentTime, options); if (cancelationEvents.empty()) { @@ -2851,15 +3092,14 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); + target.setDefaultPointerTransform(windowInfo->transform); target.globalScaleFactor = windowInfo->globalScaleFactor; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; for (size_t i = 0; i < cancelationEvents.size(); i++) { - EventEntry* cancelationEventEntry = cancelationEvents[i]; + std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]); switch (cancelationEventEntry->type) { case EventEntry::Type::KEY: { logOutboundKeyDetails("cancel - ", @@ -2883,10 +3123,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } } - enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref - target, InputTarget::FLAG_DISPATCH_AS_IS); - - cancelationEventEntry->release(); + enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), target, + InputTarget::FLAG_DISPATCH_AS_IS); } startDispatchCycleLocked(currentTime, connection); @@ -2900,7 +3138,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( nsecs_t currentTime = now(); - std::vector<EventEntry*> downEvents = + std::vector<std::unique_ptr<EventEntry>> downEvents = connection->inputState.synthesizePointerDownEvents(currentTime); if (downEvents.empty()) { @@ -2917,14 +3155,13 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); - target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop, - windowInfo->windowXScale, windowInfo->windowYScale); + target.setDefaultPointerTransform(windowInfo->transform); target.globalScaleFactor = windowInfo->globalScaleFactor; } target.inputChannel = connection->inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - for (EventEntry* downEventEntry : downEvents) { + for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { switch (downEventEntry->type) { case EventEntry::Type::MOTION: { logOutboundMotionDetails("down - ", @@ -2942,17 +3179,15 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } } - enqueueDispatchEntryLocked(connection, downEventEntry, // increments ref - target, InputTarget::FLAG_DISPATCH_AS_IS); - - downEventEntry->release(); + enqueueDispatchEntryLocked(connection, std::move(downEventEntry), target, + InputTarget::FLAG_DISPATCH_AS_IS); } startDispatchCycleLocked(currentTime, connection); } -MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotionEntry, - BitSet32 pointerIds) { +std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( + const MotionEntry& originalMotionEntry, BitSet32 pointerIds) { ALOG_ASSERT(pointerIds.value != 0); uint32_t splitPointerIndexMap[MAX_POINTERS]; @@ -3025,17 +3260,22 @@ MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry& originalMotion originalMotionEntry.id, newId); ATRACE_NAME(message.c_str()); } - MotionEntry* splitMotionEntry = - new MotionEntry(newId, originalMotionEntry.eventTime, originalMotionEntry.deviceId, - originalMotionEntry.source, originalMotionEntry.displayId, - originalMotionEntry.policyFlags, action, - originalMotionEntry.actionButton, originalMotionEntry.flags, - originalMotionEntry.metaState, originalMotionEntry.buttonState, - originalMotionEntry.classification, originalMotionEntry.edgeFlags, - originalMotionEntry.xPrecision, originalMotionEntry.yPrecision, - originalMotionEntry.xCursorPosition, - originalMotionEntry.yCursorPosition, originalMotionEntry.downTime, - splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); + std::unique_ptr<MotionEntry> splitMotionEntry = + std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime, + originalMotionEntry.deviceId, originalMotionEntry.source, + originalMotionEntry.displayId, + originalMotionEntry.policyFlags, action, + originalMotionEntry.actionButton, + originalMotionEntry.flags, originalMotionEntry.metaState, + originalMotionEntry.buttonState, + originalMotionEntry.classification, + originalMotionEntry.edgeFlags, + originalMotionEntry.xPrecision, + originalMotionEntry.yPrecision, + originalMotionEntry.xCursorPosition, + originalMotionEntry.yCursorPosition, + originalMotionEntry.downTime, splitPointerCount, + splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry.injectionState) { splitMotionEntry->injectionState = originalMotionEntry.injectionState; @@ -3054,9 +3294,9 @@ void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChange { // acquire lock std::scoped_lock _l(mLock); - ConfigurationChangedEntry* newEntry = - new ConfigurationChangedEntry(args->id, args->eventTime); - needWake = enqueueInboundEventLocked(newEntry); + std::unique_ptr<ConfigurationChangedEntry> newEntry = + std::make_unique<ConfigurationChangedEntry>(args->id, args->eventTime); + needWake = enqueueInboundEventLocked(std::move(newEntry)); } // release lock if (needWake) { @@ -3160,12 +3400,13 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { mLock.lock(); } - KeyEntry* newEntry = - new KeyEntry(args->id, args->eventTime, args->deviceId, args->source, - args->displayId, policyFlags, args->action, flags, keyCode, - args->scanCode, metaState, repeatCount, args->downTime); + std::unique_ptr<KeyEntry> newEntry = + std::make_unique<KeyEntry>(args->id, args->eventTime, args->deviceId, args->source, + args->displayId, policyFlags, args->action, flags, + keyCode, args->scanCode, metaState, repeatCount, + args->downTime); - needWake = enqueueInboundEventLocked(newEntry); + needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); } // release lock @@ -3229,13 +3470,13 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; + ui::Transform transform; event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, args->actionButton, args->flags, args->edgeFlags, - args->metaState, args->buttonState, args->classification, 1 /*xScale*/, - 1 /*yScale*/, 0 /* xOffset */, 0 /* yOffset */, args->xPrecision, - args->yPrecision, args->xCursorPosition, args->yCursorPosition, - args->downTime, args->eventTime, args->pointerCount, - args->pointerProperties, args->pointerCoords); + args->metaState, args->buttonState, args->classification, transform, + args->xPrecision, args->yPrecision, args->xCursorPosition, + args->yCursorPosition, args->downTime, args->eventTime, + args->pointerCount, args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -3246,16 +3487,18 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { } // Just enqueue a new motion event. - MotionEntry* newEntry = - new MotionEntry(args->id, args->eventTime, args->deviceId, args->source, - args->displayId, policyFlags, args->action, args->actionButton, - args->flags, args->metaState, args->buttonState, - args->classification, args->edgeFlags, args->xPrecision, - args->yPrecision, args->xCursorPosition, args->yCursorPosition, - args->downTime, args->pointerCount, args->pointerProperties, - args->pointerCoords, 0, 0); - - needWake = enqueueInboundEventLocked(newEntry); + std::unique_ptr<MotionEntry> newEntry = + std::make_unique<MotionEntry>(args->id, args->eventTime, args->deviceId, + args->source, args->displayId, policyFlags, + args->action, args->actionButton, args->flags, + args->metaState, args->buttonState, + args->classification, args->edgeFlags, + args->xPrecision, args->yPrecision, + args->xCursorPosition, args->yCursorPosition, + args->downTime, args->pointerCount, + args->pointerProperties, args->pointerCoords, 0, 0); + + needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); } // release lock @@ -3290,9 +3533,9 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { { // acquire lock std::scoped_lock _l(mLock); - DeviceResetEntry* newEntry = - new DeviceResetEntry(args->id, args->eventTime, args->deviceId); - needWake = enqueueInboundEventLocked(newEntry); + std::unique_ptr<DeviceResetEntry> newEntry = + std::make_unique<DeviceResetEntry>(args->id, args->eventTime, args->deviceId); + needWake = enqueueInboundEventLocked(std::move(newEntry)); } // release lock if (needWake) { @@ -3300,15 +3543,14 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { } } -int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid, - int32_t injectorUid, int32_t syncMode, - std::chrono::milliseconds timeout, uint32_t policyFlags) { +InputEventInjectionResult InputDispatcher::injectInputEvent( + const InputEvent* event, int32_t injectorPid, int32_t injectorUid, + InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " "syncMode=%d, timeout=%lld, policyFlags=0x%08x", event->getType(), injectorPid, injectorUid, syncMode, timeout.count(), policyFlags); #endif - nsecs_t endTime = now() + std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count(); policyFlags |= POLICY_FLAG_INJECTED; @@ -3316,13 +3558,13 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec policyFlags |= POLICY_FLAG_TRUSTED; } - std::queue<EventEntry*> injectedEntries; + std::queue<std::unique_ptr<EventEntry>> injectedEntries; switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event); int32_t action = incomingKey.getAction(); if (!validateKeyEvent(action)) { - return INPUT_EVENT_INJECTION_FAILED; + return InputEventInjectionResult::FAILED; } int32_t flags = incomingKey.getFlags(); @@ -3350,13 +3592,14 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec } mLock.lock(); - KeyEntry* injectedEntry = - new KeyEntry(incomingKey.getId(), incomingKey.getEventTime(), - VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), - incomingKey.getDisplayId(), policyFlags, action, flags, keyCode, - incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), - incomingKey.getDownTime()); - injectedEntries.push(injectedEntry); + std::unique_ptr<KeyEntry> injectedEntry = + std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(), + VIRTUAL_KEYBOARD_ID, incomingKey.getSource(), + incomingKey.getDisplayId(), policyFlags, action, + flags, keyCode, incomingKey.getScanCode(), metaState, + incomingKey.getRepeatCount(), + incomingKey.getDownTime()); + injectedEntries.push(std::move(injectedEntry)); break; } @@ -3368,7 +3611,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec int32_t actionButton = motionEvent->getActionButton(); int32_t displayId = motionEvent->getDisplayId(); if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) { - return INPUT_EVENT_INJECTION_FAILED; + return InputEventInjectionResult::FAILED; } if (!(policyFlags & POLICY_FLAG_FILTERED)) { @@ -3384,48 +3627,57 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec mLock.lock(); const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - MotionEntry* injectedEntry = - new MotionEntry(motionEvent->getId(), *sampleEventTimes, VIRTUAL_KEYBOARD_ID, - motionEvent->getSource(), motionEvent->getDisplayId(), - policyFlags, action, actionButton, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getButtonState(), - motionEvent->getClassification(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getRawXCursorPosition(), - motionEvent->getRawYCursorPosition(), - motionEvent->getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); - injectedEntries.push(injectedEntry); + std::unique_ptr<MotionEntry> injectedEntry = + std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes, + VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, + actionButton, motionEvent->getFlags(), + motionEvent->getMetaState(), + motionEvent->getButtonState(), + motionEvent->getClassification(), + motionEvent->getEdgeFlags(), + motionEvent->getXPrecision(), + motionEvent->getYPrecision(), + motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), + motionEvent->getDownTime(), + uint32_t(pointerCount), pointerProperties, + samplePointerCoords, motionEvent->getXOffset(), + motionEvent->getYOffset()); + injectedEntries.push(std::move(injectedEntry)); for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { sampleEventTimes += 1; samplePointerCoords += pointerCount; - MotionEntry* nextInjectedEntry = - new MotionEntry(motionEvent->getId(), *sampleEventTimes, - VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, action, - actionButton, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getButtonState(), - motionEvent->getClassification(), - motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), - motionEvent->getYPrecision(), - motionEvent->getRawXCursorPosition(), - motionEvent->getRawYCursorPosition(), - motionEvent->getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); - injectedEntries.push(nextInjectedEntry); + std::unique_ptr<MotionEntry> nextInjectedEntry = + std::make_unique<MotionEntry>(motionEvent->getId(), *sampleEventTimes, + VIRTUAL_KEYBOARD_ID, motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, + action, actionButton, motionEvent->getFlags(), + motionEvent->getMetaState(), + motionEvent->getButtonState(), + motionEvent->getClassification(), + motionEvent->getEdgeFlags(), + motionEvent->getXPrecision(), + motionEvent->getYPrecision(), + motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), + motionEvent->getDownTime(), + uint32_t(pointerCount), pointerProperties, + samplePointerCoords, + motionEvent->getXOffset(), + motionEvent->getYOffset()); + injectedEntries.push(std::move(nextInjectedEntry)); } break; } default: ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType())); - return INPUT_EVENT_INJECTION_FAILED; + return InputEventInjectionResult::FAILED; } InjectionState* injectionState = new InjectionState(injectorPid, injectorUid); - if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { + if (syncMode == InputEventInjectionSync::NONE) { injectionState->injectionIsAsync = true; } @@ -3434,7 +3686,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec bool needWake = false; while (!injectedEntries.empty()) { - needWake |= enqueueInboundEventLocked(injectedEntries.front()); + needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front())); injectedEntries.pop(); } @@ -3444,16 +3696,16 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec mLooper->wake(); } - int32_t injectionResult; + InputEventInjectionResult injectionResult; { // acquire lock std::unique_lock _l(mLock); - if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + if (syncMode == InputEventInjectionSync::NONE) { + injectionResult = InputEventInjectionResult::SUCCEEDED; } else { for (;;) { injectionResult = injectionState->injectionResult; - if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { + if (injectionResult != InputEventInjectionResult::PENDING) { break; } @@ -3463,15 +3715,15 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec ALOGD("injectInputEvent - Timed out waiting for injection result " "to become available."); #endif - injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + injectionResult = InputEventInjectionResult::TIMED_OUT; break; } mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout)); } - if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED && - syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { + if (injectionResult == InputEventInjectionResult::SUCCEEDED && + syncMode == InputEventInjectionSync::WAIT_FOR_FINISHED) { while (injectionState->pendingForegroundDispatches != 0) { #if DEBUG_INJECTION ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", @@ -3483,7 +3735,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec ALOGD("injectInputEvent - Timed out waiting for pending foreground " "dispatches to finish."); #endif - injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; + injectionResult = InputEventInjectionResult::TIMED_OUT; break; } @@ -3511,7 +3763,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu const KeyEvent& keyEvent = static_cast<const KeyEvent&>(event); VerifiedKeyEvent verifiedKeyEvent = verifiedKeyEventFromKeyEvent(keyEvent); result = std::make_unique<VerifiedKeyEvent>(verifiedKeyEvent); - calculatedHmac = mHmacKeyManager.sign(verifiedKeyEvent); + calculatedHmac = sign(verifiedKeyEvent); break; } case AINPUT_EVENT_TYPE_MOTION: { @@ -3519,7 +3771,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu VerifiedMotionEvent verifiedMotionEvent = verifiedMotionEventFromMotionEvent(motionEvent); result = std::make_unique<VerifiedMotionEvent>(verifiedMotionEvent); - calculatedHmac = mHmacKeyManager.sign(verifiedMotionEvent); + calculatedHmac = sign(verifiedMotionEvent); break; } default: { @@ -3541,8 +3793,9 @@ bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t inject mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); } -void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) { - InjectionState* injectionState = entry->injectionState; +void InputDispatcher::setInjectionResult(EventEntry& entry, + InputEventInjectionResult injectionResult) { + InjectionState* injectionState = entry.injectionState; if (injectionState) { #if DEBUG_INJECTION ALOGD("Setting input event injection result to %d. " @@ -3550,21 +3803,24 @@ void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionRes injectionResult, injectionState->injectorPid, injectionState->injectorUid); #endif - if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) { + if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) { // Log the outcome since the injector did not wait for the injection result. switch (injectionResult) { - case INPUT_EVENT_INJECTION_SUCCEEDED: + case InputEventInjectionResult::SUCCEEDED: ALOGV("Asynchronous input event injection succeeded."); break; - case INPUT_EVENT_INJECTION_FAILED: + case InputEventInjectionResult::FAILED: ALOGW("Asynchronous input event injection failed."); break; - case INPUT_EVENT_INJECTION_PERMISSION_DENIED: + case InputEventInjectionResult::PERMISSION_DENIED: ALOGW("Asynchronous input event injection permission denied."); break; - case INPUT_EVENT_INJECTION_TIMED_OUT: + case InputEventInjectionResult::TIMED_OUT: ALOGW("Asynchronous input event injection timed out."); break; + case InputEventInjectionResult::PENDING: + ALOGE("Setting result to 'PENDING' for asynchronous injection"); + break; } } @@ -3573,15 +3829,15 @@ void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionRes } } -void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; +void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) { + InjectionState* injectionState = entry.injectionState; if (injectionState) { injectionState->pendingForegroundDispatches += 1; } } -void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; +void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) { + InjectionState* injectionState = entry.injectionState; if (injectionState) { injectionState->pendingForegroundDispatches -= 1; @@ -3591,13 +3847,11 @@ void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) { } } -std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked( +const std::vector<sp<InputWindowHandle>>& InputDispatcher::getWindowHandlesLocked( int32_t displayId) const { - return getValueByKey(mWindowHandlesByDisplay, displayId); -} - -sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const { - return getValueByKey(mFocusedWindowHandlesByDisplay, displayId); + static const std::vector<sp<InputWindowHandle>> EMPTY_WINDOW_HANDLES; + auto it = mWindowHandlesByDisplay.find(displayId); + return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES; } sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( @@ -3607,7 +3861,7 @@ sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( } for (auto& it : mWindowHandlesByDisplay) { - const std::vector<sp<InputWindowHandle>> windowHandles = it.second; + const std::vector<sp<InputWindowHandle>>& windowHandles = it.second; for (const sp<InputWindowHandle>& windowHandle : windowHandles) { if (windowHandle->getToken() == windowHandleToken) { return windowHandle; @@ -3617,9 +3871,28 @@ sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( return nullptr; } +sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked(const sp<IBinder>& windowHandleToken, + int displayId) const { + if (windowHandleToken == nullptr) { + return nullptr; + } + + for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) { + if (windowHandle->getToken() == windowHandleToken) { + return windowHandle; + } + } + return nullptr; +} + +sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const { + sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId); + return getWindowHandleLocked(focusedToken, displayId); +} + bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const { for (auto& it : mWindowHandlesByDisplay) { - const std::vector<sp<InputWindowHandle>> windowHandles = it.second; + const std::vector<sp<InputWindowHandle>>& windowHandles = it.second; for (const sp<InputWindowHandle>& handle : windowHandles) { if (handle->getId() == windowHandle->getId() && handle->getToken() == windowHandle->getToken()) { @@ -3636,7 +3909,31 @@ bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowH return false; } -sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token) const { +bool InputDispatcher::hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const { + sp<Connection> connection = getConnectionLocked(windowHandle.getToken()); + const bool noInputChannel = + windowHandle.getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + if (connection != nullptr && noInputChannel) { + ALOGW("%s has feature NO_INPUT_CHANNEL, but it matched to connection %s", + windowHandle.getName().c_str(), connection->inputChannel->getName().c_str()); + return false; + } + + if (connection == nullptr) { + if (!noInputChannel) { + ALOGI("Could not find connection for %s", windowHandle.getName().c_str()); + } + return false; + } + if (!connection->responsive) { + ALOGW("Window %s is not responsive", windowHandle.getName().c_str()); + return false; + } + return true; +} + +std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked( + const sp<IBinder>& token) const { size_t count = mInputChannelsByToken.count(token); if (count == 0) { return nullptr; @@ -3671,10 +3968,9 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( if ((getInputChannelLocked(handle->getToken()) == nullptr && info->portalToDisplayId == ADISPLAY_ID_NONE)) { const bool noInputChannel = - info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; - const bool canReceiveInput = - !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || - !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); + info->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + const bool canReceiveInput = !info->flags.test(InputWindowInfo::Flag::NOT_TOUCHABLE) || + !info->flags.test(InputWindowInfo::Flag::NOT_FOCUSABLE); if (canReceiveInput && !noInputChannel) { ALOGV("Window handle %s has no registered input channel", handle->getName().c_str()); @@ -3731,58 +4027,46 @@ void InputDispatcher::setInputWindowsLocked( ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); } + // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL + for (const sp<InputWindowHandle>& window : inputWindowHandles) { + const bool noInputWindow = + window->getInfo()->inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + if (noInputWindow && window->getToken() != nullptr) { + ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing", + window->getName().c_str()); + window->releaseChannel(); + } + } + // Copy old handles for release if they are no longer present. const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); - sp<InputWindowHandle> newFocusedWindowHandle = nullptr; - bool foundHoveredWindow = false; - for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) { - // Set newFocusedWindowHandle to the top most focused window instead of the last one - if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && - windowHandle->getInfo()->visible) { - newFocusedWindowHandle = windowHandle; - } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; - } - } - - if (!foundHoveredWindow) { + const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId); + if (mLastHoverWindowHandle && + std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) == + windowHandles.end()) { mLastHoverWindowHandle = nullptr; } - sp<InputWindowHandle> oldFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - - if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) { - if (oldFocusedWindowHandle != nullptr) { - if (DEBUG_FOCUS) { - ALOGD("Focus left window: %s in display %" PRId32, - oldFocusedWindowHandle->getName().c_str(), displayId); - } - sp<InputChannel> focusedInputChannel = - getInputChannelLocked(oldFocusedWindowHandle->getToken()); - if (focusedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, - "focus left window"); - synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); - enqueueFocusEventLocked(*oldFocusedWindowHandle, false /*hasFocus*/); - } - mFocusedWindowHandlesByDisplay.erase(displayId); - } - if (newFocusedWindowHandle != nullptr) { - if (DEBUG_FOCUS) { - ALOGD("Focus entered window: %s in display %" PRId32, - newFocusedWindowHandle->getName().c_str(), displayId); - } - mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; - enqueueFocusEventLocked(*newFocusedWindowHandle, true /*hasFocus*/); + sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId); + if (focusedToken) { + FocusResult result = checkTokenFocusableLocked(focusedToken, displayId); + if (result != FocusResult::OK) { + onFocusChangedLocked(focusedToken, nullptr, displayId, typeToString(result)); } + } - if (mFocusedDisplayId == displayId) { - onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); + std::optional<FocusRequest> focusRequest = + getOptionalValueByKey(mPendingFocusRequests, displayId); + if (focusRequest) { + // If the window from the pending request is now visible, provide it focus. + FocusResult result = handleFocusRequestLocked(*focusRequest); + if (result != FocusResult::NOT_VISIBLE) { + // Drop the request if we were able to change the focus or we cannot change + // it for another reason. + mPendingFocusRequests.erase(displayId); } } @@ -3797,7 +4081,7 @@ void InputDispatcher::setInputWindowsLocked( ALOGD("Touched window was removed: %s in display %" PRId32, touchedWindow.windowHandle->getName().c_str(), displayId); } - sp<InputChannel> touchedInputChannel = + std::shared_ptr<InputChannel> touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, @@ -3826,7 +4110,7 @@ void InputDispatcher::setInputWindowsLocked( } void InputDispatcher::setFocusedApplication( - int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) { + int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) { if (DEBUG_FOCUS) { ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId, inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>"); @@ -3834,22 +4118,23 @@ void InputDispatcher::setFocusedApplication( { // acquire lock std::scoped_lock _l(mLock); - sp<InputApplicationHandle> oldFocusedApplicationHandle = + std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); - if (oldFocusedApplicationHandle == mAwaitedFocusedApplication && - inputApplicationHandle != oldFocusedApplicationHandle) { - resetNoFocusedWindowTimeoutLocked(); + if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) { + return; // This application is already focused. No need to wake up or change anything. } - if (inputApplicationHandle != nullptr && inputApplicationHandle->updateInfo()) { - if (oldFocusedApplicationHandle != inputApplicationHandle) { - mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; - } - } else if (oldFocusedApplicationHandle != nullptr) { - oldFocusedApplicationHandle.clear(); + // Set the new application handle. + if (inputApplicationHandle != nullptr) { + mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle; + } else { mFocusedApplicationHandlesByDisplay.erase(displayId); } + + // No matter what the old focused application was, stop waiting on it because it is + // no longer focused. + resetNoFocusedWindowTimeoutLocked(); } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -3873,11 +4158,11 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { std::scoped_lock _l(mLock); if (mFocusedDisplayId != displayId) { - sp<InputWindowHandle> oldFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); - if (oldFocusedWindowHandle != nullptr) { - sp<InputChannel> inputChannel = - getInputChannelLocked(oldFocusedWindowHandle->getToken()); + sp<IBinder> oldFocusedWindowToken = + getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId); + if (oldFocusedWindowToken != nullptr) { + std::shared_ptr<InputChannel> inputChannel = + getInputChannelLocked(oldFocusedWindowToken); if (inputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, @@ -3888,21 +4173,16 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { } mFocusedDisplayId = displayId; - // Sanity check - sp<InputWindowHandle> newFocusedWindowHandle = - getValueByKey(mFocusedWindowHandlesByDisplay, displayId); - onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); + // Find new focused window and validate + sp<IBinder> newFocusedWindowToken = + getValueByKey(mFocusedWindowTokenByDisplay, displayId); + notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken); - if (newFocusedWindowHandle == nullptr) { + if (newFocusedWindowToken == nullptr) { ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId); - if (!mFocusedWindowHandlesByDisplay.empty()) { - ALOGE("But another display has a focused window:"); - for (auto& it : mFocusedWindowHandlesByDisplay) { - const int32_t displayId = it.first; - const sp<InputWindowHandle>& windowHandle = it.second; - ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId, - windowHandle->getName().c_str()); - } + if (!mFocusedWindowTokenByDisplay.empty()) { + ALOGE("But another display has a focused window\n%s", + dumpFocusedWindowsLocked().c_str()); } } } @@ -3977,6 +4257,21 @@ void InputDispatcher::setInTouchMode(bool inTouchMode) { mInTouchMode = inTouchMode; } +void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { + if (opacity < 0 || opacity > 1) { + LOG_ALWAYS_FATAL("Maximum obscuring opacity for touch should be >= 0 and <= 1"); + return; + } + + std::scoped_lock lock(mLock); + mMaximumObscuringOpacityForTouch = opacity; +} + +void InputDispatcher::setBlockUntrustedTouchesMode(BlockUntrustedTouchesMode mode) { + std::scoped_lock lock(mLock); + mBlockUntrustedTouchesMode = mode; +} + bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { if (fromToken == toToken) { if (DEBUG_FOCUS) { @@ -4087,6 +4382,46 @@ void InputDispatcher::logDispatchStateLocked() { } } +std::string InputDispatcher::dumpFocusedWindowsLocked() { + if (mFocusedWindowTokenByDisplay.empty()) { + return INDENT "FocusedWindows: <none>\n"; + } + + std::string dump; + dump += INDENT "FocusedWindows:\n"; + for (auto& it : mFocusedWindowTokenByDisplay) { + const int32_t displayId = it.first; + const sp<InputWindowHandle> windowHandle = getFocusedWindowHandleLocked(displayId); + if (windowHandle) { + dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId, + windowHandle->getName().c_str()); + } else { + dump += StringPrintf(INDENT2 "displayId=%" PRId32 + " has focused token without a window'\n", + displayId); + } + } + return dump; +} + +std::string InputDispatcher::dumpPendingFocusRequestsLocked() { + if (mPendingFocusRequests.empty()) { + return INDENT "mPendingFocusRequests: <none>\n"; + } + + std::string dump; + dump += INDENT "mPendingFocusRequests:\n"; + for (const auto& [displayId, focusRequest] : mPendingFocusRequests) { + // Rather than printing raw values for focusRequest.token and focusRequest.focusedToken, + // try to resolve them to actual windows. + std::string windowName = getConnectionNameLocked(focusRequest.token); + std::string focusedWindowName = getConnectionNameLocked(focusRequest.focusedToken); + dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", token->%s, focusedToken->%s\n", + displayId, windowName.c_str(), focusedWindowName.c_str()); + } + return dump; +} + void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled)); dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen)); @@ -4097,30 +4432,19 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += StringPrintf(INDENT "FocusedApplications:\n"); for (auto& it : mFocusedApplicationHandlesByDisplay) { const int32_t displayId = it.first; - const sp<InputApplicationHandle>& applicationHandle = it.second; + const std::shared_ptr<InputApplicationHandle>& applicationHandle = it.second; + const std::chrono::duration timeout = + applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s', dispatchingTimeout=%" PRId64 "ms\n", - displayId, applicationHandle->getName().c_str(), - ns2ms(applicationHandle - ->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT) - .count())); + displayId, applicationHandle->getName().c_str(), millis(timeout)); } } else { dump += StringPrintf(INDENT "FocusedApplications: <none>\n"); } - if (!mFocusedWindowHandlesByDisplay.empty()) { - dump += StringPrintf(INDENT "FocusedWindows:\n"); - for (auto& it : mFocusedWindowHandlesByDisplay) { - const int32_t displayId = it.first; - const sp<InputWindowHandle>& windowHandle = it.second; - dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId, - windowHandle->getName().c_str()); - } - } else { - dump += StringPrintf(INDENT "FocusedWindows: <none>\n"); - } + dump += dumpFocusedWindowsLocked(); + dump += dumpPendingFocusRequestsLocked(); if (!mTouchStatesByDisplay.empty()) { dump += StringPrintf(INDENT "TouchStatesByDisplay:\n"); @@ -4165,29 +4489,34 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, " - "portalToDisplayId=%d, paused=%s, hasFocus=%s, " - "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, " - "flags=0x%08x, type=0x%08x, " + "portalToDisplayId=%d, paused=%s, focusable=%s, " + "hasWallpaper=%s, visible=%s, " + "flags=%s, type=0x%08x, " "frame=[%d,%d][%d,%d], globalScale=%f, " - "windowScale=(%f,%f), touchableRegion=", + "applicationInfo=%s, " + "touchableRegion=", i, windowInfo->name.c_str(), windowInfo->displayId, windowInfo->portalToDisplayId, toString(windowInfo->paused), - toString(windowInfo->hasFocus), + toString(windowInfo->focusable), toString(windowInfo->hasWallpaper), toString(windowInfo->visible), - toString(windowInfo->canReceiveKeys), - windowInfo->layoutParamsFlags, - windowInfo->layoutParamsType, windowInfo->frameLeft, - windowInfo->frameTop, windowInfo->frameRight, - windowInfo->frameBottom, windowInfo->globalScaleFactor, - windowInfo->windowXScale, windowInfo->windowYScale); - dumpRegion(dump, windowInfo->touchableRegion); - dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); + windowInfo->flags.string().c_str(), + static_cast<int32_t>(windowInfo->type), + windowInfo->frameLeft, windowInfo->frameTop, + windowInfo->frameRight, windowInfo->frameBottom, + windowInfo->globalScaleFactor, + windowInfo->applicationInfo.name.c_str()); + dump += dumpRegion(windowInfo->touchableRegion); + dump += StringPrintf(", inputFeatures=%s", + windowInfo->inputFeatures.string().c_str()); dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%" PRId64 - "ms\n", + "ms, trustedOverlay=%s, hasToken=%s\n", windowInfo->ownerPid, windowInfo->ownerUid, - ns2ms(windowInfo->dispatchingTimeout)); + millis(windowInfo->dispatchingTimeout), + toString(windowInfo->trustedOverlay), + toString(windowInfo->token != nullptr)); + windowInfo->transform.dump(dump, "transform", INDENT4); } } else { dump += INDENT2 "Windows: <none>\n"; @@ -4217,9 +4546,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { // Dump recently dispatched or dropped events from oldest to newest. if (!mRecentQueue.empty()) { dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size()); - for (EventEntry* entry : mRecentQueue) { + for (std::shared_ptr<EventEntry>& entry : mRecentQueue) { dump += INDENT2; - entry->appendDescription(dump); + dump += entry->getDescription(); dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { @@ -4230,7 +4559,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (mPendingEvent) { dump += INDENT "PendingEvent:\n"; dump += INDENT2; - mPendingEvent->appendDescription(dump); + dump += mPendingEvent->getDescription(); dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - mPendingEvent->eventTime)); } else { @@ -4240,9 +4569,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { // Dump inbound events from oldest to newest. if (!mInboundQueue.empty()) { dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size()); - for (EventEntry* entry : mInboundQueue) { + for (std::shared_ptr<EventEntry>& entry : mInboundQueue) { dump += INDENT2; - entry->appendDescription(dump); + dump += entry->getDescription(); dump += StringPrintf(", age=%" PRId64 "ms\n", ns2ms(currentTime - entry->eventTime)); } } else { @@ -4274,14 +4603,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!connection->outboundQueue.empty()) { dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", connection->outboundQueue.size()); - for (DispatchEntry* entry : connection->outboundQueue) { - dump.append(INDENT4); - entry->eventEntry->appendDescription(dump); - dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%" PRId64 - "ms\n", - entry->targetFlags, entry->resolvedAction, - ns2ms(currentTime - entry->eventEntry->eventTime)); - } + dump += dumpQueue(connection->outboundQueue, currentTime); + } else { dump += INDENT3 "OutboundQueue: <empty>\n"; } @@ -4289,15 +4612,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!connection->waitQueue.empty()) { dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n", connection->waitQueue.size()); - for (DispatchEntry* entry : connection->waitQueue) { - dump += INDENT4; - entry->eventEntry->appendDescription(dump); - dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " - "age=%" PRId64 "ms, wait=%" PRId64 "ms\n", - entry->targetFlags, entry->resolvedAction, - ns2ms(currentTime - entry->eventEntry->eventTime), - ns2ms(currentTime - entry->deliveryTime)); - } + dump += dumpQueue(connection->waitQueue, currentTime); } else { dump += INDENT3 "WaitQueue: <empty>\n"; } @@ -4323,81 +4638,82 @@ void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor> const size_t numMonitors = monitors.size(); for (size_t i = 0; i < numMonitors; i++) { const Monitor& monitor = monitors[i]; - const sp<InputChannel>& channel = monitor.inputChannel; + const std::shared_ptr<InputChannel>& channel = monitor.inputChannel; dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str()); dump += "\n"; } } -status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) { -#if DEBUG_REGISTRATION - ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str()); +base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel( + const std::string& name) { +#if DEBUG_CHANNEL_CREATION + ALOGD("channel '%s' ~ createInputChannel", name.c_str()); #endif + std::shared_ptr<InputChannel> serverChannel; + std::unique_ptr<InputChannel> clientChannel; + status_t result = openInputChannelPair(name, serverChannel, clientChannel); + + if (result) { + return base::Error(result) << "Failed to open input channel pair with name " << name; + } + { // acquire lock std::scoped_lock _l(mLock); - sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken()); - if (existingConnection != nullptr) { - ALOGW("Attempted to register already registered input channel '%s'", - inputChannel->getName().c_str()); - return BAD_VALUE; - } - - sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator); + sp<Connection> connection = new Connection(serverChannel, false /*monitor*/, mIdGenerator); - int fd = inputChannel->getFd(); + int fd = serverChannel->getFd(); mConnectionsByFd[fd] = connection; - mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel; + mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel; mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock // Wake the looper because some connections have changed. mLooper->wake(); - return OK; + return clientChannel; } -status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel, - int32_t displayId, bool isGestureMonitor) { +base::Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor( + int32_t displayId, bool isGestureMonitor, const std::string& name) { + std::shared_ptr<InputChannel> serverChannel; + std::unique_ptr<InputChannel> clientChannel; + status_t result = openInputChannelPair(name, serverChannel, clientChannel); + if (result) { + return base::Error(result) << "Failed to open input channel pair with name " << name; + } + { // acquire lock std::scoped_lock _l(mLock); if (displayId < 0) { - ALOGW("Attempted to register input monitor without a specified display."); - return BAD_VALUE; + return base::Error(BAD_VALUE) << "Attempted to create input monitor with name " << name + << " without a specified display."; } - if (inputChannel->getConnectionToken() == nullptr) { - ALOGW("Attempted to register input monitor without an identifying token."); - return BAD_VALUE; - } + sp<Connection> connection = new Connection(serverChannel, true /*monitor*/, mIdGenerator); - sp<Connection> connection = new Connection(inputChannel, true /*monitor*/, mIdGenerator); - - const int fd = inputChannel->getFd(); + const int fd = serverChannel->getFd(); mConnectionsByFd[fd] = connection; - mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel; + mInputChannelsByToken[serverChannel->getConnectionToken()] = serverChannel; auto& monitorsByDisplay = isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay; - monitorsByDisplay[displayId].emplace_back(inputChannel); + monitorsByDisplay[displayId].emplace_back(serverChannel); mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } + // Wake the looper because some connections have changed. mLooper->wake(); - return OK; + return clientChannel; } -status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) { -#if DEBUG_REGISTRATION - ALOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().c_str()); -#endif - +status_t InputDispatcher::removeInputChannel(const sp<IBinder>& connectionToken) { { // acquire lock std::scoped_lock _l(mLock); - status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/); + status_t status = removeInputChannelLocked(connectionToken, false /*notify*/); if (status) { return status; } @@ -4409,23 +4725,22 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh return OK; } -status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, - bool notify) { - sp<Connection> connection = getConnectionLocked(inputChannel->getConnectionToken()); +status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connectionToken, + bool notify) { + sp<Connection> connection = getConnectionLocked(connectionToken); if (connection == nullptr) { - ALOGW("Attempted to unregister already unregistered input channel '%s'", - inputChannel->getName().c_str()); + ALOGW("Attempted to unregister already unregistered input channel"); return BAD_VALUE; } removeConnectionLocked(connection); - mInputChannelsByToken.erase(inputChannel->getConnectionToken()); + mInputChannelsByToken.erase(connectionToken); if (connection->monitor) { - removeMonitorChannelLocked(inputChannel); + removeMonitorChannelLocked(connectionToken); } - mLooper->removeFd(inputChannel->getFd()); + mLooper->removeFd(connection->inputChannel->getFd()); nsecs_t currentTime = now(); abortBrokenDispatchCycleLocked(currentTime, connection, notify); @@ -4434,19 +4749,19 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& i return OK; } -void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) { - removeMonitorChannelLocked(inputChannel, mGlobalMonitorsByDisplay); - removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay); +void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionToken) { + removeMonitorChannelLocked(connectionToken, mGlobalMonitorsByDisplay); + removeMonitorChannelLocked(connectionToken, mGestureMonitorsByDisplay); } void InputDispatcher::removeMonitorChannelLocked( - const sp<InputChannel>& inputChannel, + const sp<IBinder>& connectionToken, std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) { for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) { std::vector<Monitor>& monitors = it->second; const size_t numMonitors = monitors.size(); for (size_t i = 0; i < numMonitors; i++) { - if (monitors[i].inputChannel == inputChannel) { + if (monitors[i].inputChannel->getConnectionToken() == connectionToken) { monitors.erase(monitors.begin() + i); break; } @@ -4497,7 +4812,8 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { options.deviceId = deviceId; options.displayId = displayId; for (const TouchedWindow& window : state.windows) { - sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken()); + std::shared_ptr<InputChannel> channel = + getInputChannelLocked(window.windowHandle->getToken()); if (channel != nullptr) { synthesizeCancelationEventsForInputChannelLocked(channel, options); } @@ -4536,6 +4852,14 @@ sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConn return nullptr; } +std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connectionToken) const { + sp<Connection> connection = getConnectionLocked(connectionToken); + if (connection == nullptr) { + return "<nullptr>"; + } + return connection->getInputChannelName(); +} + void InputDispatcher::removeConnectionLocked(const sp<Connection>& connection) { mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); removeByValue(mConnectionsByFd, connection); @@ -4564,10 +4888,8 @@ void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, postCommandLocked(std::move(commandEntry)); } -void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus, - const sp<InputWindowHandle>& newFocus) { - sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr; - sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr; +void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken, + const sp<IBinder>& newToken) { std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( &InputDispatcher::doNotifyFocusChangedLockedInterruptible); commandEntry->oldToken = oldToken; @@ -4610,7 +4932,7 @@ void InputDispatcher::onAnrLocked(const Connection& connection) { postCommandLocked(std::move(commandEntry)); } -void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) { +void InputDispatcher::onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) { std::string reason = android::base::StringPrintf("%s does not have a focused window", application->getName().c_str()); @@ -4624,14 +4946,21 @@ void InputDispatcher::onAnrLocked(const sp<InputApplicationHandle>& application) postCommandLocked(std::move(commandEntry)); } +void InputDispatcher::onUntrustedTouchLocked(const std::string& obscuringPackage) { + std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( + &InputDispatcher::doNotifyUntrustedTouchLockedInterruptible); + commandEntry->obscuringPackage = obscuringPackage; + postCommandLocked(std::move(commandEntry)); +} + void InputDispatcher::updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason) { const std::string windowLabel = getApplicationWindowLabel(nullptr, window); updateLastAnrStateLocked(windowLabel, reason); } -void InputDispatcher::updateLastAnrStateLocked(const sp<InputApplicationHandle>& application, - const std::string& reason) { +void InputDispatcher::updateLastAnrStateLocked( + const std::shared_ptr<InputApplicationHandle>& application, const std::string& reason) { const std::string windowLabel = getApplicationWindowLabel(application, nullptr); updateLastAnrStateLocked(windowLabel, reason); } @@ -4685,12 +5014,12 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) commandEntry->inputChannel ? commandEntry->inputChannel->getConnectionToken() : nullptr; mLock.unlock(); - const nsecs_t timeoutExtension = + const std::chrono::nanoseconds timeoutExtension = mPolicy->notifyAnr(commandEntry->inputApplicationHandle, token, commandEntry->reason); mLock.lock(); - if (timeoutExtension > 0) { + if (timeoutExtension > 0s) { extendAnrTimeoutsLocked(commandEntry->inputApplicationHandle, token, timeoutExtension); } else { // stop waking up for events in this connection, it is already not responding @@ -4702,12 +5031,20 @@ void InputDispatcher::doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) } } -void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application, - const sp<IBinder>& connectionToken, - nsecs_t timeoutExtension) { +void InputDispatcher::doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyUntrustedTouch(commandEntry->obscuringPackage); + + mLock.lock(); +} + +void InputDispatcher::extendAnrTimeoutsLocked( + const std::shared_ptr<InputApplicationHandle>& application, + const sp<IBinder>& connectionToken, std::chrono::nanoseconds timeoutExtension) { if (connectionToken == nullptr && application != nullptr) { // The ANR happened because there's no focused window - mNoFocusedWindowTimeoutTime = now() + timeoutExtension; + mNoFocusedWindowTimeoutTime = now() + timeoutExtension.count(); mAwaitedFocusedApplication = application; } @@ -4718,10 +5055,10 @@ void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& } ALOGI("Raised ANR, but the policy wants to keep waiting on %s for %" PRId64 "ms longer", - connection->inputChannel->getName().c_str(), ns2ms(timeoutExtension)); + connection->inputChannel->getName().c_str(), millis(timeoutExtension)); connection->responsive = true; - const nsecs_t newTimeout = now() + timeoutExtension; + const nsecs_t newTimeout = now() + timeoutExtension.count(); for (DispatchEntry* entry : connection->waitQueue) { if (newTimeout >= entry->timeoutTime) { // Already removed old entries when connection was marked unresponsive @@ -4733,8 +5070,8 @@ void InputDispatcher::extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( CommandEntry* commandEntry) { - KeyEntry* entry = commandEntry->keyEntry; - KeyEvent event = createKeyEvent(*entry); + KeyEntry& entry = *(commandEntry->keyEntry); + KeyEvent event = createKeyEvent(entry); mLock.unlock(); @@ -4742,7 +5079,7 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( sp<IBinder> token = commandEntry->inputChannel != nullptr ? commandEntry->inputChannel->getConnectionToken() : nullptr; - nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags); + nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry.policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms", std::to_string(t.duration().count()).c_str()); @@ -4751,14 +5088,13 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( mLock.lock(); if (delay < 0) { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP; + entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP; } else if (!delay) { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; } else { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER; - entry->interceptKeyWakeupTime = now() + delay; + entry.interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER; + entry.interceptKeyWakeupTime = now() + delay; } - entry->release(); } void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) { @@ -4802,11 +5138,11 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c bool restartEvent; if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) { - KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); + KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry->eventEntry)); restartEvent = afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) { - MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry); + MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry->eventEntry)); restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, handled); } else { @@ -4841,20 +5177,20 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection, DispatchEntry* dispatchEntry, - KeyEntry* keyEntry, bool handled) { - if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) { + KeyEntry& keyEntry, bool handled) { + if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) { if (!handled) { // Report the key as unhandled, since the fallback was not handled. - mReporter->reportUnhandledKey(keyEntry->id); + mReporter->reportUnhandledKey(keyEntry.id); } return false; } // Get the fallback key state. // Clear it out after dispatching the UP. - int32_t originalKeyCode = keyEntry->keyCode; + int32_t originalKeyCode = keyEntry.keyCode; int32_t fallbackKeyCode = connection->inputState.getFallbackKey(originalKeyCode); - if (keyEntry->action == AKEY_EVENT_ACTION_UP) { + if (keyEntry.action == AKEY_EVENT_ACTION_UP) { connection->inputState.removeFallbackKey(originalKeyCode); } @@ -4867,16 +5203,15 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Asking policy to cancel fallback action. " "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, - keyEntry->policyFlags); + keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); #endif - KeyEvent event = createKeyEvent(*keyEntry); + KeyEvent event = createKeyEvent(keyEntry); event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED); mLock.unlock(); mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), &event, - keyEntry->policyFlags, &event); + keyEntry.policyFlags, &event); mLock.lock(); @@ -4895,13 +5230,13 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con // If the application did not handle a non-fallback key, first check // that we are in a good state to perform unhandled key event processing // Then ask the policy what to do with it. - bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0; + bool initialDown = keyEntry.action == AKEY_EVENT_ACTION_DOWN && keyEntry.repeatCount == 0; if (fallbackKeyCode == -1 && !initialDown) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Skipping unhandled key event processing " "since this is not an initial down. " "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags); + originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); #endif return false; } @@ -4910,15 +5245,15 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Asking policy to perform fallback action. " "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags); + keyEntry.keyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); #endif - KeyEvent event = createKeyEvent(*keyEntry); + KeyEvent event = createKeyEvent(keyEntry); mLock.unlock(); bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), - &event, keyEntry->policyFlags, &event); + &event, keyEntry.policyFlags, &event); mLock.lock(); @@ -4966,7 +5301,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con fallback = false; fallbackKeyCode = AKEYCODE_UNKNOWN; - if (keyEntry->action != AKEY_EVENT_ACTION_UP) { + if (keyEntry.action != AKEY_EVENT_ACTION_UP) { connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode); } } @@ -4986,22 +5321,22 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con if (fallback) { // Restart the dispatch cycle using the fallback key. - keyEntry->eventTime = event.getEventTime(); - keyEntry->deviceId = event.getDeviceId(); - keyEntry->source = event.getSource(); - keyEntry->displayId = event.getDisplayId(); - keyEntry->flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK; - keyEntry->keyCode = fallbackKeyCode; - keyEntry->scanCode = event.getScanCode(); - keyEntry->metaState = event.getMetaState(); - keyEntry->repeatCount = event.getRepeatCount(); - keyEntry->downTime = event.getDownTime(); - keyEntry->syntheticRepeat = false; + keyEntry.eventTime = event.getEventTime(); + keyEntry.deviceId = event.getDeviceId(); + keyEntry.source = event.getSource(); + keyEntry.displayId = event.getDisplayId(); + keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK; + keyEntry.keyCode = fallbackKeyCode; + keyEntry.scanCode = event.getScanCode(); + keyEntry.metaState = event.getMetaState(); + keyEntry.repeatCount = event.getRepeatCount(); + keyEntry.downTime = event.getDownTime(); + keyEntry.syntheticRepeat = false; #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Dispatching fallback key. " "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", - originalKeyCode, fallbackKeyCode, keyEntry->metaState); + originalKeyCode, fallbackKeyCode, keyEntry.metaState); #endif return true; // restart the event } else { @@ -5010,7 +5345,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con #endif // Report the key as unhandled, since there is no fallback key. - mReporter->reportUnhandledKey(keyEntry->id); + mReporter->reportUnhandledKey(keyEntry.id); } } return false; @@ -5018,7 +5353,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection, DispatchEntry* dispatchEntry, - MotionEntry* motionEntry, bool handled) { + MotionEntry& motionEntry, bool handled) { return false; } @@ -5130,4 +5465,146 @@ bool InputDispatcher::waitForIdle() { return result == std::cv_status::no_timeout; } +/** + * Sets focus to the window identified by the token. This must be called + * after updating any input window handles. + * + * Params: + * request.token - input channel token used to identify the window that should gain focus. + * request.focusedToken - the token that the caller expects currently to be focused. If the + * specified token does not match the currently focused window, this request will be dropped. + * If the specified focused token matches the currently focused window, the call will succeed. + * Set this to "null" if this call should succeed no matter what the currently focused token is. + * request.timestamp - SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) + * when requesting the focus change. This determines which request gets + * precedence if there is a focus change request from another source such as pointer down. + */ +void InputDispatcher::setFocusedWindow(const FocusRequest& request) { + { // acquire lock + std::scoped_lock _l(mLock); + + const int32_t displayId = request.displayId; + const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId); + if (request.focusedToken && oldFocusedToken != request.focusedToken) { + ALOGD_IF(DEBUG_FOCUS, + "setFocusedWindow on display %" PRId32 + " ignored, reason: focusedToken is not focused", + displayId); + return; + } + + mPendingFocusRequests.erase(displayId); + FocusResult result = handleFocusRequestLocked(request); + if (result == FocusResult::NOT_VISIBLE) { + // The requested window is not currently visible. Wait for the window to become visible + // and then provide it focus. This is to handle situations where a user action triggers + // a new window to appear. We want to be able to queue any key events after the user + // action and deliver it to the newly focused window. In order for this to happen, we + // take focus from the currently focused window so key events can be queued. + ALOGD_IF(DEBUG_FOCUS, + "setFocusedWindow on display %" PRId32 + " pending, reason: window is not visible", + displayId); + mPendingFocusRequests[displayId] = request; + onFocusChangedLocked(oldFocusedToken, nullptr, displayId, + "setFocusedWindow_AwaitingWindowVisibility"); + } else if (result != FocusResult::OK) { + ALOGW("setFocusedWindow on display %" PRId32 " ignored, reason:%s", displayId, + typeToString(result)); + } + } // release lock + // Wake up poll loop since it may need to make new input dispatching choices. + mLooper->wake(); +} + +InputDispatcher::FocusResult InputDispatcher::handleFocusRequestLocked( + const FocusRequest& request) { + const int32_t displayId = request.displayId; + const sp<IBinder> newFocusedToken = request.token; + const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId); + + if (oldFocusedToken == request.token) { + ALOGD_IF(DEBUG_FOCUS, + "setFocusedWindow on display %" PRId32 " ignored, reason: already focused", + displayId); + return FocusResult::OK; + } + + FocusResult result = checkTokenFocusableLocked(newFocusedToken, displayId); + if (result != FocusResult::OK) { + return result; + } + + std::string_view reason = + (request.focusedToken) ? "setFocusedWindow_FocusCheck" : "setFocusedWindow"; + onFocusChangedLocked(oldFocusedToken, newFocusedToken, displayId, reason); + return FocusResult::OK; +} + +void InputDispatcher::onFocusChangedLocked(const sp<IBinder>& oldFocusedToken, + const sp<IBinder>& newFocusedToken, int32_t displayId, + std::string_view reason) { + if (oldFocusedToken) { + std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedToken); + if (focusedInputChannel) { + CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + "focus left window"); + synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); + enqueueFocusEventLocked(oldFocusedToken, false /*hasFocus*/, reason); + } + mFocusedWindowTokenByDisplay.erase(displayId); + } + if (newFocusedToken) { + mFocusedWindowTokenByDisplay[displayId] = newFocusedToken; + enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason); + } + + if (mFocusedDisplayId == displayId) { + notifyFocusChangedLocked(oldFocusedToken, newFocusedToken); + } +} + +/** + * Checks if the window token can be focused on a display. The token can be focused if there is + * at least one window handle that is visible with the same token and all window handles with the + * same token are focusable. + * + * In the case of mirroring, two windows may share the same window token and their visibility + * might be different. Example, the mirrored window can cover the window its mirroring. However, + * we expect the focusability of the windows to match since its hard to reason why one window can + * receive focus events and the other cannot when both are backed by the same input channel. + */ +InputDispatcher::FocusResult InputDispatcher::checkTokenFocusableLocked(const sp<IBinder>& token, + int32_t displayId) const { + bool allWindowsAreFocusable = true; + bool visibleWindowFound = false; + bool windowFound = false; + for (const sp<InputWindowHandle>& window : getWindowHandlesLocked(displayId)) { + if (window->getToken() != token) { + continue; + } + windowFound = true; + if (window->getInfo()->visible) { + // Check if at least a single window is visible. + visibleWindowFound = true; + } + if (!window->getInfo()->focusable) { + // Check if all windows with the window token are focusable. + allWindowsAreFocusable = false; + break; + } + } + + if (!windowFound) { + return FocusResult::NO_WINDOW; + } + if (!allWindowsAreFocusable) { + return FocusResult::NOT_FOCUSABLE; + } + if (!visibleWindowFound) { + return FocusResult::NOT_VISIBLE; + } + + return FocusResult::OK; +} } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 31fad1f991..5387c402d6 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -31,6 +31,7 @@ #include "TouchState.h" #include "TouchedWindow.h" +#include <attestation/HmacKeyManager.h> #include <input/Input.h> #include <input/InputApplication.h> #include <input/InputTransport.h> @@ -49,6 +50,7 @@ #include <deque> #include <optional> #include <unordered_map> +#include <unordered_set> #include <InputListener.h> #include <InputReporterInterface.h> @@ -57,16 +59,6 @@ namespace android::inputdispatcher { class Connection; -class HmacKeyManager { -public: - HmacKeyManager(); - std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const; - -private: - std::array<uint8_t, 32> sign(const uint8_t* data, size_t size) const; - const std::array<uint8_t, 128> mHmacKey; -}; - /* Dispatches events to input targets. Some functions of the input dispatcher, such as * identifying input targets, are controlled by a separate policy object. * @@ -103,10 +95,10 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; - virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, - int32_t injectorUid, int32_t syncMode, - std::chrono::milliseconds timeout, - uint32_t policyFlags) override; + virtual android::os::InputEventInjectionResult injectInputEvent( + const InputEvent* event, int32_t injectorPid, int32_t injectorUid, + android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, + uint32_t policyFlags) override; virtual std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override; @@ -114,21 +106,28 @@ public: const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) override; virtual void setFocusedApplication( - int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override; + int32_t displayId, + const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override; virtual void setFocusedDisplay(int32_t displayId) override; virtual void setInputDispatchMode(bool enabled, bool frozen) override; virtual void setInputFilterEnabled(bool enabled) override; virtual void setInTouchMode(bool inTouchMode) override; + virtual void setMaximumObscuringOpacityForTouch(float opacity) override; + virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override; virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) override; - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) override; - virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId, - bool isGestureMonitor) override; - virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override; + virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel( + const std::string& name) override; + virtual void setFocusedWindow(const FocusRequest&) override; + virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor( + int32_t displayId, bool isGestureMonitor, const std::string& name) override; + virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) override; virtual status_t pilferPointers(const sp<IBinder>& token) override; + std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const; + private: enum class DropReason { NOT_DROPPED, @@ -139,6 +138,14 @@ private: STALE, }; + enum class FocusResult { + OK, + NO_WINDOW, + NOT_FOCUSABLE, + NOT_VISIBLE, + }; + static const char* typeToString(FocusResult result); + std::unique_ptr<InputThread> mThread; sp<InputDispatcherPolicyInterface> mPolicy; @@ -151,9 +158,9 @@ private: sp<Looper> mLooper; - EventEntry* mPendingEvent GUARDED_BY(mLock); - std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock); - std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock); + std::shared_ptr<EventEntry> mPendingEvent GUARDED_BY(mLock); + std::deque<std::shared_ptr<EventEntry>> mInboundQueue GUARDED_BY(mLock); + std::deque<std::shared_ptr<EventEntry>> mRecentQueue GUARDED_BY(mLock); std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock); DropReason mLastDropReason GUARDED_BY(mLock); @@ -168,16 +175,17 @@ private: void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. - bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock); + bool enqueueInboundEventLocked(std::unique_ptr<EventEntry> entry) REQUIRES(mLock); // Cleans up input state when dropping an inbound event. void dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) REQUIRES(mLock); // Enqueues a focus event. - void enqueueFocusEventLocked(const InputWindowHandle& window, bool hasFocus) REQUIRES(mLock); + void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus, + std::string_view reason) REQUIRES(mLock); // Adds an event to a queue of recent events for debugging purposes. - void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); + void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock); // App switch latency optimization. bool mAppSwitchSawKeyDown GUARDED_BY(mLock); @@ -189,7 +197,7 @@ private: // Blocked event latency optimization. Drops old events when the user intends // to transfer focus to a new application. - EventEntry* mNextUnblockedEvent GUARDED_BY(mLock); + std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock); sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, TouchState* touchState, @@ -202,6 +210,8 @@ private: sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const REQUIRES(mLock); + std::string getConnectionNameLocked(const sp<IBinder>& connectionToken) const REQUIRES(mLock); + void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock); struct IBinderHash { @@ -209,8 +219,8 @@ private: return std::hash<IBinder*>{}(b.get()); } }; - std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken - GUARDED_BY(mLock); + std::unordered_map<sp<IBinder>, std::shared_ptr<InputChannel>, IBinderHash> + mInputChannelsByToken GUARDED_BY(mLock); // Finds the display ID of the gesture monitor identified by the provided token. std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token) @@ -234,20 +244,21 @@ private: // Event injection and synchronization. std::condition_variable mInjectionResultAvailable; bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); - void setInjectionResult(EventEntry* entry, int32_t injectionResult); + void setInjectionResult(EventEntry& entry, + android::os::InputEventInjectionResult injectionResult); std::condition_variable mInjectionSyncFinished; - void incrementPendingForegroundDispatches(EventEntry* entry); - void decrementPendingForegroundDispatches(EventEntry* entry); + void incrementPendingForegroundDispatches(EventEntry& entry); + void decrementPendingForegroundDispatches(EventEntry& entry); // Key repeat tracking. struct KeyRepeatState { - KeyEntry* lastKeyEntry; // or null if no repeat + std::shared_ptr<KeyEntry> lastKeyEntry; // or null if no repeat nsecs_t nextRepeatTime; } mKeyRepeatState GUARDED_BY(mLock); void resetKeyRepeatLocked() REQUIRES(mLock); - KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock); + std::shared_ptr<KeyEntry> synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock); // Key replacement tracking struct KeyReplacement { @@ -274,7 +285,7 @@ private: void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock); nsecs_t processAnrsLocked() REQUIRES(mLock); - nsecs_t getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock); + std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock); // Input filter processing. bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); @@ -283,26 +294,38 @@ private: // Inbound event processing. void drainInboundQueueLocked() REQUIRES(mLock); void releasePendingEventLocked() REQUIRES(mLock); - void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock); + void releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock); // Dispatch state. bool mDispatchEnabled GUARDED_BY(mLock); bool mDispatchFrozen GUARDED_BY(mLock); bool mInputFilterEnabled GUARDED_BY(mLock); bool mInTouchMode GUARDED_BY(mLock); + float mMaximumObscuringOpacityForTouch GUARDED_BY(mLock); + android::os::BlockUntrustedTouchesMode mBlockUntrustedTouchesMode GUARDED_BY(mLock); std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay GUARDED_BY(mLock); void setInputWindowsLocked(const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) REQUIRES(mLock); - // Get window handles by display, return an empty vector if not found. - std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const + // Get a reference to window handles by display, return an empty vector if not found. + const std::vector<sp<InputWindowHandle>>& getWindowHandlesLocked(int32_t displayId) const REQUIRES(mLock); sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const REQUIRES(mLock); + + // Same function as above, but faster. Since displayId is provided, this avoids the need + // to loop through all displays. + sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken, + int displayId) const REQUIRES(mLock); + std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const + REQUIRES(mLock); sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock); - sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock); bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); + bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock); + FocusResult handleFocusRequestLocked(const FocusRequest&) REQUIRES(mLock); + FocusResult checkTokenFocusableLocked(const sp<IBinder>& token, int32_t displayId) const + REQUIRES(mLock); /* * Validate and update InputWindowHandles for a given display. @@ -311,15 +334,17 @@ private: const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) REQUIRES(mLock); - // Focus tracking for keys, trackball, etc. - std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay - GUARDED_BY(mLock); + // Focus tracking for keys, trackball, etc. A window token can be associated with one or more + // InputWindowHandles. If a window is mirrored, the window and its mirror will share the same + // token. Focus is tracked by the token per display and the events are dispatched to the + // channel associated by this token. + std::unordered_map<int32_t, sp<IBinder>> mFocusedWindowTokenByDisplay GUARDED_BY(mLock); std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock); // Focused applications. - std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay - GUARDED_BY(mLock); + std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>> + mFocusedApplicationHandlesByDisplay GUARDED_BY(mLock); // Top focused display. int32_t mFocusedDisplayId GUARDED_BY(mLock); @@ -327,16 +352,23 @@ private: // Dispatcher state at time of last ANR. std::string mLastAnrState GUARDED_BY(mLock); + // The connection tokens of the channels that the user last interacted, for debugging + std::unordered_set<sp<IBinder>, IBinderHash> mInteractionConnectionTokens GUARDED_BY(mLock); + void updateInteractionTokensLocked(const EventEntry& entry, + const std::vector<InputTarget>& targets) REQUIRES(mLock); + // Dispatch inbound events. - bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry) + bool dispatchConfigurationChangedLocked(nsecs_t currentTime, + const ConfigurationChangedEntry& entry) REQUIRES(mLock); + bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry) REQUIRES(mLock); - bool dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock); - bool dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, - nsecs_t* nextWakeupTime) REQUIRES(mLock); - bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, - nsecs_t* nextWakeupTime) REQUIRES(mLock); - void dispatchFocusLocked(nsecs_t currentTime, FocusEntry* entry) REQUIRES(mLock); - void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, + bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<KeyEntry> entry, + DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry, + DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) + REQUIRES(mLock); + void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<EventEntry> entry, const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); @@ -366,22 +398,30 @@ private: * The focused application at the time when no focused window was present. * Used to raise an ANR when we have no focused window. */ - sp<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock); + std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock); /** * The displayId that the focused application is associated with. */ int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock); void processNoFocusedWindowAnrLocked() REQUIRES(mLock); + /** + * This map will store the pending focus requests that cannot be currently processed. This can + * happen if the window requested to be focused is not currently visible. Such a window might + * become visible later, and these requests would be processed at that time. + */ + std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests + GUARDED_BY(mLock); + // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next. // AnrTracker must be kept in-sync with all responsive connection.waitQueues. // If a connection is not responsive, then the entries should not be added to the AnrTracker. // Once a connection becomes unresponsive, its entries are removed from AnrTracker to // prevent unneeded wakeups. AnrTracker mAnrTracker GUARDED_BY(mLock); - void extendAnrTimeoutsLocked(const sp<InputApplicationHandle>& application, - const sp<IBinder>& connectionToken, nsecs_t timeoutExtension) - REQUIRES(mLock); + void extendAnrTimeoutsLocked(const std::shared_ptr<InputApplicationHandle>& application, + const sp<IBinder>& connectionToken, + std::chrono::nanoseconds timeoutExtension) REQUIRES(mLock); // Contains the last window which received a hover event. sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock); @@ -396,13 +436,12 @@ private: void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock); int32_t getTargetDisplayId(const EventEntry& entry); - int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry& entry, - std::vector<InputTarget>& inputTargets, - nsecs_t* nextWakeupTime) REQUIRES(mLock); - int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry& entry, - std::vector<InputTarget>& inputTargets, - nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions) REQUIRES(mLock); + android::os::InputEventInjectionResult findFocusedWindowTargetsLocked( + nsecs_t currentTime, const EventEntry& entry, std::vector<InputTarget>& inputTargets, + nsecs_t* nextWakeupTime) REQUIRES(mLock); + android::os::InputEventInjectionResult findTouchedWindowTargetsLocked( + nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets, + nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) REQUIRES(mLock); std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked( int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) const REQUIRES(mLock); @@ -420,23 +459,37 @@ private: void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock); bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, const InjectionState* injectionState); + + struct TouchOcclusionInfo { + bool hasBlockingOcclusion; + float obscuringOpacity; + std::string obscuringPackage; + int32_t obscuringUid; + std::vector<std::string> debugInfo; + }; + + TouchOcclusionInfo computeTouchOcclusionInfoLocked(const sp<InputWindowHandle>& windowHandle, + int32_t x, int32_t y) const REQUIRES(mLock); + bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock); bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const REQUIRES(mLock); bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); - std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle, - const sp<InputWindowHandle>& windowHandle); + std::string dumpWindowForTouchOcclusion(const InputWindowInfo* info, bool isTouchWindow) const; + std::string getApplicationWindowLabel( + const std::shared_ptr<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle); // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work // with the mutex held makes it easier to ensure that connection invariants are maintained. // If needed, the methods post commands to run later once the critical bits are done. void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - EventEntry* eventEntry, const InputTarget& inputTarget) + std::shared_ptr<EventEntry>, const InputTarget& inputTarget) REQUIRES(mLock); void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, - EventEntry* eventEntry, const InputTarget& inputTarget) + std::shared_ptr<EventEntry>, const InputTarget& inputTarget) REQUIRES(mLock); - void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, + void enqueueDispatchEntryLocked(const sp<Connection>& connection, std::shared_ptr<EventEntry>, const InputTarget& inputTarget, int32_t dispatchMode) REQUIRES(mLock); void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) @@ -459,8 +512,8 @@ private: void synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options, std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock); - void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel, - const CancelationOptions& options) + void synthesizeCancelationEventsForInputChannelLocked( + const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) REQUIRES(mLock); void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection, const CancelationOptions& options) @@ -470,7 +523,8 @@ private: REQUIRES(mLock); // Splitting motion events across windows. - MotionEntry* splitMotionEvent(const MotionEntry& originalMotionEntry, BitSet32 pointerIds); + std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry, + BitSet32 pointerIds); // Reset and drop everything the dispatcher is doing. void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock); @@ -479,13 +533,15 @@ private: void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock); void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors); void logDispatchStateLocked() REQUIRES(mLock); + std::string dumpFocusedWindowsLocked() REQUIRES(mLock); + std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock); // Registration. - void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); + void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock); void removeMonitorChannelLocked( - const sp<InputChannel>& inputChannel, + const sp<IBinder>& connectionToken, std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock); - status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify) + status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify) REQUIRES(mLock); // Interesting events that we might like to log or tell the framework about. @@ -493,13 +549,16 @@ private: uint32_t seq, bool handled) REQUIRES(mLock); void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); - void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus, - const sp<InputWindowHandle>& newFocus) REQUIRES(mLock); + void onFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus, + int32_t displayId, std::string_view reason) REQUIRES(mLock); + void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus) + REQUIRES(mLock); void onAnrLocked(const Connection& connection) REQUIRES(mLock); - void onAnrLocked(const sp<InputApplicationHandle>& application) REQUIRES(mLock); + void onAnrLocked(const std::shared_ptr<InputApplicationHandle>& application) REQUIRES(mLock); + void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock); void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason) REQUIRES(mLock); - void updateLastAnrStateLocked(const sp<InputApplicationHandle>& application, + void updateLastAnrStateLocked(const std::shared_ptr<InputApplicationHandle>& application, const std::string& reason) REQUIRES(mLock); void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason) REQUIRES(mLock); @@ -510,14 +569,15 @@ private: void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doNotifyAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); bool afterKeyEventLockedInterruptible(const sp<Connection>& connection, - DispatchEntry* dispatchEntry, KeyEntry* keyEntry, + DispatchEntry* dispatchEntry, KeyEntry& keyEntry, bool handled) REQUIRES(mLock); bool afterMotionEventLockedInterruptible(const sp<Connection>& connection, - DispatchEntry* dispatchEntry, MotionEntry* motionEntry, + DispatchEntry* dispatchEntry, MotionEntry& motionEntry, bool handled) REQUIRES(mLock); void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); KeyEvent createKeyEvent(const KeyEntry& entry); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 386056d9b2..1656a21b41 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -265,17 +265,18 @@ void InputState::MotionMemento::mergePointerStateTo(MotionMemento& other) const } } -std::vector<EventEntry*> InputState::synthesizeCancelationEvents( +std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents( nsecs_t currentTime, const CancelationOptions& options) { - std::vector<EventEntry*> events; + std::vector<std::unique_ptr<EventEntry>> events; for (KeyMemento& memento : mKeyMementos) { if (shouldCancelKey(memento, options)) { - events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, - memento.source, memento.displayId, memento.policyFlags, - AKEY_EVENT_ACTION_UP, - memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, - memento.scanCode, memento.metaState, 0 /*repeatCount*/, - memento.downTime)); + events.push_back( + std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId, + memento.source, memento.displayId, + memento.policyFlags, AKEY_EVENT_ACTION_UP, + memento.flags | AKEY_EVENT_FLAG_CANCELED, + memento.keyCode, memento.scanCode, memento.metaState, + 0 /*repeatCount*/, memento.downTime)); } } @@ -283,22 +284,26 @@ std::vector<EventEntry*> InputState::synthesizeCancelationEvents( if (shouldCancelMotion(memento, options)) { const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, - memento.source, memento.displayId, memento.policyFlags, - action, 0 /*actionButton*/, memento.flags, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, - memento.yPrecision, memento.xCursorPosition, - memento.yCursorPosition, memento.downTime, - memento.pointerCount, memento.pointerProperties, - memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/)); + events.push_back( + std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, + memento.deviceId, memento.source, + memento.displayId, memento.policyFlags, action, + 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, + memento.pointerCount, memento.pointerProperties, + memento.pointerCoords, 0 /*xOffset*/, + 0 /*yOffset*/)); } } return events; } -std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) { - std::vector<EventEntry*> events; +std::vector<std::unique_ptr<EventEntry>> InputState::synthesizePointerDownEvents( + nsecs_t currentTime) { + std::vector<std::unique_ptr<EventEntry>> events; for (MotionMemento& memento : mMotionMementos) { if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) { continue; @@ -333,15 +338,17 @@ std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t current : AMOTION_EVENT_ACTION_POINTER_DOWN | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId, - memento.source, memento.displayId, memento.policyFlags, - action, 0 /*actionButton*/, memento.flags, AMETA_NONE, - 0 /*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, - memento.yPrecision, memento.xCursorPosition, - memento.yCursorPosition, memento.downTime, - pointerCount, pointerProperties, pointerCoords, - 0 /*xOffset*/, 0 /*yOffset*/)); + events.push_back( + std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, + memento.deviceId, memento.source, + memento.displayId, memento.policyFlags, action, + 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, + pointerCount, pointerProperties, pointerCoords, + 0 /*xOffset*/, 0 /*yOffset*/)); } memento.firstNewPointerIdx = INVALID_POINTER_INDEX; diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index d97a664d74..74ae21f76a 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -51,11 +51,11 @@ public: bool trackMotion(const MotionEntry& entry, int32_t action, int32_t flags); // Synthesizes cancelation events for the current state and resets the tracked state. - std::vector<EventEntry*> synthesizeCancelationEvents(nsecs_t currentTime, - const CancelationOptions& options); + std::vector<std::unique_ptr<EventEntry>> synthesizeCancelationEvents( + nsecs_t currentTime, const CancelationOptions& options); // Synthesizes down events for the current state. - std::vector<EventEntry*> synthesizePointerDownEvents(nsecs_t currentTime); + std::vector<std::unique_ptr<EventEntry>> synthesizePointerDownEvents(nsecs_t currentTime); // Clears the current state. void clear(); diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index 0588374292..d39113b286 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -42,12 +42,11 @@ std::string dispatchModeToString(int32_t dispatchMode) { return StringPrintf("%" PRId32, dispatchMode); } -void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset, - float windowXScale, float windowYScale) { +void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) { // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors // and non splittable windows since we will just use all the pointers from the input event. if (newPointerIds.isEmpty()) { - setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale); + setDefaultPointerTransform(transform); return; } @@ -57,47 +56,38 @@ void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffs pointerIds |= newPointerIds; while (!newPointerIds.isEmpty()) { int32_t pointerId = newPointerIds.clearFirstMarkedBit(); - pointerInfos[pointerId].xOffset = xOffset; - pointerInfos[pointerId].yOffset = yOffset; - pointerInfos[pointerId].windowXScale = windowXScale; - pointerInfos[pointerId].windowYScale = windowYScale; + pointerTransforms[pointerId] = transform; } } -void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, - float windowYScale) { +void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) { pointerIds.clear(); - pointerInfos[0].xOffset = xOffset; - pointerInfos[0].yOffset = yOffset; - pointerInfos[0].windowXScale = windowXScale; - pointerInfos[0].windowYScale = windowYScale; + pointerTransforms[0] = transform; } -bool InputTarget::useDefaultPointerInfo() const { +bool InputTarget::useDefaultPointerTransform() const { return pointerIds.isEmpty(); } -const PointerInfo& InputTarget::getDefaultPointerInfo() const { - return pointerInfos[0]; +const ui::Transform& InputTarget::getDefaultPointerTransform() const { + return pointerTransforms[0]; } std::string InputTarget::getPointerInfoString() const { - if (useDefaultPointerInfo()) { - const PointerInfo& pointerInfo = getDefaultPointerInfo(); - return StringPrintf("xOffset=%.1f, yOffset=%.1f windowScaleFactor=(%.1f, %.1f)", - pointerInfo.xOffset, pointerInfo.yOffset, pointerInfo.windowXScale, - pointerInfo.windowYScale); + std::string out = "\n"; + if (useDefaultPointerTransform()) { + const ui::Transform& transform = getDefaultPointerTransform(); + transform.dump(out, "default", " "); + return out; } - std::string out; for (uint32_t i = pointerIds.firstMarkedBit(); i <= pointerIds.lastMarkedBit(); i++) { if (!pointerIds.hasBit(i)) { continue; } - out += StringPrintf("\n pointerId %d: xOffset=%.1f, yOffset=%.1f " - "windowScaleFactor=(%.1f, %.1f)", - i, pointerInfos[i].xOffset, pointerInfos[i].yOffset, - pointerInfos[i].windowXScale, pointerInfos[i].windowYScale); + + const std::string name = "pointerId " + std::to_string(i) + ":"; + pointerTransforms[i].dump(out, name.c_str(), " "); } return out; } diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 499a75fdac..debf8058b3 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -18,28 +18,13 @@ #define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H #include <input/InputTransport.h> +#include <ui/Transform.h> #include <utils/BitSet.h> #include <utils/RefBase.h> namespace android::inputdispatcher { /* - * Information about each pointer for an InputTarget. This includes offset and scale so - * all pointers can be normalized to a single offset and scale. - * - * These values are ignored for KeyEvents - */ -struct PointerInfo { - // The x and y offset to add to a MotionEvent as it is delivered. - float xOffset = 0.0f; - float yOffset = 0.0f; - - // Scaling factor to apply to MotionEvent as it is delivered. - float windowXScale = 1.0f; - float windowYScale = 1.0f; -}; - -/* * An input target specifies how an input event is to be dispatched to a particular window * including the window's input channel, control flags, a timeout, and an X / Y offset to * be added to input event coordinates to compensate for the absolute position of the @@ -106,7 +91,7 @@ struct InputTarget { }; // The input channel to be targeted. - sp<InputChannel> inputChannel; + std::shared_ptr<InputChannel> inputChannel; // Flags for the input target. int32_t flags = 0; @@ -119,13 +104,11 @@ struct InputTarget { // if FLAG_SPLIT is set. BitSet32 pointerIds; // The data is stored by the pointerId. Use the bit position of pointerIds to look up - // PointerInfo per pointerId. - PointerInfo pointerInfos[MAX_POINTERS]; + // Transform per pointerId. + ui::Transform pointerTransforms[MAX_POINTERS]; - void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale, - float windowYScale); - void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale, - float windowYScale); + void addPointers(BitSet32 pointerIds, const ui::Transform& transform); + void setDefaultPointerTransform(const ui::Transform& transform); /** * Returns whether the default pointer information should be used. This will be true when the @@ -133,13 +116,13 @@ struct InputTarget { * and non splittable windows since we want all pointers for the EventEntry to go to this * target. */ - bool useDefaultPointerInfo() const; + bool useDefaultPointerTransform() const; /** - * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is + * Returns the default Transform object. This should be used when useDefaultPointerTransform is * true. */ - const PointerInfo& getDefaultPointerInfo() const; + const ui::Transform& getDefaultPointerTransform() const; std::string getPointerInfoString() const; }; diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp index 289b0848bf..b34767459f 100644 --- a/services/inputflinger/dispatcher/Monitor.cpp +++ b/services/inputflinger/dispatcher/Monitor.cpp @@ -19,7 +19,7 @@ namespace android::inputdispatcher { // --- Monitor --- -Monitor::Monitor(const sp<InputChannel>& inputChannel) : inputChannel(inputChannel) {} +Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel) : inputChannel(inputChannel) {} // --- TouchedMonitor --- TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset) diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h index b67c9eb507..fc0b0200c6 100644 --- a/services/inputflinger/dispatcher/Monitor.h +++ b/services/inputflinger/dispatcher/Monitor.h @@ -22,9 +22,9 @@ namespace android::inputdispatcher { struct Monitor { - sp<InputChannel> inputChannel; // never null + std::shared_ptr<InputChannel> inputChannel; // never null - explicit Monitor(const sp<InputChannel>& inputChannel); + explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel); }; // For tracking the offsets we need to apply when adding gesture monitor targets. diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 2baceba582..81b3cf025b 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -137,8 +137,7 @@ bool TouchState::isSlippery() const { for (const TouchedWindow& window : windows) { if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { if (haveSlipperyForegroundWindow || - !(window.windowHandle->getInfo()->layoutParamsFlags & - InputWindowInfo::FLAG_SLIPPERY)) { + !window.windowHandle->getInfo()->flags.test(InputWindowInfo::Flag::SLIPPERY)) { return false; } haveSlipperyForegroundWindow = true; diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 9b002f437c..9154d481ae 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -18,35 +18,19 @@ #define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H #include <InputListener.h> -#include <input/ISetInputWindowsListener.h> +#include <android-base/result.h> +#include <android/FocusRequest.h> +#include <android/os/BlockUntrustedTouchesMode.h> +#include <android/os/ISetInputWindowsListener.h> +#include <android/os/InputEventInjectionResult.h> +#include <android/os/InputEventInjectionSync.h> +#include <input/InputApplication.h> +#include <input/InputTransport.h> +#include <input/InputWindow.h> #include <unordered_map> -namespace android { - -class InputApplicationHandle; -class InputChannel; -class InputWindowHandle; - -/* - * Constants used to report the outcome of input event injection. - */ -enum { - /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ - INPUT_EVENT_INJECTION_PENDING = -1, - - /* Injection succeeded. */ - INPUT_EVENT_INJECTION_SUCCEEDED = 0, - /* Injection failed because the injector did not have permission to inject - * into the application with input focus. */ - INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, - - /* Injection failed because there were no available input targets. */ - INPUT_EVENT_INJECTION_FAILED = 2, - - /* Injection failed due to a timeout. */ - INPUT_EVENT_INJECTION_TIMED_OUT = 3 -}; +namespace android { /* Notifies the system about input events generated by the input reader. * The dispatcher is expected to be mostly asynchronous. */ @@ -89,9 +73,10 @@ public: * * This method may be called on any thread (usually by the input manager). */ - virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, - int32_t injectorUid, int32_t syncMode, - std::chrono::milliseconds timeout, uint32_t policyFlags) = 0; + virtual android::os::InputEventInjectionResult injectInputEvent( + const InputEvent* event, int32_t injectorPid, int32_t injectorUid, + android::os::InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, + uint32_t policyFlags) = 0; /* * Check whether InputEvent actually happened by checking the signature of the event. @@ -113,7 +98,8 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void setFocusedApplication( - int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0; + int32_t displayId, + const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0; /* Sets the focused display. * @@ -143,19 +129,42 @@ public: */ virtual void setInTouchMode(bool inTouchMode) = 0; + /** + * Sets the maximum allowed obscuring opacity by UID to propagate touches. + * For certain window types (eg. SAWs), the decision of honoring + * FLAG_NOT_TOUCHABLE or not depends on the combined obscuring opacity of + * the windows above the touch-consuming window. + */ + virtual void setMaximumObscuringOpacityForTouch(float opacity) = 0; + + /** + * Sets the mode of the block untrusted touches feature. + * + * TODO(b/169067926): Clean-up feature modes. + */ + virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) = 0; + /* Transfers touch focus from one window to another window. * * Returns true on success. False if the window did not actually have touch focus. */ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0; - /* Registers input channels that may be used as targets for input events. + /** + * Sets focus on the specified window. + */ + virtual void setFocusedWindow(const FocusRequest&) = 0; + + /** + * Creates an input channel that may be used as targets for input events. * * This method may be called on any thread (usually by the input manager). */ - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0; + virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel( + const std::string& name) = 0; - /* Registers input channels to be used to monitor input events. + /** + * Creates an input channel to be used to monitor input events. * * Each monitor must target a specific display and will only receive input events sent to that * display. If the monitor is a gesture monitor, it will only receive pointer events on the @@ -163,14 +172,14 @@ public: * * This method may be called on any thread (usually by the input manager). */ - virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId, - bool gestureMonitor) = 0; + virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor( + int32_t displayId, bool gestureMonitor, const std::string& name) = 0; - /* Unregister input channels that will no longer receive input events. + /* Removes input channels that will no longer receive input events. * * This method may be called on any thread (usually by the input manager). */ - virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; + virtual status_t removeInputChannel(const sp<IBinder>& connectionToken) = 0; /* Allows an input monitor steal the current pointer stream away from normal input windows. * diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 667af9bbd8..463c5f15e3 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -21,11 +21,11 @@ #include <binder/IBinder.h> #include <input/Input.h> +#include <input/InputApplication.h> #include <utils/RefBase.h> namespace android { -class InputApplicationHandle; /* * Input dispatcher policy interface. @@ -47,13 +47,17 @@ public: /* Notifies the system that an application is not responding. * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ - virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle, - const sp<IBinder>& token, const std::string& reason) = 0; + virtual std::chrono::nanoseconds notifyAnr( + const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, + const sp<IBinder>& token, const std::string& reason) = 0; /* Notifies the system that an input channel is unrecoverably broken. */ virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0; virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0; + /* Notifies the system that an untrusted touch occurred. */ + virtual void notifyUntrustedTouch(const std::string& obscuringPackage) = 0; + /* Gets the input dispatcher configuration. */ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; diff --git a/services/inputflinger/docs/anr.md b/services/inputflinger/docs/anr.md new file mode 100644 index 0000000000..ce64fe9224 --- /dev/null +++ b/services/inputflinger/docs/anr.md @@ -0,0 +1,73 @@ +# ANR detection in InputDispatcher # + +'ANR' means 'application not responding'. This is an event that gets triggered when the system thinks that an application is too slow to respond. A dialog may pop up because of an ANR event. ANRs can be triggered by multiple systems within Android. + +In InputDispatcher, ANRs are raised in 2 cases: + +1. An event was sent to a connection, and the response was not received within a certain timeout. +2. The application did not have a focused window, and an input event that requires focus was generated by the user. + +Let's consider each of these cases. + +## 1. Application does not respond to an input event that was sent to it. ## + +The most common case is when an application does not respond to input that dispatcher sent to it. Typically, it means an application is performing a long operation on its UI thread. + +When the event is being dispatched to an application, the normal flow is: `mPendingEvent` → `connection.outboundQueue` → `connection.waitQueue`. + +Every dispatch cycle, InputDispatcher will check all connections to see if any are unresponsive. To determine whether an app is not responding, we look at the oldest entry in the `waitQueue`. If the entry sits in the `waitQueue` past `entry.timeoutTime`, we trigger an ANR. + + +### Checking if a connection is unresponsive ### + +When a dispatch entry is sent to the app, its `deliveryTime` and `timeoutTime` fields are populated. The `deliveryTime` is the time that the event is delivered to the app. This is simply the current time inside `publishMotionEvent`. The `timeoutTime` is the time when this entry would be considered overdue. At that time, the ANR process would start for this connection. + +Most connections are associated with a window, and each window may have a custom timeout time. To calculate the timeout time of a specific event, simply add the `window.dispatchingTimeout` to the current time. In case where there is no associated window, such as gesture monitors, use the default dispatching timeout which is defined in `IInputConstants::DEFAULT_DISPATCHING_TIMEOUT_MILLIS`. + +The `timeoutTime` field of the `DispatchEntry` is needed because the window associated with a specific connection may change its timeout time. Therefore, entries sent prior to the timeout change would need to follow the previous timeout value. If a window timeout changes, it only affects new events being dispatched, and does not alter the timeout times of events already sent to the app. + +For example, if an application is being debugged, the ActivityManager may want to increase the timeout time for a window to prevent the ANR dialog from appearing or the app from getting killed. + +Looping through `waitQueue`s of all connections on every dispatch cycle could be costly. To improve this, we introduced the `AnrTracker` class. + +`AnrTracker` uses a multiset (a set that allows duplicate entries) to keep track of the next time a dispatch entry would become out of date. Duplicate entries are allowed because there may be two events with an identical timeout time. This is unlikely to happen in practice today, but is possible if the window timeouts are different or if the device has a high input report rate or a low clock resolution. + +On each dispatch cycle, InputDispatcher checks `AnrTracker` for the nearest timeout value. If the nearest timeout value is in the past, InputDispatcher will trigger the ANR for the corresponding connection. + +When an application sends a response for a particular dispatch entry, that entry is removed from the connection's `waitQueue`, and it is also removed from the `AnrTracker`. During normal operation, the entries are removed from `AnrTracker` quickly. + + +### How to test ### + +In order to test this behaviour, you can create an application that calls `SystemClock.sleep` while handling a click event. + +When this happens, the expectation is that the ANR dialog will come up within a short period of sending an input event (typically 5 seconds). While the app is not responding, it is expected that touches on other applications and gesture monitors still continue to work. + + +## 2. Application does not have a focused window, and a focused event comes in ## + +This is a legacy behaviour that we are maintaining inside InputDispatcher. When an application is launched, WindowManager calls `setFocusedApplication` to tell InputDispatcher that there is a focused application. This is used by InputDispatcher purely for ANR and debugging purposes. + +After launching, an application may not add a focused window. This could be either due to a bug in WindowManager or in the app. + +The legacy behaviour in this situation is as follows: touches will continue to function normally, without causing an ANR. If there is a focused event, however, it would require a focused window to be dispatched. InputDispatcher will keep this focused event inside mPendingEvent until: + +* A focused window is added +* Timeout occurs +* User touches another application + +To keep track of this timeout, when this situation is detected initially, `mInputTargetWaitTimeoutTime` and `mAwaitedFocusedApplication` are set. When the `mInputTargetWaitTimeoutTime` expires, an ANR will be raised. + + +### How to test ### + +Create an empty application that sets `FLAG_NOT_FOCUSABLE` on its window in `onCreate`. Touching the application's window should not cause an ANR. Sending a key event to the application, however, should cause ANR. One easy way to do this is by pressing or gesturing the BACK key. In this scenario, `adb shell dumpsys input` will reveal that there's no focused window in the current display. + + +## Extending the timeout based on the response from policy ## + +When the policy processes the ANR notification and responds with a positive timeout, InputDispatcher marks the connection as "responsive" by setting `inputPublisherBlocked = false`. All of the entries for this connection inside AnrTracker will be modified to expire at `time = (current time) + (timeout extension returned by policy)`. + +If the policy wants to abort dispatch, it returns a timeout value of 0. In this case, InputDispatcher will synthesize cancel events for the connection. + +When an app is unresponsive, new touches do not go to the app. They get dropped with a warning log. This is done to prevent overwhelming the app with events in case it later becomes responsive. diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp index b56f356dfd..9e797e4862 100644 --- a/services/inputflinger/host/Android.bp +++ b/services/inputflinger/host/Android.bp @@ -23,6 +23,7 @@ cc_library_shared { header_libs: ["jni_headers"], shared_libs: [ + "libbase", "libbinder", "libcrypto", "libcutils", @@ -59,9 +60,11 @@ cc_binary { shared_libs: [ "libbinder", "libinputflingerhost", - "libutils" + "libutils", + "libinput" ], static_libs: [ "libarect", + "libui-types", ], } diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp index c9001b0224..2ebdbcfcc4 100644 --- a/services/inputflinger/host/InputDriver.cpp +++ b/services/inputflinger/host/InputDriver.cpp @@ -36,7 +36,7 @@ #define INDENT2 " " struct input_property_map { - android::PropertyMap* propertyMap; + std::unique_ptr<android::PropertyMap> propertyMap; }; struct input_property { @@ -217,22 +217,25 @@ input_property_map_t* InputDriver::inputGetDevicePropertyMap(input_device_identi idi.product = id->productId; idi.version = id->version; - std::string configFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( - idi, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); + std::string configFile = + getInputDeviceConfigurationFilePathByDeviceIdentifier(idi, + InputDeviceConfigurationFileType:: + CONFIGURATION); if (configFile.empty()) { ALOGD("No input device configuration file found for device '%s'.", idi.name.c_str()); } else { - auto propMap = new input_property_map_t(); - status_t status = PropertyMap::load(String8(configFile.c_str()), &propMap->propertyMap); - if (status) { + std::unique_ptr<input_property_map_t> propMap = std::make_unique<input_property_map_t>(); + android::base::Result<std::unique_ptr<PropertyMap>> result = + PropertyMap::load(configFile.c_str()); + if (!result.ok()) { ALOGE("Error loading input device configuration file for device '%s'. " "Using default configuration.", idi.name.c_str()); - delete propMap; return nullptr; } - return propMap; + propMap->propertyMap = std::move(*result); + return propMap.release(); } return nullptr; } @@ -276,7 +279,6 @@ void InputDriver::inputFreeDeviceProperty(input_property_t* property) { void InputDriver::inputFreeDevicePropertyMap(input_property_map_t* map) { if (map != nullptr) { - delete map->propertyMap; delete map; } } diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index 973b4f92fa..47773d9180 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -22,13 +22,17 @@ #include "InputHost.h" +#include <android/os/BnInputFlinger.h> +#include <android/os/ISetInputWindowsListener.h> +#include <binder/Binder.h> #include <cutils/compiler.h> -#include <input/IInputFlinger.h> -#include <input/ISetInputWindowsListener.h> -#include <utils/String8.h> #include <utils/String16.h> +#include <utils/String8.h> #include <utils/StrongPointer.h> +using android::os::BnInputFlinger; +using android::os::ISetInputWindowsListener; + namespace android { class InputFlinger : public BnInputFlinger { @@ -40,10 +44,15 @@ public: InputFlinger() ANDROID_API; virtual status_t dump(int fd, const Vector<String16>& args); - void setInputWindows(const std::vector<InputWindowInfo>&, - const sp<ISetInputWindowsListener>&) {} - void registerInputChannel(const sp<InputChannel>&) {} - void unregisterInputChannel(const sp<InputChannel>&) {} + binder::Status setInputWindows(const std::vector<InputWindowInfo>&, + const sp<ISetInputWindowsListener>&) { + return binder::Status::ok(); + } + binder::Status createInputChannel(const std::string&, InputChannel*) { + return binder::Status::ok(); + } + binder::Status removeInputChannel(const sp<IBinder>&) { return binder::Status::ok(); } + binder::Status setFocusedWindow(const FocusRequest&) { return binder::Status::ok(); } private: virtual ~InputFlinger(); diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 0fa878759b..ffd8bf2fe1 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -17,31 +17,28 @@ #ifndef _UI_INPUT_READER_BASE_H #define _UI_INPUT_READER_BASE_H -#include "PointerControllerInterface.h" - #include <input/DisplayViewport.h> #include <input/Input.h> #include <input/InputDevice.h> #include <input/VelocityControl.h> #include <input/VelocityTracker.h> +#include <stddef.h> +#include <unistd.h> #include <utils/Errors.h> #include <utils/RefBase.h> -#include <stddef.h> -#include <unistd.h> #include <optional> #include <set> #include <unordered_map> #include <vector> +#include "PointerControllerInterface.h" +#include "VibrationElement.h" + // Maximum supported size of a vibration pattern. // Must be at least 2. #define MAX_VIBRATE_PATTERN_SIZE 100 -// Maximum allowable delay value in a vibration pattern before -// which the delay will be truncated. -#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL) - namespace android { // --- InputReaderInterface --- @@ -81,7 +78,7 @@ public: * * This method may be called on any thread (usually by the input manager). */ - virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) = 0; + virtual std::vector<InputDeviceInfo> getInputDevices() const = 0; /* Query current input state. */ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, @@ -104,8 +101,8 @@ public: virtual void requestRefreshConfiguration(uint32_t changes) = 0; /* Controls the vibrator of a particular input device. */ - virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, - ssize_t repeat, int32_t token) = 0; + virtual void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, + ssize_t repeat, int32_t token) = 0; virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0; /* Return true if the device can send input events to the specified display. */ @@ -344,7 +341,7 @@ public: virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0; /* Gets the keyboard layout for a particular input device. */ - virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay( + virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier) = 0; /* Gets a user-supplied alias for a particular input device, or an empty string if none. */ diff --git a/services/inputflinger/include/VibrationElement.h b/services/inputflinger/include/VibrationElement.h new file mode 100644 index 0000000000..b60fface44 --- /dev/null +++ b/services/inputflinger/include/VibrationElement.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 _VIBRATION_ELEMENT_H +#define _VIBRATION_ELEMENT_H + +#include <array> +#include <chrono> +#include <cstdint> +#include <string> + +namespace android { + +// evdev FF_RUMBLE effect only supports two channels of vibration. +constexpr size_t CHANNEL_SIZE = 2; +/* + * Describes a rumble effect + */ +struct VibrationElement { + std::chrono::milliseconds duration; + // Channel amplitude range 0-255. + std::array<uint8_t, CHANNEL_SIZE> channels = {0, 0}; + + const std::string toString() const; + uint16_t getMagnitude(size_t channelIndex) const; + bool isOn() const; +}; + +} // namespace android + +#endif // _VIBRATION_ELEMENT_H diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 83a610f768..0ccada94e4 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -81,4 +81,7 @@ cc_library_shared { export_header_lib_headers: [ "libinputreader_headers", ], + static_libs: [ + "libc++fs" + ], } diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index a1514af668..c5210b53ae 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -34,45 +34,36 @@ #define LOG_TAG "EventHub" // #define LOG_NDEBUG 0 - -#include "EventHub.h" - #include <android-base/stringprintf.h> #include <cutils/properties.h> +#include <input/KeyCharacterMap.h> +#include <input/KeyLayoutMap.h> +#include <input/VirtualKeyMap.h> #include <openssl/sha.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/Timers.h> -#include <utils/threads.h> - -#include <input/KeyCharacterMap.h> -#include <input/KeyLayoutMap.h> -#include <input/VirtualKeyMap.h> -/* this macro is used to tell if "bit" is set in "array" - * it selects a byte from the array, and does a boolean AND - * operation with a byte that only has the relevant bit set. - * eg. to check for the 12th bit, we do (array[1] & 1<<4) - */ -#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8))) +#include <filesystem> -/* this macro computes the number of bytes needed to represent a bit array of the specified size */ -#define sizeof_bit_array(bits) (((bits) + 7) / 8) +#include "EventHub.h" #define INDENT " " #define INDENT2 " " #define INDENT3 " " using android::base::StringPrintf; +using namespace android::flag_operators; namespace android { -static constexpr bool DEBUG = false; - static const char* DEVICE_PATH = "/dev/input"; // v4l2 devices go directly into /dev static const char* VIDEO_DEVICE_PATH = "/dev"; +static constexpr size_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0; +static constexpr size_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1; + static inline const char* toString(bool value) { return value ? "true" : "false"; } @@ -94,8 +85,8 @@ static std::string sha1(const std::string& in) { /** * Return true if name matches "v4l-touch*" */ -static bool isV4lTouchNode(const char* name) { - return strstr(name, "v4l-touch") == name; +static bool isV4lTouchNode(std::string name) { + return name.find("v4l-touch") != std::string::npos; } /** @@ -138,9 +129,9 @@ static nsecs_t processEventTimestamp(const struct input_event& event) { // --- Global Functions --- -uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { +Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses) { // Touch devices get dibs on touch-related axes. - if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) { + if (deviceClasses.test(InputDeviceClass::TOUCH)) { switch (axis) { case ABS_X: case ABS_Y: @@ -162,27 +153,26 @@ uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { case ABS_MT_TRACKING_ID: case ABS_MT_PRESSURE: case ABS_MT_DISTANCE: - return INPUT_DEVICE_CLASS_TOUCH; + return InputDeviceClass::TOUCH; } } // External stylus gets the pressure axis - if (deviceClasses & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + if (deviceClasses.test(InputDeviceClass::EXTERNAL_STYLUS)) { if (axis == ABS_PRESSURE) { - return INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; + return InputDeviceClass::EXTERNAL_STYLUS; } } // Joystick devices get the rest. - return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK; + return deviceClasses & InputDeviceClass::JOYSTICK; } // --- EventHub::Device --- EventHub::Device::Device(int fd, int32_t id, const std::string& path, const InputDeviceIdentifier& identifier) - : next(nullptr), - fd(fd), + : fd(fd), id(id), path(path), identifier(identifier), @@ -193,19 +183,10 @@ EventHub::Device::Device(int fd, int32_t id, const std::string& path, ffEffectId(-1), controllerNumber(0), enabled(true), - isVirtual(fd < 0) { - memset(keyBitmask, 0, sizeof(keyBitmask)); - memset(absBitmask, 0, sizeof(absBitmask)); - memset(relBitmask, 0, sizeof(relBitmask)); - memset(swBitmask, 0, sizeof(swBitmask)); - memset(ledBitmask, 0, sizeof(ledBitmask)); - memset(ffBitmask, 0, sizeof(ffBitmask)); - memset(propBitmask, 0, sizeof(propBitmask)); -} + isVirtual(fd < 0) {} EventHub::Device::~Device() { close(); - delete configuration; } void EventHub::Device::close() { @@ -231,10 +212,159 @@ status_t EventHub::Device::disable() { return OK; } -bool EventHub::Device::hasValidFd() { +bool EventHub::Device::hasValidFd() const { return !isVirtual && enabled; } +const std::shared_ptr<KeyCharacterMap> EventHub::Device::getKeyCharacterMap() const { + return keyMap.keyCharacterMap; +} + +template <std::size_t N> +status_t EventHub::Device::readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray) { + if (!hasValidFd()) { + return BAD_VALUE; + } + if ((_IOC_SIZE(ioctlCode) == 0)) { + ioctlCode |= _IOC(0, 0, 0, bitArray.bytes()); + } + + typename BitArray<N>::Buffer buffer; + status_t ret = ioctl(fd, ioctlCode, buffer.data()); + bitArray.loadFromBuffer(buffer); + return ret; +} + +void EventHub::Device::configureFd() { + // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type + if (classes.test(InputDeviceClass::KEYBOARD)) { + // Disable kernel key repeat since we handle it ourselves + unsigned int repeatRate[] = {0, 0}; + if (ioctl(fd, EVIOCSREP, repeatRate)) { + ALOGW("Unable to disable kernel key repeat for %s: %s", path.c_str(), strerror(errno)); + } + } + + // Tell the kernel that we want to use the monotonic clock for reporting timestamps + // associated with input events. This is important because the input system + // uses the timestamps extensively and assumes they were recorded using the monotonic + // clock. + int clockId = CLOCK_MONOTONIC; + bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId); + ALOGI("usingClockIoctl=%s", toString(usingClockIoctl)); +} + +bool EventHub::Device::hasKeycodeLocked(int keycode) const { + if (!keyMap.haveKeyLayout()) { + return false; + } + + std::vector<int32_t> scanCodes; + keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); + const size_t N = scanCodes.size(); + for (size_t i = 0; i < N && i <= KEY_MAX; i++) { + int32_t sc = scanCodes[i]; + if (sc >= 0 && sc <= KEY_MAX && keyBitmask.test(sc)) { + return true; + } + } + + return false; +} + +void EventHub::Device::loadConfigurationLocked() { + configurationFile = + getInputDeviceConfigurationFilePathByDeviceIdentifier(identifier, + InputDeviceConfigurationFileType:: + CONFIGURATION); + if (configurationFile.empty()) { + ALOGD("No input device configuration file found for device '%s'.", identifier.name.c_str()); + } else { + android::base::Result<std::unique_ptr<PropertyMap>> propertyMap = + PropertyMap::load(configurationFile.c_str()); + if (!propertyMap.ok()) { + ALOGE("Error loading input device configuration file for device '%s'. " + "Using default configuration.", + identifier.name.c_str()); + } else { + configuration = std::move(*propertyMap); + } + } +} + +bool EventHub::Device::loadVirtualKeyMapLocked() { + // The virtual key map is supplied by the kernel as a system board property file. + std::string propPath = "/sys/board_properties/virtualkeys."; + propPath += identifier.getCanonicalName(); + if (access(propPath.c_str(), R_OK)) { + return false; + } + virtualKeyMap = VirtualKeyMap::load(propPath); + return virtualKeyMap != nullptr; +} + +status_t EventHub::Device::loadKeyMapLocked() { + return keyMap.load(identifier, configuration.get()); +} + +bool EventHub::Device::isExternalDeviceLocked() { + if (configuration) { + bool value; + if (configuration->tryGetProperty(String8("device.internal"), value)) { + return !value; + } + } + return identifier.bus == BUS_USB || identifier.bus == BUS_BLUETOOTH; +} + +bool EventHub::Device::deviceHasMicLocked() { + if (configuration) { + bool value; + if (configuration->tryGetProperty(String8("audio.mic"), value)) { + return value; + } + } + return false; +} + +void EventHub::Device::setLedStateLocked(int32_t led, bool on) { + int32_t sc; + if (hasValidFd() && mapLed(led, &sc) != NAME_NOT_FOUND) { + struct input_event ev; + ev.time.tv_sec = 0; + ev.time.tv_usec = 0; + ev.type = EV_LED; + ev.code = sc; + ev.value = on ? 1 : 0; + + ssize_t nWrite; + do { + nWrite = write(fd, &ev, sizeof(struct input_event)); + } while (nWrite == -1 && errno == EINTR); + } +} + +void EventHub::Device::setLedForControllerLocked() { + for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) { + setLedStateLocked(ALED_CONTROLLER_1 + i, controllerNumber == i + 1); + } +} + +status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { + if (!keyMap.haveKeyLayout()) { + return NAME_NOT_FOUND; + } + + int32_t scanCode; + if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { + if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) { + *outScanCode = scanCode; + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + /** * Get the capabilities for the current process. * Crashes the system if unable to create / check / destroy the capabilities object. @@ -284,8 +414,6 @@ EventHub::EventHub(void) : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), - mOpeningDevices(nullptr), - mClosingDevices(nullptr), mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), @@ -340,12 +468,6 @@ EventHub::EventHub(void) EventHub::~EventHub(void) { closeAllDevicesLocked(); - while (mClosingDevices) { - Device* device = mClosingDevices; - mClosingDevices = device->next; - delete device; - } - ::close(mEpollFd); ::close(mINotifyFd); ::close(mWakeReadPipeFd); @@ -355,28 +477,25 @@ EventHub::~EventHub(void) { InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == nullptr) return InputDeviceIdentifier(); - return device->identifier; + return device != nullptr ? device->identifier : InputDeviceIdentifier(); } -uint32_t EventHub::getDeviceClasses(int32_t deviceId) const { +Flags<InputDeviceClass> EventHub::getDeviceClasses(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == nullptr) return 0; - return device->classes; + return device != nullptr ? device->classes : Flags<InputDeviceClass>(0); } int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == nullptr) return 0; - return device->controllerNumber; + return device != nullptr ? device->controllerNumber : 0; } void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->configuration) { + if (device != nullptr && device->configuration) { *outConfiguration = *device->configuration; } else { outConfiguration->clear(); @@ -391,7 +510,7 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) { + if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) { struct input_absinfo info; if (ioctl(device->fd, EVIOCGABS(axis), &info)) { ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis, @@ -416,25 +535,19 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, bool EventHub::hasRelativeAxis(int32_t deviceId, int axis) const { if (axis >= 0 && axis <= REL_MAX) { AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device) { - return test_bit(axis, device->relBitmask); - } + return device != nullptr ? device->relBitmask.test(axis) : false; } return false; } bool EventHub::hasInputProperty(int32_t deviceId, int property) const { - if (property >= 0 && property <= INPUT_PROP_MAX) { - AutoMutex _l(mLock); + AutoMutex _l(mLock); - Device* device = getDeviceLocked(deviceId); - if (device) { - return test_bit(property, device->propBitmask); - } - } - return false; + Device* device = getDeviceLocked(deviceId); + return property >= 0 && property <= INPUT_PROP_MAX && device != nullptr + ? device->propBitmask.test(property) + : false; } int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { @@ -442,11 +555,9 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && test_bit(scanCode, device->keyBitmask)) { - uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)]; - memset(keyState, 0, sizeof(keyState)); - if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) { - return test_bit(scanCode, keyState) ? AKEY_STATE_DOWN : AKEY_STATE_UP; + if (device != nullptr && device->hasValidFd() && device->keyBitmask.test(scanCode)) { + if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) { + return device->keyState.test(scanCode) ? AKEY_STATE_DOWN : AKEY_STATE_UP; } } } @@ -457,16 +568,14 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && device->keyMap.haveKeyLayout()) { + if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) { std::vector<int32_t> scanCodes; device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); if (scanCodes.size() != 0) { - uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)]; - memset(keyState, 0, sizeof(keyState)); - if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) { + if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) { for (size_t i = 0; i < scanCodes.size(); i++) { int32_t sc = scanCodes[i]; - if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, keyState)) { + if (sc >= 0 && sc <= KEY_MAX && device->keyState.test(sc)) { return AKEY_STATE_DOWN; } } @@ -482,11 +591,9 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && test_bit(sw, device->swBitmask)) { - uint8_t swState[sizeof_bit_array(SW_MAX + 1)]; - memset(swState, 0, sizeof(swState)); - if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) { - return test_bit(sw, swState) ? AKEY_STATE_DOWN : AKEY_STATE_UP; + if (device != nullptr && device->hasValidFd() && device->swBitmask.test(sw)) { + if (device->readDeviceBitMask(EVIOCGSW(0), device->swState) >= 0) { + return device->swState.test(sw) ? AKEY_STATE_DOWN : AKEY_STATE_UP; } } } @@ -500,7 +607,7 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) { + if (device != nullptr && device->hasValidFd() && device->absBitmask.test(axis)) { struct input_absinfo info; if (ioctl(device->fd, EVIOCGABS(axis), &info)) { ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis, @@ -520,7 +627,7 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const in AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->keyMap.haveKeyLayout()) { + if (device != nullptr && device->keyMap.haveKeyLayout()) { std::vector<int32_t> scanCodes; for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { scanCodes.clear(); @@ -531,7 +638,7 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const in // check the possible scan codes identified by the layout map against the // map of codes actually emitted by the driver for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (test_bit(scanCodes[sc], device->keyBitmask)) { + if (device->keyBitmask.test(scanCodes[sc])) { outFlags[codeIndex] = 1; break; } @@ -549,10 +656,10 @@ status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, Device* device = getDeviceLocked(deviceId); status_t status = NAME_NOT_FOUND; - if (device) { + if (device != nullptr) { // Check the key character map first. - sp<KeyCharacterMap> kcm = device->getKeyCharacterMap(); - if (kcm != nullptr) { + const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap(); + if (kcm) { if (!kcm->mapKey(scanCode, usageCode, outKeycode)) { *outFlags = 0; status = NO_ERROR; @@ -567,7 +674,7 @@ status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, } if (status == NO_ERROR) { - if (kcm != nullptr) { + if (kcm) { kcm->tryRemapKey(*outKeycode, metaState, outKeycode, outMetaState); } else { *outMetaState = metaState; @@ -588,7 +695,7 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->keyMap.haveKeyLayout()) { + if (device != nullptr && device->keyMap.haveKeyLayout()) { status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); if (err == NO_ERROR) { return NO_ERROR; @@ -607,10 +714,8 @@ void EventHub::setExcludedDevices(const std::vector<std::string>& devices) { bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && scanCode >= 0 && scanCode <= KEY_MAX) { - if (test_bit(scanCode, device->keyBitmask)) { - return true; - } + if (device != nullptr && scanCode >= 0 && scanCode <= KEY_MAX) { + return device->keyBitmask.test(scanCode); } return false; } @@ -619,10 +724,8 @@ bool EventHub::hasLed(int32_t deviceId, int32_t led) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); int32_t sc; - if (device && mapLed(device, led, &sc) == NO_ERROR) { - if (test_bit(sc, device->ledBitmask)) { - return true; - } + if (device != nullptr && device->mapLed(led, &sc) == NO_ERROR) { + return device->ledBitmask.test(sc); } return false; } @@ -630,23 +733,8 @@ bool EventHub::hasLed(int32_t deviceId, int32_t led) const { void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - setLedStateLocked(device, led, on); -} - -void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) { - int32_t sc; - if (device && device->hasValidFd() && mapLed(device, led, &sc) != NAME_NOT_FOUND) { - struct input_event ev; - ev.time.tv_sec = 0; - ev.time.tv_usec = 0; - ev.type = EV_LED; - ev.code = sc; - ev.value = on ? 1 : 0; - - ssize_t nWrite; - do { - nWrite = write(device->fd, &ev, sizeof(struct input_event)); - } while (nWrite == -1 && errno == EINTR); + if (device != nullptr && device->hasValidFd()) { + device->setLedStateLocked(led, on); } } @@ -656,31 +744,29 @@ void EventHub::getVirtualKeyDefinitions(int32_t deviceId, AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->virtualKeyMap) { + if (device != nullptr && device->virtualKeyMap) { const std::vector<VirtualKeyDefinition> virtualKeys = device->virtualKeyMap->getVirtualKeys(); outVirtualKeys.insert(outVirtualKeys.end(), virtualKeys.begin(), virtualKeys.end()); } } -sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const { +const std::shared_ptr<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device) { + if (device != nullptr) { return device->getKeyCharacterMap(); } return nullptr; } -bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) { +bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr<KeyCharacterMap> map) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device) { - if (map != device->overlayKeyMap) { - device->overlayKeyMap = map; - device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map); - return true; - } + if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) { + device->keyMap.keyCharacterMap->combine(*map); + device->keyMap.keyCharacterMapFile = device->keyMap.keyCharacterMap->getLoadFileName(); + return true; } return false; } @@ -735,17 +821,18 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { identifier.descriptor.c_str()); } -void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { +void EventHub::vibrate(int32_t deviceId, const VibrationElement& element) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd()) { + if (device != nullptr && device->hasValidFd()) { ff_effect effect; memset(&effect, 0, sizeof(effect)); effect.type = FF_RUMBLE; effect.id = device->ffEffectId; - effect.u.rumble.strong_magnitude = 0xc000; - effect.u.rumble.weak_magnitude = 0xc000; - effect.replay.length = (duration + 999999LL) / 1000000LL; + // evdev FF_RUMBLE effect only supports two channels of vibration. + effect.u.rumble.strong_magnitude = element.getMagnitude(FF_STRONG_MAGNITUDE_CHANNEL_IDX); + effect.u.rumble.weak_magnitude = element.getMagnitude(FF_WEAK_MAGNITUDE_CHANNEL_IDX); + effect.replay.length = element.duration.count(); effect.replay.delay = 0; if (ioctl(device->fd, EVIOCSFF, &effect)) { ALOGW("Could not upload force feedback effect to device %s due to error %d.", @@ -772,7 +859,7 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { void EventHub::cancelVibrate(int32_t deviceId) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->hasValidFd()) { + if (device != nullptr && device->hasValidFd()) { if (device->ffEffectPlaying) { device->ffEffectPlaying = false; @@ -792,11 +879,9 @@ void EventHub::cancelVibrate(int32_t deviceId) { } EventHub::Device* EventHub::getDeviceByDescriptorLocked(const std::string& descriptor) const { - size_t size = mDevices.size(); - for (size_t i = 0; i < size; i++) { - Device* device = mDevices.valueAt(i); + for (const auto& [id, device] : mDevices) { if (descriptor == device->identifier.descriptor) { - return device; + return device.get(); } } return nullptr; @@ -806,15 +891,14 @@ EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { if (deviceId == ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID) { deviceId = mBuiltInKeyboardId; } - ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt(index) : NULL; + const auto& it = mDevices.find(deviceId); + return it != mDevices.end() ? it->second.get() : nullptr; } -EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const { - for (size_t i = 0; i < mDevices.size(); i++) { - Device* device = mDevices.valueAt(i); +EventHub::Device* EventHub::getDeviceByPathLocked(const std::string& devicePath) const { + for (const auto& [id, device] : mDevices) { if (device->path == devicePath) { - return device; + return device.get(); } } return nullptr; @@ -828,15 +912,14 @@ EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const * devices are ignored. */ EventHub::Device* EventHub::getDeviceByFdLocked(int fd) const { - for (size_t i = 0; i < mDevices.size(); i++) { - Device* device = mDevices.valueAt(i); + for (const auto& [id, device] : mDevices) { if (device->fd == fd) { // This is an input device event - return device; + return device.get(); } if (device->videoDevice && device->videoDevice->getFd() == fd) { // This is a video device event - return device; + return device.get(); } } // We do not check mUnattachedVideoDevices here because they should not participate in epoll, @@ -869,17 +952,16 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } // Report any devices that had last been added/removed. - while (mClosingDevices) { - Device* device = mClosingDevices; + for (auto it = mClosingDevices.begin(); it != mClosingDevices.end();) { + std::unique_ptr<Device> device = std::move(*it); ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str()); - mClosingDevices = device->next; event->when = now; event->deviceId = (device->id == mBuiltInKeyboardId) ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id; event->type = DEVICE_REMOVED; event += 1; - delete device; + it = mClosingDevices.erase(it); mNeedToSendFinishedDeviceScan = true; if (--capacity == 0) { break; @@ -892,14 +974,30 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz mNeedToSendFinishedDeviceScan = true; } - while (mOpeningDevices != nullptr) { - Device* device = mOpeningDevices; + while (!mOpeningDevices.empty()) { + std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin()); + mOpeningDevices.pop_back(); ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str()); - mOpeningDevices = device->next; event->when = now; event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; event->type = DEVICE_ADDED; event += 1; + + // Try to find a matching video device by comparing device names + for (auto it = mUnattachedVideoDevices.begin(); it != mUnattachedVideoDevices.end(); + it++) { + std::unique_ptr<TouchVideoDevice>& videoDevice = *it; + if (tryAddVideoDevice(*device, videoDevice)) { + // videoDevice was transferred to 'device' + it = mUnattachedVideoDevices.erase(it); + break; + } + } + + auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device)); + if (!inserted) { + ALOGW("Device id %d exists, replaced.", device->id); + } mNeedToSendFinishedDeviceScan = true; if (--capacity == 0) { break; @@ -933,11 +1031,11 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz if (eventItem.events & EPOLLIN) { ALOGV("awoken after wake()"); awoken = true; - char buffer[16]; + char wakeReadBuffer[16]; ssize_t nRead; do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); + nRead = read(mWakeReadPipeFd, wakeReadBuffer, sizeof(wakeReadBuffer)); + } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(wakeReadBuffer)); } else { ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.", eventItem.events); @@ -946,7 +1044,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } Device* device = getDeviceByFdLocked(eventItem.data.fd); - if (!device) { + if (device == nullptr) { ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events, eventItem.data.fd); ALOG_ASSERT(!DEBUG); @@ -982,7 +1080,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz " bufferSize: %zu capacity: %zu errno: %d)\n", device->fd, readSize, bufferSize, capacity, errno); deviceChanged = true; - closeDeviceLocked(device); + closeDeviceLocked(*device); } else if (readSize < 0) { if (errno != EAGAIN && errno != EINTR) { ALOGW("could not get event (errno=%d)", errno); @@ -1014,7 +1112,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz ALOGI("Removing device %s due to epoll hang-up event.", device->identifier.name.c_str()); deviceChanged = true; - closeDeviceLocked(device); + closeDeviceLocked(*device); } else { ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, device->identifier.name.c_str()); @@ -1089,7 +1187,7 @@ std::vector<TouchVideoFrame> EventHub::getVideoFrames(int32_t deviceId) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (!device || !device->videoDevice) { + if (device == nullptr || !device->videoDevice) { return {}; } return device->videoDevice->consumeFrames(); @@ -1119,24 +1217,13 @@ void EventHub::scanDevicesLocked() { ALOGE("scan video dir failed for %s", VIDEO_DEVICE_PATH); } } - if (mDevices.indexOfKey(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) < 0) { + if (mDevices.find(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) == mDevices.end()) { createVirtualKeyboardLocked(); } } // ---------------------------------------------------------------------------- -static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) { - const uint8_t* end = array + endIndex; - array += startIndex; - while (array != end) { - if (*(array++) != 0) { - return true; - } - } - return false; -} - static const int32_t GAMEPAD_KEYCODES[] = { AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, // AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, // @@ -1166,20 +1253,14 @@ status_t EventHub::unregisterFdFromEpoll(int fd) { return OK; } -status_t EventHub::registerDeviceForEpollLocked(Device* device) { - if (device == nullptr) { - if (DEBUG) { - LOG_ALWAYS_FATAL("Cannot call registerDeviceForEpollLocked with null Device"); - } - return BAD_VALUE; - } - status_t result = registerFdForEpoll(device->fd); +status_t EventHub::registerDeviceForEpollLocked(Device& device) { + status_t result = registerFdForEpoll(device.fd); if (result != OK) { - ALOGE("Could not add input device fd to epoll for device %" PRId32, device->id); + ALOGE("Could not add input device fd to epoll for device %" PRId32, device.id); return result; } - if (device->videoDevice) { - registerVideoDeviceForEpollLocked(*device->videoDevice); + if (device.videoDevice) { + registerVideoDeviceForEpollLocked(*device.videoDevice); } return result; } @@ -1191,16 +1272,16 @@ void EventHub::registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDe } } -status_t EventHub::unregisterDeviceFromEpollLocked(Device* device) { - if (device->hasValidFd()) { - status_t result = unregisterFdFromEpoll(device->fd); +status_t EventHub::unregisterDeviceFromEpollLocked(Device& device) { + if (device.hasValidFd()) { + status_t result = unregisterFdFromEpoll(device.fd); if (result != OK) { - ALOGW("Could not remove input device fd from epoll for device %" PRId32, device->id); + ALOGW("Could not remove input device fd from epoll for device %" PRId32, device.id); return result; } } - if (device->videoDevice) { - unregisterVideoDeviceFromEpollLocked(*device->videoDevice); + if (device.videoDevice) { + unregisterVideoDeviceFromEpollLocked(*device.videoDevice); } return OK; } @@ -1215,14 +1296,14 @@ void EventHub::unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& vide } } -status_t EventHub::openDeviceLocked(const char* devicePath) { +status_t EventHub::openDeviceLocked(const std::string& devicePath) { char buffer[80]; - ALOGV("Opening device: %s", devicePath); + ALOGV("Opening device: %s", devicePath.c_str()); - int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK); + int fd = open(devicePath.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); if (fd < 0) { - ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); + ALOGE("could not open %s, %s\n", devicePath.c_str(), strerror(errno)); return -1; } @@ -1230,7 +1311,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // Get device name. if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { - ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno)); + ALOGE("Could not get device name for %s: %s", devicePath.c_str(), strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.name = buffer; @@ -1240,7 +1321,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { for (size_t i = 0; i < mExcludedDevices.size(); i++) { const std::string& item = mExcludedDevices[i]; if (identifier.name == item) { - ALOGI("ignoring event id %s driver %s\n", devicePath, item.c_str()); + ALOGI("ignoring event id %s driver %s\n", devicePath.c_str(), item.c_str()); close(fd); return -1; } @@ -1249,7 +1330,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // Get device driver version. int driverVersion; if (ioctl(fd, EVIOCGVERSION, &driverVersion)) { - ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno)); + ALOGE("could not get driver version for %s, %s\n", devicePath.c_str(), strerror(errno)); close(fd); return -1; } @@ -1257,7 +1338,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // Get device identifier. struct input_id inputId; if (ioctl(fd, EVIOCGID, &inputId)) { - ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno)); + ALOGE("could not get device input id for %s, %s\n", devicePath.c_str(), strerror(errno)); close(fd); return -1; } @@ -1287,9 +1368,9 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // Allocate device. (The device object takes ownership of the fd at this point.) int32_t deviceId = mNextDeviceId++; - Device* device = new Device(fd, deviceId, devicePath, identifier); + std::unique_ptr<Device> device = std::make_unique<Device>(fd, deviceId, devicePath, identifier); - ALOGV("add device %d: %s\n", deviceId, devicePath); + ALOGV("add device %d: %s\n", deviceId, devicePath.c_str()); ALOGV(" bus: %04x\n" " vendor %04x\n" " product %04x\n" @@ -1303,35 +1384,31 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { driverVersion & 0xff); // Load the configuration file for the device. - loadConfigurationLocked(device); + device->loadConfigurationLocked(); // Figure out the kinds of events the device reports. - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask); - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask); - ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask); - ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask); - ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask); - ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask); - ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask); + device->readDeviceBitMask(EVIOCGBIT(EV_KEY, 0), device->keyBitmask); + device->readDeviceBitMask(EVIOCGBIT(EV_ABS, 0), device->absBitmask); + device->readDeviceBitMask(EVIOCGBIT(EV_REL, 0), device->relBitmask); + device->readDeviceBitMask(EVIOCGBIT(EV_SW, 0), device->swBitmask); + device->readDeviceBitMask(EVIOCGBIT(EV_LED, 0), device->ledBitmask); + device->readDeviceBitMask(EVIOCGBIT(EV_FF, 0), device->ffBitmask); + device->readDeviceBitMask(EVIOCGPROP(0), device->propBitmask); // See if this is a keyboard. Ignore everything in the button range except for // joystick and gamepad buttons which are handled like keyboards for the most part. bool haveKeyboardKeys = - containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) || - containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_WHEEL), - sizeof_bit_array(KEY_MAX + 1)); - bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), - sizeof_bit_array(BTN_MOUSE)) || - containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), - sizeof_bit_array(BTN_DIGI)); + device->keyBitmask.any(0, BTN_MISC) || device->keyBitmask.any(BTN_WHEEL, KEY_MAX + 1); + bool haveGamepadButtons = device->keyBitmask.any(BTN_MISC, BTN_MOUSE) || + device->keyBitmask.any(BTN_JOYSTICK, BTN_DIGI); if (haveKeyboardKeys || haveGamepadButtons) { - device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; + device->classes |= InputDeviceClass::KEYBOARD; } // See if this is a cursor device such as a trackball or mouse. - if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) && - test_bit(REL_Y, device->relBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_CURSOR; + if (device->keyBitmask.test(BTN_MOUSE) && device->relBitmask.test(REL_X) && + device->relBitmask.test(REL_Y)) { + device->classes |= InputDeviceClass::CURSOR; } // See if this is a rotary encoder type device. @@ -1339,43 +1416,41 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { if (device->configuration && device->configuration->tryGetProperty(String8("device.type"), deviceType)) { if (!deviceType.compare(String8("rotaryEncoder"))) { - device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER; + device->classes |= InputDeviceClass::ROTARY_ENCODER; } } // See if this is a touch pad. // Is this a new modern multi-touch driver? - if (test_bit(ABS_MT_POSITION_X, device->absBitmask) && - test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { + if (device->absBitmask.test(ABS_MT_POSITION_X) && device->absBitmask.test(ABS_MT_POSITION_Y)) { // Some joysticks such as the PS3 controller report axes that conflict // with the ABS_MT range. Try to confirm that the device really is // a touch screen. - if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { - device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; + if (device->keyBitmask.test(BTN_TOUCH) || !haveGamepadButtons) { + device->classes |= (InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT); } // Is this an old style single-touch driver? - } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) && - test_bit(ABS_Y, device->absBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_TOUCH; + } else if (device->keyBitmask.test(BTN_TOUCH) && device->absBitmask.test(ABS_X) && + device->absBitmask.test(ABS_Y)) { + device->classes |= InputDeviceClass::TOUCH; // Is this a BT stylus? - } else if ((test_bit(ABS_PRESSURE, device->absBitmask) || - test_bit(BTN_TOUCH, device->keyBitmask)) && - !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; + } else if ((device->absBitmask.test(ABS_PRESSURE) || device->keyBitmask.test(BTN_TOUCH)) && + !device->absBitmask.test(ABS_X) && !device->absBitmask.test(ABS_Y)) { + device->classes |= InputDeviceClass::EXTERNAL_STYLUS; // Keyboard will try to claim some of the buttons but we really want to reserve those so we // can fuse it with the touch screen data, so just take them back. Note this means an // external stylus cannot also be a keyboard device. - device->classes &= ~INPUT_DEVICE_CLASS_KEYBOARD; + device->classes &= ~InputDeviceClass::KEYBOARD; } // See if this device is a joystick. // Assumes that joysticks always have gamepad buttons in order to distinguish them // from other devices such as accelerometers that also have absolute axes. if (haveGamepadButtons) { - uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK; + auto assumedClasses = device->classes | InputDeviceClass::JOYSTICK; for (int i = 0; i <= ABS_MAX; i++) { - if (test_bit(i, device->absBitmask) && - (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) { + if (device->absBitmask.test(i) && + (getAbsAxisUsage(i, assumedClasses).test(InputDeviceClass::JOYSTICK))) { device->classes = assumedClasses; break; } @@ -1384,142 +1459,107 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // Check whether this device has switches. for (int i = 0; i <= SW_MAX; i++) { - if (test_bit(i, device->swBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_SWITCH; + if (device->swBitmask.test(i)) { + device->classes |= InputDeviceClass::SWITCH; break; } } // Check whether this device supports the vibrator. - if (test_bit(FF_RUMBLE, device->ffBitmask)) { - device->classes |= INPUT_DEVICE_CLASS_VIBRATOR; + if (device->ffBitmask.test(FF_RUMBLE)) { + device->classes |= InputDeviceClass::VIBRATOR; } // Configure virtual keys. - if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { + if ((device->classes.test(InputDeviceClass::TOUCH))) { // Load the virtual keys for the touch screen, if any. // We do this now so that we can make sure to load the keymap if necessary. - bool success = loadVirtualKeyMapLocked(device); + bool success = device->loadVirtualKeyMapLocked(); if (success) { - device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; + device->classes |= InputDeviceClass::KEYBOARD; } } // Load the key map. // We need to do this for joysticks too because the key layout may specify axes. status_t keyMapStatus = NAME_NOT_FOUND; - if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) { + if (device->classes.any(InputDeviceClass::KEYBOARD | InputDeviceClass::JOYSTICK)) { // Load the keymap for the device. - keyMapStatus = loadKeyMapLocked(device); + keyMapStatus = device->loadKeyMapLocked(); } // Configure the keyboard, gamepad or virtual keyboard. - if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { + if (device->classes.test(InputDeviceClass::KEYBOARD)) { // Register the keyboard as a built-in keyboard if it is eligible. if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD && - isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { + isEligibleBuiltInKeyboard(device->identifier, device->configuration.get(), + &device->keyMap)) { mBuiltInKeyboardId = device->id; } // 'Q' key support = cheap test of whether this is an alpha-capable kbd - if (hasKeycodeLocked(device, AKEYCODE_Q)) { - device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; + if (device->hasKeycodeLocked(AKEYCODE_Q)) { + device->classes |= InputDeviceClass::ALPHAKEY; } // See if this device has a DPAD. - if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && - hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && - hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) { - device->classes |= INPUT_DEVICE_CLASS_DPAD; + if (device->hasKeycodeLocked(AKEYCODE_DPAD_UP) && + device->hasKeycodeLocked(AKEYCODE_DPAD_DOWN) && + device->hasKeycodeLocked(AKEYCODE_DPAD_LEFT) && + device->hasKeycodeLocked(AKEYCODE_DPAD_RIGHT) && + device->hasKeycodeLocked(AKEYCODE_DPAD_CENTER)) { + device->classes |= InputDeviceClass::DPAD; } // See if this device has a gamepad. for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) { - if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) { - device->classes |= INPUT_DEVICE_CLASS_GAMEPAD; + if (device->hasKeycodeLocked(GAMEPAD_KEYCODES[i])) { + device->classes |= InputDeviceClass::GAMEPAD; break; } } } // If the device isn't recognized as something we handle, don't monitor it. - if (device->classes == 0) { - ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath, + if (device->classes == Flags<InputDeviceClass>(0)) { + ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath.c_str(), device->identifier.name.c_str()); - delete device; return -1; } // Determine whether the device has a mic. - if (deviceHasMicLocked(device)) { - device->classes |= INPUT_DEVICE_CLASS_MIC; + if (device->deviceHasMicLocked()) { + device->classes |= InputDeviceClass::MIC; } // Determine whether the device is external or internal. - if (isExternalDeviceLocked(device)) { - device->classes |= INPUT_DEVICE_CLASS_EXTERNAL; - } - - if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) && - device->classes & INPUT_DEVICE_CLASS_GAMEPAD) { - device->controllerNumber = getNextControllerNumberLocked(device); - setLedForControllerLocked(device); + if (device->isExternalDeviceLocked()) { + device->classes |= InputDeviceClass::EXTERNAL; } - // Find a matching video device by comparing device names - // This should be done before registerDeviceForEpollLocked, so that both fds are added to epoll - for (std::unique_ptr<TouchVideoDevice>& videoDevice : mUnattachedVideoDevices) { - if (device->identifier.name == videoDevice->getName()) { - device->videoDevice = std::move(videoDevice); - break; - } + if (device->classes.any(InputDeviceClass::JOYSTICK | InputDeviceClass::DPAD) && + device->classes.test(InputDeviceClass::GAMEPAD)) { + device->controllerNumber = getNextControllerNumberLocked(device->identifier.name); + device->setLedForControllerLocked(); } - mUnattachedVideoDevices - .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(), - [](const std::unique_ptr<TouchVideoDevice>& videoDevice) { - return videoDevice == nullptr; - }), - mUnattachedVideoDevices.end()); - if (registerDeviceForEpollLocked(device) != OK) { - delete device; + if (registerDeviceForEpollLocked(*device) != OK) { return -1; } - configureFd(device); + device->configureFd(); - ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " + ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, " "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ", - deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes, - device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(), - device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId)); + deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(), + device->classes.string().c_str(), device->configurationFile.c_str(), + device->keyMap.keyLayoutFile.c_str(), device->keyMap.keyCharacterMapFile.c_str(), + toString(mBuiltInKeyboardId == deviceId)); - addDeviceLocked(device); + addDeviceLocked(std::move(device)); return OK; } -void EventHub::configureFd(Device* device) { - // Set fd parameters with ioctl, such as key repeat, suspend block, and clock type - if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { - // Disable kernel key repeat since we handle it ourselves - unsigned int repeatRate[] = {0, 0}; - if (ioctl(device->fd, EVIOCSREP, repeatRate)) { - ALOGW("Unable to disable kernel key repeat for %s: %s", device->path.c_str(), - strerror(errno)); - } - } - - // Tell the kernel that we want to use the monotonic clock for reporting timestamps - // associated with input events. This is important because the input system - // uses the timestamps extensively and assumes they were recorded using the monotonic - // clock. - int clockId = CLOCK_MONOTONIC; - bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId); - ALOGI("usingClockIoctl=%s", toString(usingClockIoctl)); -} - void EventHub::openVideoDeviceLocked(const std::string& devicePath) { std::unique_ptr<TouchVideoDevice> videoDevice = TouchVideoDevice::create(devicePath); if (!videoDevice) { @@ -1527,14 +1567,9 @@ void EventHub::openVideoDeviceLocked(const std::string& devicePath) { return; } // Transfer ownership of this video device to a matching input device - for (size_t i = 0; i < mDevices.size(); i++) { - Device* device = mDevices.valueAt(i); - if (videoDevice->getName() == device->identifier.name) { - device->videoDevice = std::move(videoDevice); - if (device->enabled) { - registerVideoDeviceForEpollLocked(*device->videoDevice); - } - return; + for (const auto& [id, device] : mDevices) { + if (tryAddVideoDevice(*device, videoDevice)) { + return; // 'device' now owns 'videoDevice' } } @@ -1545,6 +1580,18 @@ void EventHub::openVideoDeviceLocked(const std::string& devicePath) { mUnattachedVideoDevices.push_back(std::move(videoDevice)); } +bool EventHub::tryAddVideoDevice(EventHub::Device& device, + std::unique_ptr<TouchVideoDevice>& videoDevice) { + if (videoDevice->getName() != device.identifier.name) { + return false; + } + device.videoDevice = std::move(videoDevice); + if (device.enabled) { + registerVideoDeviceForEpollLocked(*device.videoDevice); + } + return true; +} + bool EventHub::isDeviceEnabled(int32_t deviceId) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); @@ -1572,9 +1619,9 @@ status_t EventHub::enableDevice(int32_t deviceId) { return result; } - configureFd(device); + device->configureFd(); - return registerDeviceForEpollLocked(device); + return registerDeviceForEpollLocked(*device); } status_t EventHub::disableDevice(int32_t deviceId) { @@ -1588,7 +1635,7 @@ status_t EventHub::disableDevice(int32_t deviceId) { ALOGW("Duplicate call to %s, input device already disabled", __func__); return OK; } - unregisterDeviceFromEpollLocked(device); + unregisterDeviceFromEpollLocked(*device); return device->disable(); } @@ -1598,77 +1645,23 @@ void EventHub::createVirtualKeyboardLocked() { identifier.uniqueId = "<virtual>"; assignDescriptorLocked(identifier); - Device* device = - new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", identifier); - device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY | - INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL; - loadKeyMapLocked(device); - addDeviceLocked(device); + std::unique_ptr<Device> device = + std::make_unique<Device>(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", + identifier); + device->classes = InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY | + InputDeviceClass::DPAD | InputDeviceClass::VIRTUAL; + device->loadKeyMapLocked(); + addDeviceLocked(std::move(device)); } -void EventHub::addDeviceLocked(Device* device) { - mDevices.add(device->id, device); - device->next = mOpeningDevices; - mOpeningDevices = device; +void EventHub::addDeviceLocked(std::unique_ptr<Device> device) { + mOpeningDevices.push_back(std::move(device)); } -void EventHub::loadConfigurationLocked(Device* device) { - device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( - device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); - if (device->configurationFile.empty()) { - ALOGD("No input device configuration file found for device '%s'.", - device->identifier.name.c_str()); - } else { - status_t status = PropertyMap::load(String8(device->configurationFile.c_str()), - &device->configuration); - if (status) { - ALOGE("Error loading input device configuration file for device '%s'. " - "Using default configuration.", - device->identifier.name.c_str()); - } - } -} - -bool EventHub::loadVirtualKeyMapLocked(Device* device) { - // The virtual key map is supplied by the kernel as a system board property file. - std::string path; - path += "/sys/board_properties/virtualkeys."; - path += device->identifier.getCanonicalName(); - if (access(path.c_str(), R_OK)) { - return false; - } - device->virtualKeyMap = VirtualKeyMap::load(path); - return device->virtualKeyMap != nullptr; -} - -status_t EventHub::loadKeyMapLocked(Device* device) { - return device->keyMap.load(device->identifier, device->configuration); -} - -bool EventHub::isExternalDeviceLocked(Device* device) { - if (device->configuration) { - bool value; - if (device->configuration->tryGetProperty(String8("device.internal"), value)) { - return !value; - } - } - return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH; -} - -bool EventHub::deviceHasMicLocked(Device* device) { - if (device->configuration) { - bool value; - if (device->configuration->tryGetProperty(String8("audio.mic"), value)) { - return value; - } - } - return false; -} - -int32_t EventHub::getNextControllerNumberLocked(Device* device) { +int32_t EventHub::getNextControllerNumberLocked(const std::string& name) { if (mControllerNumbers.isFull()) { ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s", - device->identifier.name.c_str()); + name.c_str()); return 0; } // Since the controller number 0 is reserved for non-controllers, translate all numbers up by @@ -1676,61 +1669,19 @@ int32_t EventHub::getNextControllerNumberLocked(Device* device) { return static_cast<int32_t>(mControllerNumbers.markFirstUnmarkedBit() + 1); } -void EventHub::releaseControllerNumberLocked(Device* device) { - int32_t num = device->controllerNumber; - device->controllerNumber = 0; - if (num == 0) { - return; - } - mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1)); -} - -void EventHub::setLedForControllerLocked(Device* device) { - for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) { - setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1); +void EventHub::releaseControllerNumberLocked(int32_t num) { + if (num > 0) { + mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1)); } } -bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { - if (!device->keyMap.haveKeyLayout()) { - return false; - } - - std::vector<int32_t> scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); - const size_t N = scanCodes.size(); - for (size_t i = 0; i < N && i <= KEY_MAX; i++) { - int32_t sc = scanCodes[i]; - if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) { - return true; - } - } - - return false; -} - -status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) const { - if (!device->keyMap.haveKeyLayout()) { - return NAME_NOT_FOUND; - } - - int32_t scanCode; - if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { - if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) { - *outScanCode = scanCode; - return NO_ERROR; - } - } - return NAME_NOT_FOUND; -} - -void EventHub::closeDeviceByPathLocked(const char* devicePath) { +void EventHub::closeDeviceByPathLocked(const std::string& devicePath) { Device* device = getDeviceByPathLocked(devicePath); - if (device) { - closeDeviceLocked(device); + if (device != nullptr) { + closeDeviceLocked(*device); return; } - ALOGV("Remove device: %s not found, device may already have been removed.", devicePath); + ALOGV("Remove device: %s not found, device may already have been removed.", devicePath.c_str()); } /** @@ -1741,8 +1692,7 @@ void EventHub::closeDeviceByPathLocked(const char* devicePath) { void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) { // A video device may be owned by an existing input device, or it may be stored in // the mUnattachedVideoDevices queue. Check both locations. - for (size_t i = 0; i < mDevices.size(); i++) { - Device* device = mDevices.valueAt(i); + for (const auto& [id, device] : mDevices) { if (device->videoDevice && device->videoDevice->getPath() == devicePath) { unregisterVideoDeviceFromEpollLocked(*device->videoDevice); device->videoDevice = nullptr; @@ -1760,60 +1710,33 @@ void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) { void EventHub::closeAllDevicesLocked() { mUnattachedVideoDevices.clear(); - while (mDevices.size() > 0) { - closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1)); + while (!mDevices.empty()) { + closeDeviceLocked(*(mDevices.begin()->second)); } } -void EventHub::closeDeviceLocked(Device* device) { - ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(), - device->identifier.name.c_str(), device->id, device->fd, device->classes); +void EventHub::closeDeviceLocked(Device& device) { + ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=%s", device.path.c_str(), + device.identifier.name.c_str(), device.id, device.fd, device.classes.string().c_str()); - if (device->id == mBuiltInKeyboardId) { + if (device.id == mBuiltInKeyboardId) { ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", - device->path.c_str(), mBuiltInKeyboardId); + device.path.c_str(), mBuiltInKeyboardId); mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD; } unregisterDeviceFromEpollLocked(device); - if (device->videoDevice) { + if (device.videoDevice) { // This must be done after the video device is removed from epoll - mUnattachedVideoDevices.push_back(std::move(device->videoDevice)); + mUnattachedVideoDevices.push_back(std::move(device.videoDevice)); } - releaseControllerNumberLocked(device); + releaseControllerNumberLocked(device.controllerNumber); + device.controllerNumber = 0; + device.close(); + mClosingDevices.push_back(std::move(mDevices[device.id])); - mDevices.removeItem(device->id); - device->close(); - - // Unlink for opening devices list if it is present. - Device* pred = nullptr; - bool found = false; - for (Device* entry = mOpeningDevices; entry != nullptr;) { - if (entry == device) { - found = true; - break; - } - pred = entry; - entry = entry->next; - } - if (found) { - // Unlink the device from the opening devices list then delete it. - // We don't need to tell the client that the device was closed because - // it does not even know it was opened in the first place. - ALOGI("Device %s was immediately closed after opening.", device->path.c_str()); - if (pred) { - pred->next = device->next; - } else { - mOpeningDevices = device->next; - } - delete device; - } else { - // Link into closing devices list. - // The device will be deleted later after we have informed the client. - device->next = mClosingDevices; - mClosingDevices = device; - } + mDevices.erase(device.id); } status_t EventHub::readNotifyLocked() { @@ -1835,16 +1758,16 @@ status_t EventHub::readNotifyLocked() { event = (struct inotify_event*)(event_buf + event_pos); if (event->len) { if (event->wd == mInputWd) { - std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name); + std::string filename = std::string(DEVICE_PATH) + "/" + event->name; if (event->mask & IN_CREATE) { - openDeviceLocked(filename.c_str()); + openDeviceLocked(filename); } else { ALOGI("Removing device '%s' due to inotify event\n", filename.c_str()); - closeDeviceByPathLocked(filename.c_str()); + closeDeviceByPathLocked(filename); } } else if (event->wd == mVideoWd) { if (isV4lTouchNode(event->name)) { - std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name); + std::string filename = std::string(VIDEO_DEVICE_PATH) + "/" + event->name; if (event->mask & IN_CREATE) { openVideoDeviceLocked(filename); } else { @@ -1863,24 +1786,10 @@ status_t EventHub::readNotifyLocked() { return 0; } -status_t EventHub::scanDirLocked(const char* dirname) { - char devname[PATH_MAX]; - char* filename; - DIR* dir; - struct dirent* de; - dir = opendir(dirname); - if (dir == nullptr) return -1; - strcpy(devname, dirname); - filename = devname + strlen(devname); - *filename++ = '/'; - while ((de = readdir(dir))) { - if (de->d_name[0] == '.' && - (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) - continue; - strcpy(filename, de->d_name); - openDeviceLocked(devname); +status_t EventHub::scanDirLocked(const std::string& dirname) { + for (const auto& entry : std::filesystem::directory_iterator(dirname)) { + openDeviceLocked(entry.path()); } - closedir(dir); return 0; } @@ -1888,22 +1797,12 @@ status_t EventHub::scanDirLocked(const char* dirname) { * Look for all dirname/v4l-touch* devices, and open them. */ status_t EventHub::scanVideoDirLocked(const std::string& dirname) { - DIR* dir; - struct dirent* de; - dir = opendir(dirname.c_str()); - if (!dir) { - ALOGE("Could not open video directory %s", dirname.c_str()); - return BAD_VALUE; - } - - while ((de = readdir(dir))) { - const char* name = de->d_name; - if (isV4lTouchNode(name)) { - ALOGI("Found touch video device %s", name); - openVideoDeviceLocked(dirname + "/" + name); + for (const auto& entry : std::filesystem::directory_iterator(dirname)) { + if (isV4lTouchNode(entry.path())) { + ALOGI("Found touch video device %s", entry.path().c_str()); + openVideoDeviceLocked(entry.path()); } } - closedir(dir); return OK; } @@ -1924,8 +1823,7 @@ void EventHub::dump(std::string& dump) { dump += INDENT "Devices:\n"; - for (size_t i = 0; i < mDevices.size(); i++) { - const Device* device = mDevices.valueAt(i); + for (const auto& [id, device] : mDevices) { if (mBuiltInKeyboardId == device->id) { dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n", device->id, device->identifier.name.c_str()); @@ -1933,7 +1831,7 @@ void EventHub::dump(std::string& dump) { dump += StringPrintf(INDENT2 "%d: %s\n", device->id, device->identifier.name.c_str()); } - dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes); + dump += StringPrintf(INDENT3 "Classes: %s\n", device->classes.string().c_str()); dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str()); dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(device->enabled)); dump += StringPrintf(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.c_str()); @@ -1950,8 +1848,6 @@ void EventHub::dump(std::string& dump) { device->keyMap.keyCharacterMapFile.c_str()); dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n", device->configurationFile.c_str()); - dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n", - toString(device->overlayKeyMap != nullptr)); dump += INDENT3 "VideoDevice: "; if (device->videoDevice) { dump += device->videoDevice->dump() + "\n"; diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 3347ba6ad7..271bc2f943 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -18,6 +18,7 @@ #include "InputDevice.h" +#include <input/Flags.h> #include <algorithm> #include "CursorInputMapper.h" @@ -109,7 +110,7 @@ void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) { dump += INDENT2 "Motion Ranges:\n"; for (size_t i = 0; i < ranges.size(); i++) { const InputDeviceInfo::MotionRange& range = ranges[i]; - const char* label = getAxisLabel(range.axis); + const char* label = InputEventLookup::getAxisLabel(range.axis); char name[32]; if (label) { strncpy(name, label, sizeof(name)); @@ -133,7 +134,7 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { return; } std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId)); - uint32_t classes = contextPtr->getDeviceClasses(); + Flags<InputDeviceClass> classes = contextPtr->getDeviceClasses(); std::vector<std::unique_ptr<InputMapper>> mappers; // Check if we should skip population @@ -143,33 +144,33 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { } // Switch-like devices. - if (classes & INPUT_DEVICE_CLASS_SWITCH) { + if (classes.test(InputDeviceClass::SWITCH)) { mappers.push_back(std::make_unique<SwitchInputMapper>(*contextPtr)); } // Scroll wheel-like devices. - if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { + if (classes.test(InputDeviceClass::ROTARY_ENCODER)) { mappers.push_back(std::make_unique<RotaryEncoderInputMapper>(*contextPtr)); } // Vibrator-like devices. - if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { + if (classes.test(InputDeviceClass::VIBRATOR)) { mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr)); } // Keyboard-like devices. uint32_t keyboardSource = 0; int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; - if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { + if (classes.test(InputDeviceClass::KEYBOARD)) { keyboardSource |= AINPUT_SOURCE_KEYBOARD; } - if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { + if (classes.test(InputDeviceClass::ALPHAKEY)) { keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; } - if (classes & INPUT_DEVICE_CLASS_DPAD) { + if (classes.test(InputDeviceClass::DPAD)) { keyboardSource |= AINPUT_SOURCE_DPAD; } - if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + if (classes.test(InputDeviceClass::GAMEPAD)) { keyboardSource |= AINPUT_SOURCE_GAMEPAD; } @@ -179,24 +180,24 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { } // Cursor-like devices. - if (classes & INPUT_DEVICE_CLASS_CURSOR) { + if (classes.test(InputDeviceClass::CURSOR)) { mappers.push_back(std::make_unique<CursorInputMapper>(*contextPtr)); } // Touchscreens and touchpad devices. - if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { + if (classes.test(InputDeviceClass::TOUCH_MT)) { mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr)); - } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { + } else if (classes.test(InputDeviceClass::TOUCH)) { mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr)); } // Joystick-like devices. - if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { + if (classes.test(InputDeviceClass::JOYSTICK)) { mappers.push_back(std::make_unique<JoystickInputMapper>(*contextPtr)); } // External stylus-like devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + if (classes.test(InputDeviceClass::EXTERNAL_STYLUS)) { mappers.push_back(std::make_unique<ExternalStylusInputMapper>(*contextPtr)); } @@ -213,7 +214,7 @@ void InputDevice::removeEventHubDevice(int32_t eventHubId) { void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { mSources = 0; - mClasses = 0; + mClasses = Flags<InputDeviceClass>(0); mControllerNumber = 0; for_each_subdevice([this](InputDeviceContext& context) { @@ -228,8 +229,8 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } }); - mIsExternal = !!(mClasses & INPUT_DEVICE_CLASS_EXTERNAL); - mHasMic = !!(mClasses & INPUT_DEVICE_CLASS_MIC); + mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL); + mHasMic = mClasses.test(InputDeviceClass::MIC); if (!isIgnored()) { if (!changes) { // first time only @@ -242,8 +243,8 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { - if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { - sp<KeyCharacterMap> keyboardLayout = + if (!mClasses.test(InputDeviceClass::VIRTUAL)) { + std::shared_ptr<KeyCharacterMap> keyboardLayout = mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); bool shouldBumpGeneration = false; for_each_subdevice( @@ -259,7 +260,7 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { - if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + if (!(mClasses.test(InputDeviceClass::VIRTUAL))) { std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); if (mAlias != alias) { mAlias = alias; @@ -428,10 +429,10 @@ bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, return result; } -void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, +void InputDevice::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) { - for_each_mapper([pattern, patternSize, repeat, token](InputMapper& mapper) { - mapper.vibrate(pattern, patternSize, repeat, token); + for_each_mapper([pattern, repeat, token](InputMapper& mapper) { + mapper.vibrate(pattern, repeat, token); }); } @@ -484,6 +485,10 @@ size_t InputDevice::getMapperCount() { return count; } +void InputDevice::updateLedState(bool reset) { + for_each_mapper([reset](InputMapper& mapper) { mapper.updateLedState(reset); }); +} + InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId) : mDevice(device), mContext(device.getContext()), diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index dff830ca7f..2028b91501 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -47,6 +47,7 @@ InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, mEventHub(eventHub), mPolicy(policy), mGlobalMetaState(0), + mLedMetaState(AMETA_NUM_LOCK_ON), mGeneration(1), mNextInputDeviceId(END_RESERVED_ID), mDisableVirtualKeysTimeout(LLONG_MIN), @@ -86,7 +87,6 @@ void InputReader::loopOnce() { int32_t oldGeneration; int32_t timeoutMillis; bool inputDevicesChanged = false; - std::vector<InputDeviceInfo> inputDevices; { // acquire lock AutoMutex _l(mLock); @@ -127,13 +127,12 @@ void InputReader::loopOnce() { if (oldGeneration != mGeneration) { inputDevicesChanged = true; - getInputDevicesLocked(inputDevices); } } // release lock // Send out a message that the describes the changed input devices. if (inputDevicesChanged) { - mPolicy->notifyInputDevicesChanged(inputDevices); + mPolicy->notifyInputDevicesChanged(getInputDevicesLocked()); } // Flush queued events out to the listener. @@ -216,7 +215,7 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { } bumpGenerationLocked(); - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) { notifyExternalStylusPresenceChanged(); } } @@ -256,7 +255,7 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { device->removeEventHubDevice(eventHubId); - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS)) { notifyExternalStylusPresenceChanged(); } @@ -372,6 +371,18 @@ int32_t InputReader::getGlobalMetaStateLocked() { return mGlobalMetaState; } +void InputReader::updateLedMetaStateLocked(int32_t metaState) { + mLedMetaState = metaState; + for (auto& devicePair : mDevices) { + std::shared_ptr<InputDevice>& device = devicePair.second; + device->updateLedState(false); + } +} + +int32_t InputReader::getLedMetaStateLocked() { + return mLedMetaState; +} + void InputReader::notifyExternalStylusPresenceChanged() { refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE); } @@ -379,7 +390,7 @@ void InputReader::notifyExternalStylusPresenceChanged() { void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) { for (auto& devicePair : mDevices) { std::shared_ptr<InputDevice>& device = devicePair.second; - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) { + if (device->getClasses().test(InputDeviceClass::EXTERNAL_STYLUS) && !device->isIgnored()) { InputDeviceInfo info; device->getDeviceInfo(&info); outDevices.push_back(info); @@ -461,13 +472,14 @@ int32_t InputReader::bumpGenerationLocked() { return ++mGeneration; } -void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) { +std::vector<InputDeviceInfo> InputReader::getInputDevices() const { AutoMutex _l(mLock); - getInputDevicesLocked(outInputDevices); + return getInputDevicesLocked(); } -void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) { - outInputDevices.clear(); +std::vector<InputDeviceInfo> InputReader::getInputDevicesLocked() const { + std::vector<InputDeviceInfo> outInputDevices; + outInputDevices.reserve(mDeviceToEventHubIdsMap.size()); for (const auto& [device, eventHubIds] : mDeviceToEventHubIdsMap) { if (!device->isIgnored()) { @@ -476,6 +488,7 @@ void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDe outInputDevices.push_back(info); } } + return outInputDevices; } int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) { @@ -577,12 +590,12 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) { } } -void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, +void InputReader::vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) { AutoMutex _l(mLock); InputDevice* device = findInputDevice(deviceId); if (device) { - device->vibrate(pattern, patternSize, repeat, token); + device->vibrate(pattern, repeat, token); } } @@ -734,6 +747,16 @@ int32_t InputReader::ContextImpl::getGlobalMetaState() { return mReader->getGlobalMetaStateLocked(); } +void InputReader::ContextImpl::updateLedMetaState(int32_t metaState) { + // lock is already held by the input loop + mReader->updateLedMetaStateLocked(metaState); +} + +int32_t InputReader::ContextImpl::getLedMetaState() { + // lock is already held by the input loop + return mReader->getLedMetaStateLocked(); +} + void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) { // lock is already held by the input loop mReader->disableVirtualKeysUntilLocked(time); diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index f5451d7fef..edb82d3932 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -17,8 +17,12 @@ #ifndef _RUNTIME_EVENT_HUB_H #define _RUNTIME_EVENT_HUB_H +#include <bitset> +#include <climits> +#include <unordered_map> #include <vector> +#include <input/Flags.h> #include <input/Input.h> #include <input/InputDevice.h> #include <input/KeyCharacterMap.h> @@ -26,6 +30,8 @@ #include <input/Keyboard.h> #include <input/PropertyMap.h> #include <input/VirtualKeyMap.h> +#include <linux/input.h> +#include <sys/epoll.h> #include <utils/BitSet.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> @@ -33,15 +39,8 @@ #include <utils/Log.h> #include <utils/Mutex.h> -#include <linux/input.h> -#include <sys/epoll.h> - #include "TouchVideoDevice.h" - -/* Convenience constants. */ - -#define BTN_FIRST 0x100 // first button code -#define BTN_LAST 0x15f // last button code +#include "VibrationElement.h" namespace android { @@ -79,58 +78,58 @@ struct RawAbsoluteAxisInfo { /* * Input device classes. */ -enum { +enum class InputDeviceClass : uint32_t { /* The input device is a keyboard or has buttons. */ - INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, + KEYBOARD = 0x00000001, /* The input device is an alpha-numeric keyboard (not just a dial pad). */ - INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002, + ALPHAKEY = 0x00000002, /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */ - INPUT_DEVICE_CLASS_TOUCH = 0x00000004, + TOUCH = 0x00000004, /* The input device is a cursor device such as a trackball or mouse. */ - INPUT_DEVICE_CLASS_CURSOR = 0x00000008, + CURSOR = 0x00000008, /* The input device is a multi-touch touchscreen. */ - INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010, + TOUCH_MT = 0x00000010, /* The input device is a directional pad (implies keyboard, has DPAD keys). */ - INPUT_DEVICE_CLASS_DPAD = 0x00000020, + DPAD = 0x00000020, /* The input device is a gamepad (implies keyboard, has BUTTON keys). */ - INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040, + GAMEPAD = 0x00000040, /* The input device has switches. */ - INPUT_DEVICE_CLASS_SWITCH = 0x00000080, + SWITCH = 0x00000080, /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ - INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, + JOYSTICK = 0x00000100, /* The input device has a vibrator (supports FF_RUMBLE). */ - INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200, + VIBRATOR = 0x00000200, /* The input device has a microphone. */ - INPUT_DEVICE_CLASS_MIC = 0x00000400, + MIC = 0x00000400, /* The input device is an external stylus (has data we want to fuse with touch data). */ - INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800, + EXTERNAL_STYLUS = 0x00000800, /* The input device has a rotary encoder */ - INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000, + ROTARY_ENCODER = 0x00001000, /* The input device is virtual (not a real device, not part of UI configuration). */ - INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000, + VIRTUAL = 0x40000000, /* The input device is external (not built-in). */ - INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000, + EXTERNAL = 0x80000000, }; /* * Gets the class that owns an axis, in cases where multiple classes might claim * the same axis for different purposes. */ -extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses); +extern Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses); /* * Grand Central Station for events. @@ -163,7 +162,7 @@ public: FIRST_SYNTHETIC_EVENT = DEVICE_ADDED, }; - virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0; + virtual Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const = 0; virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0; @@ -227,11 +226,12 @@ public: virtual void getVirtualKeyDefinitions( int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0; - virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0; - virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0; + virtual const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0; + virtual bool setKeyboardLayoutOverlay(int32_t deviceId, + std::shared_ptr<KeyCharacterMap> map) = 0; /* Control the vibrator. */ - virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0; + virtual void vibrate(int32_t deviceId, const VibrationElement& effect) = 0; virtual void cancelVibrate(int32_t deviceId) = 0; /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */ @@ -256,73 +256,150 @@ public: virtual status_t disableDevice(int32_t deviceId) = 0; }; +template <std::size_t BITS> +class BitArray { + /* Array element type and vector of element type. */ + using Element = std::uint32_t; + /* Number of bits in each BitArray element. */ + static constexpr size_t WIDTH = sizeof(Element) * CHAR_BIT; + /* Number of elements to represent a bit array of the specified size of bits. */ + static constexpr size_t COUNT = (BITS + WIDTH - 1) / WIDTH; + +public: + /* BUFFER type declaration for BitArray */ + using Buffer = std::array<Element, COUNT>; + /* To tell if a bit is set in array, it selects an element from the array, and test + * if the relevant bit set. + * Note the parameter "bit" is an index to the bit, 0 <= bit < BITS. + */ + inline bool test(size_t bit) const { + return (bit < BITS) ? mData[bit / WIDTH].test(bit % WIDTH) : false; + } + /* Returns total number of bytes needed for the array */ + inline size_t bytes() { return (BITS + CHAR_BIT - 1) / CHAR_BIT; } + /* Returns true if array contains any non-zero bit from the range defined by start and end + * bit index [startIndex, endIndex). + */ + bool any(size_t startIndex, size_t endIndex) { + if (startIndex >= endIndex || startIndex > BITS || endIndex > BITS + 1) { + ALOGE("Invalid start/end index. start = %zu, end = %zu, total bits = %zu", startIndex, + endIndex, BITS); + return false; + } + size_t se = startIndex / WIDTH; // Start of element + size_t ee = endIndex / WIDTH; // End of element + size_t si = startIndex % WIDTH; // Start index in start element + size_t ei = endIndex % WIDTH; // End index in end element + // Need to check first unaligned bitset for any non zero bit + if (si > 0) { + size_t nBits = se == ee ? ei - si : WIDTH - si; + // Generate the mask of interested bit range + Element mask = ((1 << nBits) - 1) << si; + if (mData[se++].to_ulong() & mask) { + return true; + } + } + // Check whole bitset for any bit set + for (; se < ee; se++) { + if (mData[se].any()) { + return true; + } + } + // Need to check last unaligned bitset for any non zero bit + if (ei > 0 && se <= ee) { + // Generate the mask of interested bit range + Element mask = (1 << ei) - 1; + if (mData[se].to_ulong() & mask) { + return true; + } + } + return false; + } + /* Load bit array values from buffer */ + void loadFromBuffer(const Buffer& buffer) { + for (size_t i = 0; i < COUNT; i++) { + mData[i] = std::bitset<WIDTH>(buffer[i]); + } + } + +private: + std::array<std::bitset<WIDTH>, COUNT> mData; +}; + class EventHub : public EventHubInterface { public: EventHub(); - virtual uint32_t getDeviceClasses(int32_t deviceId) const override; + Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override final; - virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override; + InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override final; - virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override; + int32_t getDeviceControllerNumber(int32_t deviceId) const override final; - virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override; + void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override final; - virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const override; + status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const override final; - virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override; + bool hasRelativeAxis(int32_t deviceId, int axis) const override final; - virtual bool hasInputProperty(int32_t deviceId, int property) const override; + bool hasInputProperty(int32_t deviceId, int property) const override final; - virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, - int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, - uint32_t* outFlags) const override; + status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, + uint32_t* outFlags) const override final; - virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, - AxisInfo* outAxisInfo) const override; + status_t mapAxis(int32_t deviceId, int32_t scanCode, + AxisInfo* outAxisInfo) const override final; - virtual void setExcludedDevices(const std::vector<std::string>& devices) override; + void setExcludedDevices(const std::vector<std::string>& devices) override final; - virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override; - virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override; - virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const override; + int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override final; + int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override final; + int32_t getSwitchState(int32_t deviceId, int32_t sw) const override final; + status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const override final; - virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const override; + bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) const override final; - virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override; - virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override; + size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override final; + std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override final; - virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override; - virtual bool hasLed(int32_t deviceId, int32_t led) const override; - virtual void setLedState(int32_t deviceId, int32_t led, bool on) override; + bool hasScanCode(int32_t deviceId, int32_t scanCode) const override final; + bool hasLed(int32_t deviceId, int32_t led) const override final; + void setLedState(int32_t deviceId, int32_t led, bool on) override final; - virtual void getVirtualKeyDefinitions( - int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override; + void getVirtualKeyDefinitions( + int32_t deviceId, + std::vector<VirtualKeyDefinition>& outVirtualKeys) const override final; - virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override; - virtual bool setKeyboardLayoutOverlay(int32_t deviceId, - const sp<KeyCharacterMap>& map) override; + const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap( + int32_t deviceId) const override final; + bool setKeyboardLayoutOverlay(int32_t deviceId, + std::shared_ptr<KeyCharacterMap> map) override final; + + void vibrate(int32_t deviceId, const VibrationElement& effect) override final; + void cancelVibrate(int32_t deviceId) override final; + + void requestReopenDevices() override final; + + void wake() override final; - virtual void vibrate(int32_t deviceId, nsecs_t duration) override; - virtual void cancelVibrate(int32_t deviceId) override; + void dump(std::string& dump) override final; - virtual void requestReopenDevices() override; + void monitor() override final; - virtual void wake() override; + bool isDeviceEnabled(int32_t deviceId) override final; - virtual void dump(std::string& dump) override; - virtual void monitor() override; + status_t enableDevice(int32_t deviceId) override final; - virtual ~EventHub() override; + status_t disableDevice(int32_t deviceId) override final; + + ~EventHub() override; private: struct Device { - Device* next; - int fd; // may be -1 if device is closed const int32_t id; const std::string path; @@ -330,24 +407,23 @@ private: std::unique_ptr<TouchVideoDevice> videoDevice; - uint32_t classes; + Flags<InputDeviceClass> classes; - uint8_t keyBitmask[(KEY_MAX + 1) / 8]; - uint8_t absBitmask[(ABS_MAX + 1) / 8]; - uint8_t relBitmask[(REL_MAX + 1) / 8]; - uint8_t swBitmask[(SW_MAX + 1) / 8]; - uint8_t ledBitmask[(LED_MAX + 1) / 8]; - uint8_t ffBitmask[(FF_MAX + 1) / 8]; - uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8]; + BitArray<KEY_MAX> keyBitmask; + BitArray<KEY_MAX> keyState; + BitArray<ABS_MAX> absBitmask; + BitArray<REL_MAX> relBitmask; + BitArray<SW_MAX> swBitmask; + BitArray<SW_MAX> swState; + BitArray<LED_MAX> ledBitmask; + BitArray<FF_MAX> ffBitmask; + BitArray<INPUT_PROP_MAX> propBitmask; std::string configurationFile; - PropertyMap* configuration; + std::unique_ptr<PropertyMap> configuration; std::unique_ptr<VirtualKeyMap> virtualKeyMap; KeyMap keyMap; - sp<KeyCharacterMap> overlayKeyMap; - sp<KeyCharacterMap> combinedKeyMap; - bool ffEffectPlaying; int16_t ffEffectId; // initially -1 @@ -362,69 +438,68 @@ private: bool enabled; // initially true status_t enable(); status_t disable(); - bool hasValidFd(); + bool hasValidFd() const; const bool isVirtual; // set if fd < 0 is passed to constructor - const sp<KeyCharacterMap>& getKeyCharacterMap() const { - if (combinedKeyMap != nullptr) { - return combinedKeyMap; - } - return keyMap.keyCharacterMap; - } + const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const; + + template <std::size_t N> + status_t readDeviceBitMask(unsigned long ioctlCode, BitArray<N>& bitArray); + + void configureFd(); + bool hasKeycodeLocked(int keycode) const; + void loadConfigurationLocked(); + bool loadVirtualKeyMapLocked(); + status_t loadKeyMapLocked(); + bool isExternalDeviceLocked(); + bool deviceHasMicLocked(); + void setLedForControllerLocked(); + status_t mapLed(int32_t led, int32_t* outScanCode) const; + void setLedStateLocked(int32_t led, bool on); }; - status_t openDeviceLocked(const char* devicePath); + status_t openDeviceLocked(const std::string& devicePath); void openVideoDeviceLocked(const std::string& devicePath); + /** + * Try to associate a video device with an input device. If the association succeeds, + * the videoDevice is moved into the input device. 'videoDevice' will become null if this + * happens. + * Return true if the association succeeds. + * Return false otherwise. + */ + bool tryAddVideoDevice(Device& device, std::unique_ptr<TouchVideoDevice>& videoDevice); void createVirtualKeyboardLocked(); - void addDeviceLocked(Device* device); + void addDeviceLocked(std::unique_ptr<Device> device); void assignDescriptorLocked(InputDeviceIdentifier& identifier); - void closeDeviceByPathLocked(const char* devicePath); + void closeDeviceByPathLocked(const std::string& devicePath); void closeVideoDeviceByPathLocked(const std::string& devicePath); - void closeDeviceLocked(Device* device); + void closeDeviceLocked(Device& device); void closeAllDevicesLocked(); - void configureFd(Device* device); - - bool isDeviceEnabled(int32_t deviceId) override; - status_t enableDevice(int32_t deviceId) override; - status_t disableDevice(int32_t deviceId) override; status_t registerFdForEpoll(int fd); status_t unregisterFdFromEpoll(int fd); - status_t registerDeviceForEpollLocked(Device* device); + status_t registerDeviceForEpollLocked(Device& device); void registerVideoDeviceForEpollLocked(const TouchVideoDevice& videoDevice); - status_t unregisterDeviceFromEpollLocked(Device* device); + status_t unregisterDeviceFromEpollLocked(Device& device); void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice); - status_t scanDirLocked(const char* dirname); + status_t scanDirLocked(const std::string& dirname); status_t scanVideoDirLocked(const std::string& dirname); void scanDevicesLocked(); status_t readNotifyLocked(); Device* getDeviceByDescriptorLocked(const std::string& descriptor) const; Device* getDeviceLocked(int32_t deviceId) const; - Device* getDeviceByPathLocked(const char* devicePath) const; + Device* getDeviceByPathLocked(const std::string& devicePath) const; /** * Look through all available fd's (both for input devices and for video devices), * and return the device pointer. */ Device* getDeviceByFdLocked(int fd) const; - bool hasKeycodeLocked(Device* device, int keycode) const; - - void loadConfigurationLocked(Device* device); - bool loadVirtualKeyMapLocked(Device* device); - status_t loadKeyMapLocked(Device* device); - - bool isExternalDeviceLocked(Device* device); - bool deviceHasMicLocked(Device* device); - - int32_t getNextControllerNumberLocked(Device* device); - void releaseControllerNumberLocked(Device* device); - void setLedForControllerLocked(Device* device); - - status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const; - void setLedStateLocked(Device* device, int32_t led, bool on); + int32_t getNextControllerNumberLocked(const std::string& name); + void releaseControllerNumberLocked(int32_t num); // Protect all internal state. mutable Mutex mLock; @@ -442,7 +517,7 @@ private: BitSet32 mControllerNumbers; - KeyedVector<int32_t, Device*> mDevices; + std::unordered_map<int32_t, std::unique_ptr<Device>> mDevices; /** * Video devices that report touchscreen heatmap, but have not (yet) been paired * with a specific input device. Video device discovery is independent from input device @@ -452,8 +527,8 @@ private: */ std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices; - Device* mOpeningDevices; - Device* mClosingDevices; + std::vector<std::unique_ptr<Device>> mOpeningDevices; + std::vector<std::unique_ptr<Device>> mClosingDevices; bool mNeedToSendFinishedDeviceScan; bool mNeedToReopenDevices; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index d2bb4f4b4c..7d160ebf2c 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -18,6 +18,7 @@ #define _UI_INPUTREADER_INPUT_DEVICE_H #include <input/DisplayViewport.h> +#include <input/Flags.h> #include <input/InputDevice.h> #include <input/PropertyMap.h> #include <stdint.h> @@ -48,7 +49,7 @@ public: inline int32_t getGeneration() const { return mGeneration; } inline const std::string getName() const { return mIdentifier.name; } inline const std::string getDescriptor() { return mIdentifier.descriptor; } - inline uint32_t getClasses() const { return mClasses; } + inline Flags<InputDeviceClass> getClasses() const { return mClasses; } inline uint32_t getSources() const { return mSources; } inline bool hasEventHubDevices() const { return !mDevices.empty(); } @@ -81,7 +82,7 @@ public: int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); - void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token); void cancelVibrate(int32_t token); void cancelTouch(nsecs_t when); @@ -97,6 +98,8 @@ public: std::optional<int32_t> getAssociatedDisplayId(); + void updateLedState(bool reset); + size_t getMapperCount(); // construct and add a mapper to the input device @@ -121,7 +124,7 @@ private: int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; std::string mAlias; - uint32_t mClasses; + Flags<InputDeviceClass> mClasses; // map from eventHubId to device context and mappers using MapperVector = std::vector<std::unique_ptr<InputMapper>>; @@ -206,7 +209,9 @@ public: inline int32_t getId() { return mDeviceId; } inline int32_t getEventHubId() { return mId; } - inline uint32_t getDeviceClasses() const { return mEventHub->getDeviceClasses(mId); } + inline Flags<InputDeviceClass> getDeviceClasses() const { + return mEventHub->getDeviceClasses(mId); + } inline InputDeviceIdentifier getDeviceIdentifier() const { return mEventHub->getDeviceIdentifier(mId); } @@ -256,13 +261,15 @@ public: inline void getVirtualKeyDefinitions(std::vector<VirtualKeyDefinition>& outVirtualKeys) const { return mEventHub->getVirtualKeyDefinitions(mId, outVirtualKeys); } - inline sp<KeyCharacterMap> getKeyCharacterMap() const { + inline const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap() const { return mEventHub->getKeyCharacterMap(mId); } - inline bool setKeyboardLayoutOverlay(const sp<KeyCharacterMap>& map) { + inline bool setKeyboardLayoutOverlay(std::shared_ptr<KeyCharacterMap> map) { return mEventHub->setKeyboardLayoutOverlay(mId, map); } - inline void vibrate(nsecs_t duration) { return mEventHub->vibrate(mId, duration); } + inline void vibrate(const VibrationElement& element) { + return mEventHub->vibrate(mId, element); + } inline void cancelVibrate() { return mEventHub->cancelVibrate(mId); } inline bool hasAbsoluteAxis(int32_t code) const { diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 2773f709eb..563018ad5a 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -55,34 +55,32 @@ public: const sp<InputListenerInterface>& listener); virtual ~InputReader(); - virtual void dump(std::string& dump) override; - virtual void monitor() override; + void dump(std::string& dump) override; + void monitor() override; - virtual status_t start() override; - virtual status_t stop() override; + status_t start() override; + status_t stop() override; - virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override; + std::vector<InputDeviceInfo> getInputDevices() const override; - virtual bool isInputDeviceEnabled(int32_t deviceId) override; + bool isInputDeviceEnabled(int32_t deviceId) override; - virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode) override; - virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode) override; - virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; + int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) override; + int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override; + int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; - virtual void toggleCapsLockState(int32_t deviceId) override; + void toggleCapsLockState(int32_t deviceId) override; - virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) override; + bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) override; - virtual void requestRefreshConfiguration(uint32_t changes) override; + void requestRefreshConfiguration(uint32_t changes) override; - virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, - ssize_t repeat, int32_t token) override; - virtual void cancelVibrate(int32_t deviceId, int32_t token) override; + void vibrate(int32_t deviceId, const std::vector<VibrationElement>& pattern, ssize_t repeat, + int32_t token) override; + void cancelVibrate(int32_t deviceId, int32_t token) override; - virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override; + bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override; protected: // These members are protected so they can be instrumented by test cases. @@ -100,21 +98,22 @@ protected: public: explicit ContextImpl(InputReader* reader); - virtual void updateGlobalMetaState() override; - virtual int32_t getGlobalMetaState() override; - virtual void disableVirtualKeysUntil(nsecs_t time) override; - virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override; - virtual void fadePointer() override; - virtual std::shared_ptr<PointerControllerInterface> getPointerController( - int32_t deviceId) override; - virtual void requestTimeoutAtTime(nsecs_t when) override; - virtual int32_t bumpGeneration() override; - virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override; - virtual void dispatchExternalStylusState(const StylusState& outState) override; - virtual InputReaderPolicyInterface* getPolicy() override; - virtual InputListenerInterface* getListener() override; - virtual EventHubInterface* getEventHub() override; - virtual int32_t getNextId() override; + void updateGlobalMetaState() override; + int32_t getGlobalMetaState() override; + void disableVirtualKeysUntil(nsecs_t time) override; + bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override; + void fadePointer() override; + std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override; + void requestTimeoutAtTime(nsecs_t when) override; + int32_t bumpGeneration() override; + void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override; + void dispatchExternalStylusState(const StylusState& outState) override; + InputReaderPolicyInterface* getPolicy() override; + InputListenerInterface* getListener() override; + EventHubInterface* getEventHub() override; + int32_t getNextId() override; + void updateLedMetaState(int32_t metaState) override; + int32_t getLedMetaState() override; } mContext; friend class ContextImpl; @@ -122,7 +121,7 @@ protected: private: std::unique_ptr<InputThread> mThread; - Mutex mLock; + mutable Mutex mLock; Condition mReaderIsAliveCondition; @@ -162,6 +161,10 @@ private: void updateGlobalMetaStateLocked(); int32_t getGlobalMetaStateLocked(); + int32_t mLedMetaState; + void updateLedMetaStateLocked(int32_t metaState); + int32_t getLedMetaStateLocked(); + void notifyExternalStylusPresenceChanged(); void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices); void dispatchExternalStylusState(const StylusState& state); @@ -178,7 +181,7 @@ private: int32_t mNextInputDeviceId; int32_t nextInputDeviceIdLocked(); - void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices); + std::vector<InputDeviceInfo> getInputDevicesLocked() const; nsecs_t mDisableVirtualKeysTimeout; void disableVirtualKeysUntilLocked(nsecs_t time); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index ffb8d8c44a..dc807f7886 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -59,6 +59,9 @@ public: virtual EventHubInterface* getEventHub() = 0; virtual int32_t getNextId() = 0; + + virtual void updateLedMetaState(int32_t metaState) = 0; + virtual int32_t getLedMetaState() = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 55c1a1dcac..254b64b4d9 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -14,7 +14,9 @@ * limitations under the License. */ +// clang-format off #include "../Macros.h" +// clang-format on #include "CursorInputMapper.h" @@ -188,7 +190,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mOrientation = DISPLAY_ORIENTATION_0; if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { std::optional<DisplayViewport> internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + config->getDisplayViewportByType(ViewportType::INTERNAL); if (internalViewport) { mOrientation = internalViewport->orientation; } diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp index a8fe39aa10..1db829f4e3 100644 --- a/services/inputflinger/reader/mapper/InputMapper.cpp +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -56,7 +56,7 @@ bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, return false; } -void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, +void InputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) {} void InputMapper::cancelVibrate(int32_t token) {} diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index 949c7ea34e..56ab9288b6 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -22,6 +22,7 @@ #include "InputListener.h" #include "InputReaderContext.h" #include "StylusState.h" +#include "VibrationElement.h" namespace android { @@ -62,7 +63,8 @@ public: virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, + int32_t token); virtual void cancelVibrate(int32_t token); virtual void cancelTouch(nsecs_t when); @@ -72,6 +74,7 @@ public: virtual void updateExternalStylusState(const StylusState& state); virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; } + virtual void updateLedState(bool reset) {} protected: InputDeviceContext& mDeviceContext; diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index 030a846727..abd8aa9119 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -32,8 +32,8 @@ uint32_t JoystickInputMapper::getSources() { void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); - for (size_t i = 0; i < mAxes.size(); i++) { - const Axis& axis = mAxes.valueAt(i); + for (std::pair<const int32_t, Axis>& pair : mAxes) { + const Axis& axis = pair.second; addMotionRange(axis.axisInfo.axis, axis, info); if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { @@ -72,17 +72,15 @@ void JoystickInputMapper::dump(std::string& dump) { dump += INDENT2 "Joystick Input Mapper:\n"; dump += INDENT3 "Axes:\n"; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - const char* label = getAxisLabel(axis.axisInfo.axis); + for (const auto& [rawAxis, axis] : mAxes) { + const char* label = InputEventLookup::getAxisLabel(axis.axisInfo.axis); if (label) { dump += StringPrintf(INDENT4 "%s", label); } else { dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); } if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - label = getAxisLabel(axis.axisInfo.highAxis); + label = InputEventLookup::getAxisLabel(axis.axisInfo.highAxis); if (label) { dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); } else { @@ -100,7 +98,7 @@ void JoystickInputMapper::dump(std::string& dump) { axis.scale, axis.offset, axis.highScale, axis.highOffset); dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", - mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, + rawAxis, axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, axis.rawAxisInfo.resolution); } @@ -113,8 +111,8 @@ void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration if (!changes) { // first time only // Collect all axes. for (int32_t abs = 0; abs <= ABS_MAX; abs++) { - if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses()) & - INPUT_DEVICE_CLASS_JOYSTICK)) { + if (!(getAbsAxisUsage(abs, getDeviceContext().getDeviceClasses()) + .test(InputDeviceClass::JOYSTICK))) { continue; // axis must be claimed by a different device } @@ -123,43 +121,14 @@ void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration if (rawAxisInfo.valid) { // Map axis. AxisInfo axisInfo; - bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo); + const bool explicitlyMapped = !getDeviceContext().mapAxis(abs, &axisInfo); + if (!explicitlyMapped) { // Axis is not explicitly mapped, will choose a generic axis later. axisInfo.mode = AxisInfo::MODE_NORMAL; axisInfo.axis = -1; } - - // Apply flat override. - int32_t rawFlat = - axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; - - // Calculate scaling factors and limits. - Axis axis; - if (axisInfo.mode == AxisInfo::MODE_SPLIT) { - float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); - float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale, - 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else if (isCenteredAxis(axisInfo.axis)) { - float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale, - offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else { - float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale, - 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } - - // To eliminate noise while the joystick is at rest, filter out small variations - // in axis values up front. - axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; - - mAxes.add(abs, axis); + mAxes.insert({abs, createAxis(axisInfo, rawAxisInfo, explicitlyMapped)}); } } @@ -174,9 +143,8 @@ void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration // Assign generic axis ids to remaining axes. int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); + for (auto it = mAxes.begin(); it != mAxes.end(); /*increment it inside loop*/) { + Axis& axis = it->second; if (axis.axisInfo.axis < 0) { while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 && haveAxis(nextGenericAxisId)) { @@ -189,19 +157,57 @@ void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration } else { ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " "have already been assigned to other axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i--); - numAxes -= 1; + getDeviceName().c_str(), it->first); + it = mAxes.erase(it); + continue; } } + it++; } } } +JoystickInputMapper::Axis JoystickInputMapper::createAxis(const AxisInfo& axisInfo, + const RawAbsoluteAxisInfo& rawAxisInfo, + bool explicitlyMapped) { + // Apply flat override. + int32_t rawFlat = axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; + + float scale = std::numeric_limits<float>::signaling_NaN(); + float highScale = std::numeric_limits<float>::signaling_NaN(); + float highOffset = 0; + float offset = 0; + float min = 0; + // Calculate scaling factors and limits. + if (axisInfo.mode == AxisInfo::MODE_SPLIT) { + scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); + highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); + } else if (isCenteredAxis(axisInfo.axis)) { + scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; + highOffset = offset; + highScale = scale; + min = -1.0f; + } else { + scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + highScale = scale; + } + + constexpr float max = 1.0; + const float flat = rawFlat * scale; + const float fuzz = rawAxisInfo.fuzz * scale; + const float resolution = rawAxisInfo.resolution * scale; + + // To eliminate noise while the joystick is at rest, filter out small variations + // in axis values up front. + const float filter = fuzz ? fuzz : flat * 0.25f; + return Axis(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, highScale, highOffset, min, + max, flat, fuzz, resolution, filter); +} + bool JoystickInputMapper::haveAxis(int32_t axisId) { - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); + for (const std::pair<int32_t, Axis>& pair : mAxes) { + const Axis& axis = pair.second; if (axis.axisInfo.axis == axisId || (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) { return true; @@ -211,14 +217,14 @@ bool JoystickInputMapper::haveAxis(int32_t axisId) { } void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { - size_t i = mAxes.size(); - while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { - if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { + while (mAxes.size() > PointerCoords::MAX_AXES) { + auto it = mAxes.begin(); + if (ignoreExplicitlyMappedAxes && it->second.explicitlyMapped) { continue; } ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i); + getDeviceName().c_str(), it->first); + mAxes.erase(it); } } @@ -243,9 +249,8 @@ bool JoystickInputMapper::isCenteredAxis(int32_t axis) { void JoystickInputMapper::reset(nsecs_t when) { // Recenter all axes. - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); + for (std::pair<const int32_t, Axis>& pair : mAxes) { + Axis& axis = pair.second; axis.resetValue(); } @@ -255,9 +260,9 @@ void JoystickInputMapper::reset(nsecs_t when) { void JoystickInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EV_ABS: { - ssize_t index = mAxes.indexOfKey(rawEvent->code); - if (index >= 0) { - Axis& axis = mAxes.editValueAt(index); + auto it = mAxes.find(rawEvent->code); + if (it != mAxes.end()) { + Axis& axis = it->second; float newValue, highNewValue; switch (axis.axisInfo.mode) { case AxisInfo::MODE_INVERT: @@ -317,9 +322,8 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { PointerCoords pointerCoords; pointerCoords.clear(); - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); + for (std::pair<const int32_t, Axis>& pair : mAxes) { + const Axis& axis = pair.second; setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, @@ -357,9 +361,8 @@ void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords bool JoystickInputMapper::filterAxes(bool force) { bool atLeastOneSignificantChange = force; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); + for (std::pair<const int32_t, Axis>& pair : mAxes) { + Axis& axis = pair.second; if (force || hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min, axis.max)) { diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h index 823a096d68..0cf60a27d8 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.h +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -36,6 +36,26 @@ public: private: struct Axis { + explicit Axis(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, + bool explicitlyMapped, float scale, float offset, float highScale, + float highOffset, float min, float max, float flat, float fuzz, + float resolution, float filter) + : rawAxisInfo(rawAxisInfo), + axisInfo(axisInfo), + explicitlyMapped(explicitlyMapped), + scale(scale), + offset(offset), + highScale(highScale), + highOffset(highOffset), + min(min), + max(max), + flat(flat), + fuzz(fuzz), + resolution(resolution), + filter(filter) { + resetValue(); + } + RawAbsoluteAxisInfo rawAxisInfo; AxisInfo axisInfo; @@ -58,26 +78,6 @@ private: float highCurrentValue; // current value of high split float highNewValue; // most recent value of high split - void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, - bool explicitlyMapped, float scale, float offset, float highScale, - float highOffset, float min, float max, float flat, float fuzz, - float resolution) { - this->rawAxisInfo = rawAxisInfo; - this->axisInfo = axisInfo; - this->explicitlyMapped = explicitlyMapped; - this->scale = scale; - this->offset = offset; - this->highScale = highScale; - this->highOffset = highOffset; - this->min = min; - this->max = max; - this->flat = flat; - this->fuzz = fuzz; - this->resolution = resolution; - this->filter = 0; - resetValue(); - } - void resetValue() { this->currentValue = 0; this->newValue = 0; @@ -86,8 +86,11 @@ private: } }; + static Axis createAxis(const AxisInfo& AxisInfo, const RawAbsoluteAxisInfo& rawAxisInfo, + bool explicitlyMapped); + // Axes indexed by raw ABS_* axis index. - KeyedVector<int32_t, Axis> mAxes; + std::unordered_map<int32_t, Axis> mAxes; void sync(nsecs_t when, bool force); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index e0092210a0..8b9f235438 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -14,7 +14,9 @@ * limitations under the License. */ +// clang-format off #include "../Macros.h" +// clang-format on #include "KeyboardInputMapper.h" @@ -138,7 +140,7 @@ std::optional<DisplayViewport> KeyboardInputMapper::findViewport( // No associated display defined, try to find default display if orientationAware. if (mParameters.orientationAware) { - return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + return config->getDisplayViewportByType(ViewportType::INTERNAL); } return std::nullopt; @@ -388,11 +390,14 @@ void KeyboardInputMapper::updateMetaState(int32_t keyCode) { bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { int32_t oldMetaState = mMetaState; int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); - bool metaStateChanged = oldMetaState != newMetaState; + int32_t metaStateChanged = oldMetaState ^ newMetaState; if (metaStateChanged) { mMetaState = newMetaState; - updateLedState(false); - + constexpr int32_t allLedMetaState = + AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON; + if ((metaStateChanged & allLedMetaState) != 0) { + getContext()->updateLedMetaState(newMetaState & allLedMetaState); + } getContext()->updateGlobalMetaState(); } @@ -413,6 +418,26 @@ void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { } void KeyboardInputMapper::updateLedState(bool reset) { + mMetaState |= getContext()->getLedMetaState(); + + constexpr int32_t META_NUM = 3; + const std::array<int32_t, META_NUM> keyCodes = {AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK, + AKEYCODE_SCROLL_LOCK}; + const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON, + AMETA_SCROLL_LOCK_ON}; + std::array<uint8_t, META_NUM> flags = {0, 0, 0}; + bool hasKeyLayout = + getDeviceContext().markSupportedKeyCodes(META_NUM, keyCodes.data(), flags.data()); + // If the device doesn't have the physical meta key it shouldn't generate the corresponding + // meta state. + if (hasKeyLayout) { + for (int i = 0; i < META_NUM; i++) { + if (!flags[i]) { + mMetaState &= ~metaCodes[i]; + } + } + } + updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset); updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset); updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 0bdeded285..4c0b42a137 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -42,6 +42,7 @@ public: virtual int32_t getMetaState() override; virtual void updateMetaState(int32_t keyCode) override; virtual std::optional<int32_t> getAssociatedDisplayId() override; + virtual void updateLedState(bool reset); private: // The current viewport. @@ -93,7 +94,6 @@ private: void resetLedState(); void initializeLedState(LedState& ledState, int32_t led); - void updateLedState(bool reset); void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); std::optional<DisplayViewport> findViewport(nsecs_t when, const InputReaderConfiguration* config); diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index 98858897f4..594ff42f87 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -14,7 +14,9 @@ * limitations under the License. */ +// clang-format off #include "../Macros.h" +// clang-format on #include "RotaryEncoderInputMapper.h" @@ -66,7 +68,7 @@ void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfigur } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { std::optional<DisplayViewport> internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + config->getDisplayViewportByType(ViewportType::INTERNAL); if (internalViewport) { mOrientation = internalViewport->orientation; } else { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index cfc2919e7b..ea848355e0 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -18,6 +18,7 @@ #include "../Macros.h" // clang-format on +#include <input/NamedEnum.h> #include "TouchInputMapper.h" #include "CursorButtonAccumulator.h" @@ -170,6 +171,8 @@ TouchInputMapper::TouchInputMapper(InputDeviceContext& deviceContext) mRawSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0), + mSurfaceRight(0), + mSurfaceBottom(0), mPhysicalWidth(-1), mPhysicalHeight(-1), mPhysicalLeft(0), @@ -255,7 +258,8 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { } void TouchInputMapper::dump(std::string& dump) { - dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); + dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", + NamedEnum::string(mDeviceMode).c_str()); dumpParameters(dump); dumpVirtualKeys(dump); dumpRawPointerAxes(dump); @@ -343,22 +347,6 @@ void TouchInputMapper::dump(std::string& dump) { } } -const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { - switch (deviceMode) { - case DeviceMode::DISABLED: - return "disabled"; - case DeviceMode::DIRECT: - return "direct"; - case DeviceMode::UNSCALED: - return "unscaled"; - case DeviceMode::NAVIGATION: - return "navigation"; - case DeviceMode::POINTER: - return "pointer"; - } - return "unknown"; -} - void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { InputMapper::configure(when, config, changes); @@ -510,33 +498,9 @@ void TouchInputMapper::configureParameters() { void TouchInputMapper::dumpParameters(std::string& dump) { dump += INDENT3 "Parameters:\n"; - switch (mParameters.gestureMode) { - case Parameters::GestureMode::SINGLE_TOUCH: - dump += INDENT4 "GestureMode: single-touch\n"; - break; - case Parameters::GestureMode::MULTI_TOUCH: - dump += INDENT4 "GestureMode: multi-touch\n"; - break; - default: - assert(false); - } + dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n"; - switch (mParameters.deviceType) { - case Parameters::DeviceType::TOUCH_SCREEN: - dump += INDENT4 "DeviceType: touchScreen\n"; - break; - case Parameters::DeviceType::TOUCH_PAD: - dump += INDENT4 "DeviceType: touchPad\n"; - break; - case Parameters::DeviceType::TOUCH_NAVIGATION: - dump += INDENT4 "DeviceType: touchNavigation\n"; - break; - case Parameters::DeviceType::POINTER: - dump += INDENT4 "DeviceType: pointer\n"; - break; - default: - ALOG_ASSERT(false); - } + dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n"; dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " "displayId='%s'\n", @@ -606,18 +570,18 @@ std::optional<DisplayViewport> TouchInputMapper::findViewport() { ViewportType viewportTypeToUse; if (mParameters.associatedDisplayIsExternal) { - viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; + viewportTypeToUse = ViewportType::EXTERNAL; } else { - viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; + viewportTypeToUse = ViewportType::INTERNAL; } std::optional<DisplayViewport> viewport = mConfig.getDisplayViewportByType(viewportTypeToUse); - if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { + if (!viewport && viewportTypeToUse == ViewportType::EXTERNAL) { ALOGW("Input device %s should be associated with external display, " "fallback to internal one for the external viewport is not found.", getDeviceName().c_str()); - viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + viewport = mConfig.getDisplayViewportByType(ViewportType::INTERNAL); } return viewport; @@ -2622,14 +2586,14 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Update the velocity tracker. { - VelocityTracker::Position positions[MAX_POINTERS]; - uint32_t count = 0; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { + std::vector<VelocityTracker::Position> positions; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { uint32_t id = idBits.clearFirstMarkedBit(); const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - positions[count].x = pointer.x * mPointerXMovementScale; - positions[count].y = pointer.y * mPointerYMovementScale; + float x = pointer.x * mPointerXMovementScale; + float y = pointer.y * mPointerYMovementScale; + positions.push_back({x, y}); } mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, positions); @@ -3857,9 +3821,9 @@ void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) #if DEBUG_POINTER_ASSIGNMENT ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i, - heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance); + for (size_t j = 0; j < heapSize; j++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, j, + heap[j].currentPointerIndex, heap[j].lastPointerIndex, heap[j].distance); } #endif } diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp index 7665680feb..ac7c266dc5 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -39,23 +39,17 @@ void VibratorInputMapper::process(const RawEvent* rawEvent) { // TODO: Handle FF_STATUS, although it does not seem to be widely supported. } -void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, +void VibratorInputMapper::vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) { #if DEBUG_VIBRATOR std::string patternStr; - for (size_t i = 0; i < patternSize; i++) { - if (i != 0) { - patternStr += ", "; - } - patternStr += StringPrintf("%" PRId64, pattern[i]); - } + dumpPattern(patternStr); ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), patternStr.c_str(), repeat, token); #endif mVibrating = true; - memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); - mPatternSize = patternSize; + mPattern = pattern; mRepeat = repeat; mToken = token; mIndex = -1; @@ -85,7 +79,7 @@ void VibratorInputMapper::timeoutExpired(nsecs_t when) { void VibratorInputMapper::nextStep() { mIndex += 1; - if (size_t(mIndex) >= mPatternSize) { + if (size_t(mIndex) >= mPattern.size()) { if (mRepeat < 0) { // We are done. stopVibrating(); @@ -94,13 +88,14 @@ void VibratorInputMapper::nextStep() { mIndex = mRepeat; } - bool vibratorOn = mIndex & 1; - nsecs_t duration = mPattern[mIndex]; - if (vibratorOn) { + const VibrationElement& element = mPattern[mIndex]; + if (element.isOn()) { #if DEBUG_VIBRATOR - ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); + std::string description = element.toString(); + ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(), + description.c_str()); #endif - getDeviceContext().vibrate(duration); + getDeviceContext().vibrate(element); } else { #if DEBUG_VIBRATOR ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); @@ -108,10 +103,12 @@ void VibratorInputMapper::nextStep() { getDeviceContext().cancelVibrate(); } nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - mNextStepTime = now + duration; + std::chrono::nanoseconds duration = + std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration); + mNextStepTime = now + duration.count(); getContext()->requestTimeoutAtTime(mNextStepTime); #if DEBUG_VIBRATOR - ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); + ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count()); #endif } @@ -126,6 +123,25 @@ void VibratorInputMapper::stopVibrating() { void VibratorInputMapper::dump(std::string& dump) { dump += INDENT2 "Vibrator Input Mapper:\n"; dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); + if (mVibrating) { + dump += INDENT3 "Pattern: "; + dumpPattern(dump); + dump += "\n"; + dump += StringPrintf(INDENT3 "Repeat Index: %zd\n", mRepeat); + } +} + +void VibratorInputMapper::dumpPattern(std::string& dump) const { + dump += "["; + + for (auto it = mPattern.begin(); it != mPattern.end(); ++it) { + dump += it->toString(); + if (std::next(it) != mPattern.end()) { + dump += ", "; + } + } + + dump += "]"; } } // namespace android diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h index f69fdde22e..bfa5ec1bdc 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.h +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -30,7 +30,7 @@ public: virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; virtual void process(const RawEvent* rawEvent) override; - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + virtual void vibrate(const std::vector<VibrationElement>& pattern, ssize_t repeat, int32_t token) override; virtual void cancelVibrate(int32_t token) override; virtual void timeoutExpired(nsecs_t when) override; @@ -38,13 +38,13 @@ public: private: bool mVibrating; - nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; - size_t mPatternSize; + std::vector<VibrationElement> mPattern; ssize_t mRepeat; int32_t mToken; ssize_t mIndex; nsecs_t mNextStepTime; + void dumpPattern(std::string& dump) const; void nextStep(); void stopVibrating(); }; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index a0d2f4f172..8cb7194a80 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -30,12 +30,23 @@ cc_test { "AnrTracker_test.cpp", "BlockingQueue_test.cpp", "EventHub_test.cpp", - "TestInputListener.cpp", + "IInputFlingerQuery.aidl", "InputClassifier_test.cpp", "InputClassifierConverter_test.cpp", "InputDispatcher_test.cpp", "InputReader_test.cpp", + "InputFlingerService_test.cpp", + "TestInputListener.cpp", "UinputDevice.cpp", ], + aidl: { + include_dirs: [ + "frameworks/native/libs/input", + ], + }, + static_libs: [ + "libc++fs" + ], require_root: true, + test_suites: ["device-tests"], } diff --git a/services/inputflinger/tests/BlockingQueue_test.cpp b/services/inputflinger/tests/BlockingQueue_test.cpp index 0dea8d7861..fd9d9d5bd3 100644 --- a/services/inputflinger/tests/BlockingQueue_test.cpp +++ b/services/inputflinger/tests/BlockingQueue_test.cpp @@ -26,7 +26,7 @@ namespace android { // --- BlockingQueueTest --- /** - * Sanity check of basic pop and push operation. + * Validate basic pop and push operation. */ TEST(BlockingQueueTest, Queue_AddAndRemove) { constexpr size_t capacity = 10; diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp index 71731b046c..ef68a84fdb 100644 --- a/services/inputflinger/tests/EventHub_test.cpp +++ b/services/inputflinger/tests/EventHub_test.cpp @@ -199,3 +199,76 @@ TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) { lastEventTime = event.when; // Ensure all returned events are monotonic } } + +// --- BitArrayTest --- +class BitArrayTest : public testing::Test { +protected: + static constexpr size_t SINGLE_ELE_BITS = 32UL; + static constexpr size_t MULTI_ELE_BITS = 256UL; + + virtual void SetUp() override { + mBitmaskSingle.loadFromBuffer(mBufferSingle); + mBitmaskMulti.loadFromBuffer(mBufferMulti); + } + + android::BitArray<SINGLE_ELE_BITS> mBitmaskSingle; + android::BitArray<MULTI_ELE_BITS> mBitmaskMulti; + +private: + const typename android::BitArray<SINGLE_ELE_BITS>::Buffer mBufferSingle = { + 0x800F0F0FUL // bit 0 - 31 + }; + const typename android::BitArray<MULTI_ELE_BITS>::Buffer mBufferMulti = { + 0xFFFFFFFFUL, // bit 0 - 31 + 0x01000001UL, // bit 32 - 63 + 0x00000000UL, // bit 64 - 95 + 0x80000000UL, // bit 96 - 127 + 0x00000000UL, // bit 128 - 159 + 0x00000000UL, // bit 160 - 191 + 0x80000008UL, // bit 192 - 223 + 0x00000000UL, // bit 224 - 255 + }; +}; + +TEST_F(BitArrayTest, SetBit) { + ASSERT_TRUE(mBitmaskSingle.test(0)); + ASSERT_TRUE(mBitmaskSingle.test(31)); + ASSERT_FALSE(mBitmaskSingle.test(7)); + + ASSERT_TRUE(mBitmaskMulti.test(32)); + ASSERT_TRUE(mBitmaskMulti.test(56)); + ASSERT_FALSE(mBitmaskMulti.test(192)); + ASSERT_TRUE(mBitmaskMulti.test(223)); + ASSERT_FALSE(mBitmaskMulti.test(255)); +} + +TEST_F(BitArrayTest, AnyBit) { + ASSERT_TRUE(mBitmaskSingle.any(31, 32)); + ASSERT_FALSE(mBitmaskSingle.any(12, 16)); + + ASSERT_TRUE(mBitmaskMulti.any(31, 32)); + ASSERT_FALSE(mBitmaskMulti.any(33, 33)); + ASSERT_TRUE(mBitmaskMulti.any(32, 55)); + ASSERT_TRUE(mBitmaskMulti.any(33, 57)); + ASSERT_FALSE(mBitmaskMulti.any(33, 55)); + ASSERT_FALSE(mBitmaskMulti.any(130, 190)); + + ASSERT_FALSE(mBitmaskMulti.any(128, 195)); + ASSERT_TRUE(mBitmaskMulti.any(128, 196)); + ASSERT_TRUE(mBitmaskMulti.any(128, 224)); + ASSERT_FALSE(mBitmaskMulti.any(255, 256)); +} + +TEST_F(BitArrayTest, SetBit_InvalidBitIndex) { + ASSERT_FALSE(mBitmaskSingle.test(32)); + ASSERT_FALSE(mBitmaskMulti.test(256)); +} + +TEST_F(BitArrayTest, AnyBit_InvalidBitIndex) { + ASSERT_FALSE(mBitmaskSingle.any(32, 32)); + ASSERT_FALSE(mBitmaskSingle.any(33, 34)); + + ASSERT_FALSE(mBitmaskMulti.any(256, 256)); + ASSERT_FALSE(mBitmaskMulti.any(257, 258)); + ASSERT_FALSE(mBitmaskMulti.any(0, 0)); +} diff --git a/services/inputflinger/tests/IInputFlingerQuery.aidl b/services/inputflinger/tests/IInputFlingerQuery.aidl new file mode 100644 index 0000000000..5c8a8da612 --- /dev/null +++ b/services/inputflinger/tests/IInputFlingerQuery.aidl @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.FocusRequest; +import android.InputChannel; +import android.InputWindowInfo; +import android.os.ISetInputWindowsListener; + +/** @hide */ +interface IInputFlingerQuery +{ + /* Test interfaces */ + void getInputWindows(out InputWindowInfo[] inputHandles); + void getInputChannels(out InputChannel[] channels); + void getLastFocusRequest(out FocusRequest request); + void resetInputManager(); +} diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 668a7ab421..dc320036f1 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -29,6 +29,9 @@ #include <vector> using android::base::StringPrintf; +using android::os::InputEventInjectionResult; +using android::os::InputEventInjectionSync; +using namespace android::flag_operators; namespace android::inputdispatcher { @@ -123,16 +126,17 @@ public: // This function must be called soon after the expected ANR timer starts, // because we are also checking how much time has passed. - void assertNotifyAnrWasCalled(std::chrono::nanoseconds timeout, - const sp<InputApplicationHandle>& expectedApplication, - const sp<IBinder>& expectedToken) { - std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData; + void assertNotifyAnrWasCalled( + std::chrono::nanoseconds timeout, + const std::shared_ptr<InputApplicationHandle>& expectedApplication, + const sp<IBinder>& expectedToken) { + std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData; ASSERT_NO_FATAL_FAILURE(anrData = getNotifyAnrData(timeout)); ASSERT_EQ(expectedApplication, anrData.first); ASSERT_EQ(expectedToken, anrData.second); } - std::pair<sp<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData( + std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> getNotifyAnrData( std::chrono::nanoseconds timeout) { const std::chrono::time_point start = std::chrono::steady_clock::now(); std::unique_lock lock(mLock); @@ -161,7 +165,7 @@ public: << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count() << "ms instead"; } - std::pair<sp<InputApplicationHandle>, sp<IBinder>> result = + std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> result = std::make_pair(mAnrApplications.front(), mAnrWindowTokens.front()); mAnrApplications.pop(); mAnrWindowTokens.pop(); @@ -189,7 +193,7 @@ private: std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock); // ANR handling - std::queue<sp<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock); + std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock); std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock); std::condition_variable mNotifyAnr; std::chrono::nanoseconds mAnrTimeout = 0ms; @@ -199,19 +203,22 @@ private: mConfigurationChangedTime = when; } - virtual nsecs_t notifyAnr(const sp<InputApplicationHandle>& application, - const sp<IBinder>& windowToken, const std::string&) override { + std::chrono::nanoseconds notifyAnr(const std::shared_ptr<InputApplicationHandle>& application, + const sp<IBinder>& windowToken, + const std::string&) override { std::scoped_lock lock(mLock); mAnrApplications.push(application); mAnrWindowTokens.push(windowToken); mNotifyAnr.notify_all(); - return mAnrTimeout.count(); + return mAnrTimeout; } virtual void notifyInputChannelBroken(const sp<IBinder>&) override {} virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {} + virtual void notifyUntrustedTouch(const std::string& obscuringPackage) override {} + virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override { *outConfig = mConfig; } @@ -292,70 +299,6 @@ private: } }; -// --- HmacKeyManagerTest --- - -class HmacKeyManagerTest : public testing::Test { -protected: - HmacKeyManager mHmacKeyManager; -}; - -/** - * Ensure that separate calls to sign the same data are generating the same key. - * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance - * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky - * tests. - */ -TEST_F(HmacKeyManagerTest, GeneratedHmac_IsConsistent) { - KeyEvent event = getTestKeyEvent(); - VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event); - - std::array<uint8_t, 32> hmac1 = mHmacKeyManager.sign(verifiedEvent); - std::array<uint8_t, 32> hmac2 = mHmacKeyManager.sign(verifiedEvent); - ASSERT_EQ(hmac1, hmac2); -} - -/** - * Ensure that changes in VerifiedKeyEvent produce a different hmac. - */ -TEST_F(HmacKeyManagerTest, GeneratedHmac_ChangesWhenFieldsChange) { - KeyEvent event = getTestKeyEvent(); - VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event); - std::array<uint8_t, 32> initialHmac = mHmacKeyManager.sign(verifiedEvent); - - verifiedEvent.deviceId += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.source += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.eventTimeNanos += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.displayId += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.action += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.downTimeNanos += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.flags += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.keyCode += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.scanCode += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.metaState += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); - - verifiedEvent.repeatCount += 1; - ASSERT_NE(initialHmac, mHmacKeyManager.sign(verifiedEvent)); -} - // --- InputDispatcherTest --- class InputDispatcherTest : public testing::Test { @@ -390,6 +333,18 @@ protected: ALOGE("%s", to.c_str()); } } + + void setFocusedWindow(const sp<InputWindowHandle>& window, + const sp<InputWindowHandle>& focusedWindow = nullptr) { + FocusRequest request; + request.token = window->getToken(); + if (focusedWindow) { + request.focusedToken = focusedWindow->getToken(); + } + request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + request.displayId = window->getInfo()->displayId; + mDispatcher->setFocusedWindow(request); + } }; @@ -401,18 +356,18 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { INVALID_HMAC, /*action*/ -1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject key events with undefined action."; // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE, INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject key events with ACTION_MULTIPLE."; } @@ -432,113 +387,110 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { constexpr int32_t metaState = AMETA_NONE; constexpr MotionClassification classification = MotionClassification::NONE; + ui::Transform identityTransform; // Rejects undefined motion actions. event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, - /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, - 1 /* yScale */, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, + identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with undefined action."; // Rejects pointer down with invalid index. event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, + pointerCoords); + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with pointer down index too large."; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, + pointerCoords); + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with pointer down index too small."; // Rejects pointer up with invalid index. event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, + pointerCoords); + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with pointer up index too large."; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 1 /* xScale */, 1 /* yScale */, - 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, + pointerCoords); + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, - 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with 0 pointers."; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, - 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with more than MAX_POINTERS pointers."; // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, - 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, - 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with pointer ids greater than MAX_POINTER_ID."; // Rejects motion events with duplicate pointer ids. @@ -546,13 +498,12 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { pointerProperties[1].id = 1; event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, - 1 /* xScale */, 1 /* yScale */, 0, 0, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, - INPUT_EVENT_INJECTION_SYNC_NONE, 0ms, 0)) + InputEventInjectionSync::NONE, 0ms, 0)) << "Should reject motion events with duplicate pointer ids."; } @@ -586,7 +537,8 @@ public: FakeApplicationHandle() { mInfo.name = "Fake Application"; mInfo.token = new BBinder(); - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); + mInfo.dispatchingTimeoutMillis = + std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count(); } virtual ~FakeApplicationHandle() {} @@ -594,16 +546,16 @@ public: return true; } - void setDispatchingTimeout(std::chrono::nanoseconds timeout) { - mInfo.dispatchingTimeout = timeout.count(); + void setDispatchingTimeout(std::chrono::milliseconds timeout) { + mInfo.dispatchingTimeoutMillis = timeout.count(); } }; class FakeInputReceiver { public: - explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name) + explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name) : mName(name) { - mConsumer = std::make_unique<InputConsumer>(clientChannel); + mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel)); } InputEvent* consume() { @@ -748,51 +700,50 @@ public: static const int32_t WIDTH = 600; static const int32_t HEIGHT = 800; - FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle, + FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, const sp<InputDispatcher>& dispatcher, const std::string name, - int32_t displayId, sp<IBinder> token = nullptr) + int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt) : mName(name) { - if (token == nullptr) { - sp<InputChannel> serverChannel, clientChannel; - InputChannel::openInputChannelPair(name, serverChannel, clientChannel); - mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name); - dispatcher->registerInputChannel(serverChannel); - token = serverChannel->getConnectionToken(); + if (token == std::nullopt) { + base::Result<std::unique_ptr<InputChannel>> channel = + dispatcher->createInputChannel(name); + token = (*channel)->getConnectionToken(); + mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name); } inputApplicationHandle->updateInfo(); mInfo.applicationInfo = *inputApplicationHandle->getInfo(); - mInfo.token = token; + mInfo.token = *token; mInfo.id = sId++; mInfo.name = name; - mInfo.layoutParamsFlags = 0; - mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION; - mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT.count(); + mInfo.type = InputWindowInfo::Type::APPLICATION; + mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; mInfo.frameLeft = 0; mInfo.frameTop = 0; mInfo.frameRight = WIDTH; mInfo.frameBottom = HEIGHT; + mInfo.transform.set(0, 0); mInfo.globalScaleFactor = 1.0; mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT)); mInfo.visible = true; - mInfo.canReceiveKeys = true; - mInfo.hasFocus = false; + mInfo.focusable = false; mInfo.hasWallpaper = false; mInfo.paused = false; mInfo.ownerPid = INJECTOR_PID; mInfo.ownerUid = INJECTOR_UID; - mInfo.inputFeatures = 0; mInfo.displayId = displayId; } virtual bool updateInfo() { return true; } - void setFocus(bool hasFocus) { mInfo.hasFocus = hasFocus; } + void setFocusable(bool focusable) { mInfo.focusable = focusable; } + + void setVisible(bool visible) { mInfo.visible = visible; } void setDispatchingTimeout(std::chrono::nanoseconds timeout) { - mInfo.dispatchingTimeout = timeout.count(); + mInfo.dispatchingTimeout = timeout; } void setPaused(bool paused) { mInfo.paused = paused; } @@ -802,17 +753,21 @@ public: mInfo.frameTop = frame.top; mInfo.frameRight = frame.right; mInfo.frameBottom = frame.bottom; + mInfo.transform.set(frame.left, frame.top); mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(frame); } - void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; } + void setFlags(Flags<InputWindowInfo::Flag> flags) { mInfo.flags = flags; } - void setWindowScale(float xScale, float yScale) { - mInfo.windowXScale = xScale; - mInfo.windowYScale = yScale; + void setInputFeatures(InputWindowInfo::Feature features) { mInfo.inputFeatures = features; } + + void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) { + mInfo.transform.set(dsdx, dtdx, dtdy, dsdy); } + void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); } + void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) { consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId, expectedFlags); @@ -894,8 +849,12 @@ public: } void assertNoEvents() { - ASSERT_NE(mInputReceiver, nullptr) - << "Call 'assertNoEvents' on a window with an InputReceiver"; + if (mInputReceiver == nullptr && + mInfo.inputFeatures.test(InputWindowInfo::Feature::NO_INPUT_CHANNEL)) { + return; // Can't receive events if the window does not have input channel + } + ASSERT_NE(nullptr, mInputReceiver) + << "Window without InputReceiver must specify feature NO_INPUT_CHANNEL"; mInputReceiver->assertNoEvents(); } @@ -911,10 +870,11 @@ private: std::atomic<int32_t> FakeWindowHandle::sId{1}; -static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount, - int32_t displayId = ADISPLAY_ID_NONE, - int32_t syncMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, - std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) { +static InputEventInjectionResult injectKey( + const sp<InputDispatcher>& dispatcher, int32_t action, int32_t repeatCount, + int32_t displayId = ADISPLAY_ID_NONE, + InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT, + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT) { KeyEvent event; nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); @@ -929,59 +889,167 @@ static int32_t injectKey(const sp<InputDispatcher>& dispatcher, int32_t action, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static InputEventInjectionResult injectKeyDown(const sp<InputDispatcher>& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /* repeatCount */ 0, displayId); } -static int32_t injectKeyUp(const sp<InputDispatcher>& dispatcher, - int32_t displayId = ADISPLAY_ID_NONE) { +static InputEventInjectionResult injectKeyUp(const sp<InputDispatcher>& dispatcher, + int32_t displayId = ADISPLAY_ID_NONE) { return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /* repeatCount */ 0, displayId); } -static int32_t injectMotionEvent( +class PointerBuilder { +public: + PointerBuilder(int32_t id, int32_t toolType) { + mProperties.clear(); + mProperties.id = id; + mProperties.toolType = toolType; + mCoords.clear(); + } + + PointerBuilder& x(float x) { return axis(AMOTION_EVENT_AXIS_X, x); } + + PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); } + + PointerBuilder& axis(int32_t axis, float value) { + mCoords.setAxisValue(axis, value); + return *this; + } + + PointerProperties buildProperties() const { return mProperties; } + + PointerCoords buildCoords() const { return mCoords; } + +private: + PointerProperties mProperties; + PointerCoords mCoords; +}; + +class MotionEventBuilder { +public: + MotionEventBuilder(int32_t action, int32_t source) { + mAction = action; + mSource = source; + mEventTime = systemTime(SYSTEM_TIME_MONOTONIC); + } + + MotionEventBuilder& eventTime(nsecs_t eventTime) { + mEventTime = eventTime; + return *this; + } + + MotionEventBuilder& displayId(int32_t displayId) { + mDisplayId = displayId; + return *this; + } + + MotionEventBuilder& actionButton(int32_t actionButton) { + mActionButton = actionButton; + return *this; + } + + MotionEventBuilder& buttonState(int32_t actionButton) { + mActionButton = actionButton; + return *this; + } + + MotionEventBuilder& rawXCursorPosition(float rawXCursorPosition) { + mRawXCursorPosition = rawXCursorPosition; + return *this; + } + + MotionEventBuilder& rawYCursorPosition(float rawYCursorPosition) { + mRawYCursorPosition = rawYCursorPosition; + return *this; + } + + MotionEventBuilder& pointer(PointerBuilder pointer) { + mPointers.push_back(pointer); + return *this; + } + + MotionEvent build() { + std::vector<PointerProperties> pointerProperties; + std::vector<PointerCoords> pointerCoords; + for (const PointerBuilder& pointer : mPointers) { + pointerProperties.push_back(pointer.buildProperties()); + pointerCoords.push_back(pointer.buildCoords()); + } + + // Set mouse cursor position for the most common cases to avoid boilerplate. + if (mSource == AINPUT_SOURCE_MOUSE && + !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) && + mPointers.size() == 1) { + mRawXCursorPosition = pointerCoords[0].getX(); + mRawYCursorPosition = pointerCoords[0].getY(); + } + + MotionEvent event; + ui::Transform identityTransform; + event.initialize(InputEvent::nextId(), DEVICE_ID, mSource, mDisplayId, INVALID_HMAC, + mAction, mActionButton, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, + mButtonState, MotionClassification::NONE, identityTransform, + /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition, + mRawYCursorPosition, mEventTime, mEventTime, mPointers.size(), + pointerProperties.data(), pointerCoords.data()); + + return event; + } + +private: + int32_t mAction; + int32_t mSource; + nsecs_t mEventTime; + int32_t mDisplayId{ADISPLAY_ID_DEFAULT}; + int32_t mActionButton{0}; + int32_t mButtonState{0}; + float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; + float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; + + std::vector<PointerBuilder> mPointers; +}; + +static InputEventInjectionResult injectMotionEvent( + const sp<InputDispatcher>& dispatcher, const MotionEvent& event, + std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, + InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT) { + return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode, + injectionTimeout, + POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); +} + +static InputEventInjectionResult injectMotionEvent( const sp<InputDispatcher>& dispatcher, int32_t action, int32_t source, int32_t displayId, const PointF& position, const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION}, std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT, - int32_t injectionMode = INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, + InputEventInjectionSync injectionMode = InputEventInjectionSync::WAIT_FOR_RESULT, nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC)) { - MotionEvent event; - PointerProperties pointerProperties[1]; - PointerCoords pointerCoords[1]; - - pointerProperties[0].clear(); - pointerProperties[0].id = 0; - pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - - pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, position.x); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, position.y); - - // Define a valid motion down event. - event.initialize(InputEvent::nextId(), DEVICE_ID, source, displayId, INVALID_HMAC, action, - /* actionButton */ 0, - /* flags */ 0, - /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - /* xScale */ 1, /* yScale */ 1, /* xOffset */ 0, /* yOffset */ 0, - /* xPrecision */ 0, /* yPrecision */ 0, cursorPosition.x, cursorPosition.y, - eventTime, eventTime, - /*pointerCount*/ 1, pointerProperties, pointerCoords); + MotionEvent event = MotionEventBuilder(action, source) + .displayId(displayId) + .eventTime(eventTime) + .rawXCursorPosition(cursorPosition.x) + .rawYCursorPosition(cursorPosition.y) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(position.x) + .y(position.y)) + .build(); // Inject event until dispatch out. - return dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, injectionMode, - injectionTimeout, - POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); + return injectMotionEvent(dispatcher, event); } -static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source, - int32_t displayId, const PointF& location = {100, 200}) { +static InputEventInjectionResult injectMotionDown(const sp<InputDispatcher>& dispatcher, + int32_t source, int32_t displayId, + const PointF& location = {100, 200}) { return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location); } -static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source, - int32_t displayId, const PointF& location = {100, 200}) { +static InputEventInjectionResult injectMotionUp(const sp<InputDispatcher>& dispatcher, + int32_t source, int32_t displayId, + const PointF& location = {100, 200}) { return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location); } @@ -1033,14 +1101,14 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 } TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Window should receive motion event. window->consumeMotionDown(ADISPLAY_ID_DEFAULT); @@ -1055,17 +1123,17 @@ TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { * called twice. */ TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFrame(Rect(0, 0, 100, 100)); - window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Window should receive motion event. window->consumeMotionDown(ADISPLAY_ID_DEFAULT); @@ -1078,18 +1146,18 @@ TEST_F(InputDispatcherTest, SetInputWindowOnce_SingleWindowTouch) { * when finding touched windows. */ TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->setFrame(Rect(0, 0, 100, 100)); - window->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Window should receive motion event. window->consumeMotionDown(ADISPLAY_ID_DEFAULT); @@ -1097,107 +1165,225 @@ TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) { // The foreground window should receive the first touch down event. TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Top window should receive the touch down event. Second window should not receive anything. windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowSecond->assertNoEvents(); } -TEST_F(InputDispatcherTest, SetInputWindow_FocusedWindow) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); - sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top", - ADISPLAY_ID_DEFAULT); - sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", - ADISPLAY_ID_DEFAULT); - - // Set focused application. - mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - - // Display should have only one focused window - windowSecond->setFocus(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); - - windowSecond->consumeFocusEvent(true); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) - << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - - // Focused window should receive event. - windowTop->assertNoEvents(); - windowSecond->consumeKeyDown(ADISPLAY_ID_NONE); -} - -TEST_F(InputDispatcherTest, SetInputWindow_FocusPriority) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); - sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top", - ADISPLAY_ID_DEFAULT); - sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", - ADISPLAY_ID_DEFAULT); +TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> windowLeft = + new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + windowLeft->setFrame(Rect(0, 0, 600, 800)); + windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); + sp<FakeWindowHandle> windowRight = + new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + windowRight->setFrame(Rect(600, 0, 1200, 800)); + windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); - // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - // Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first) - windowTop->setFocus(true); - windowSecond->setFocus(true); - - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); - windowTop->consumeFocusEvent(true); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) - << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; - - // Top focused window should receive event. - windowTop->consumeKeyDown(ADISPLAY_ID_NONE); - windowSecond->assertNoEvents(); -} + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowLeft, windowRight}}}); -TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + // Start cursor position in right window so that we can move the cursor to left window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(900) + .y(400)) + .build())); + windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + // Move cursor into left window + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + // Inject a series of mouse events for a mouse click + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT); - sp<FakeWindowHandle> windowTop = new FakeWindowHandle(application, mDispatcher, "Top", - ADISPLAY_ID_DEFAULT); - sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second", - ADISPLAY_ID_DEFAULT); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, + AINPUT_SOURCE_MOUSE) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, + AINPUT_SOURCE_MOUSE) + .buttonState(0) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) + .buttonState(0) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + windowLeft->consumeMotionUp(ADISPLAY_ID_DEFAULT); + + // Move mouse cursor back to right window + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, + AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(900) + .y(400)) + .build())); + windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + windowRight->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_MOVE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); +} + +// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected +// directly in this test. +TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 1200, 800)); + window->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); - // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - windowTop->setFocus(true); - windowSecond->setFocus(true); - // Release channel for window is no longer valid. - windowTop->releaseChannel(); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); - windowSecond->consumeFocusEvent(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - // Test inject a key down, should dispatch to a valid window. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) - << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, + AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_ENTER, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + // Inject a series of mouse events for a mouse click + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - // Top window is invalid, so it should not receive any input event. - windowTop->assertNoEvents(); - windowSecond->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, + AINPUT_SOURCE_MOUSE) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_PRESS, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_RELEASE, + AINPUT_SOURCE_MOUSE) + .buttonState(0) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_BUTTON_RELEASE, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) + .buttonState(0) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + window->consumeMotionUp(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, + AINPUT_SOURCE_MOUSE) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(300) + .y(400)) + .build())); + window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_HOVER_EXIT, + ADISPLAY_ID_DEFAULT, 0 /* expectedFlag */); } TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> windowLeft = new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); windowLeft->setFrame(Rect(0, 0, 600, 800)); - windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + windowLeft->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); sp<FakeWindowHandle> windowRight = new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); windowRight->setFrame(Rect(600, 0, 1200, 800)); - windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + windowRight->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -1205,7 +1391,7 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { // Inject an event with coordinate in the area of right window, with mouse cursor in the area of // left window. This event should be dispatched to the left window. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400})); windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT); @@ -1213,12 +1399,14 @@ TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { } TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - window->setFocus(true); + window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); @@ -1236,7 +1424,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) { } TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); @@ -1259,7 +1447,7 @@ TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) { } TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); // Create a couple of windows sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher, @@ -1294,7 +1482,7 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_OnePointer) { } TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); PointF touchPoint = {10, 10}; @@ -1350,21 +1538,21 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointerNoSplitTouch) { } TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); // Create a non touch modal window that supports split touch sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); firstWindow->setFrame(Rect(0, 0, 600, 400)); - firstWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL - | InputWindowInfo::FLAG_SPLIT_TOUCH); + firstWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); // Create a non touch modal window that supports split touch sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); secondWindow->setFrame(Rect(0, 400, 600, 800)); - secondWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL - | InputWindowInfo::FLAG_SPLIT_TOUCH); + secondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); // Add the windows to the dispatcher mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); @@ -1414,12 +1602,13 @@ TEST_F(InputDispatcherTest, TransferTouchFocus_TwoPointersSplitTouch) { } TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - window->setFocus(true); + window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); window->consumeFocusEvent(true); @@ -1431,7 +1620,7 @@ TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { } TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); @@ -1446,7 +1635,7 @@ TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) // If a window is touchable, but does not have focus, it should receive motion events, but not keys TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); @@ -1470,10 +1659,9 @@ class FakeMonitorReceiver { public: FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId, bool isGestureMonitor = false) { - sp<InputChannel> serverChannel, clientChannel; - InputChannel::openInputChannelPair(name, serverChannel, clientChannel); - mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name); - dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor); + base::Result<std::unique_ptr<InputChannel>> channel = + dispatcher->createInputMonitor(displayId, isGestureMonitor, name); + mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name); } sp<IBinder> getToken() { return mInputReceiver->getToken(); } @@ -1505,7 +1693,7 @@ private: // Tests for gesture monitors TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -1513,35 +1701,37 @@ TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; window->consumeMotionDown(ADISPLAY_ID_DEFAULT); monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); } TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocus(true); + window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true); FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) - << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; window->consumeKeyDown(ADISPLAY_ID_DEFAULT); monitor.assertNoEvents(); } TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -1549,9 +1739,9 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; window->consumeMotionDown(ADISPLAY_ID_DEFAULT); monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); @@ -1559,9 +1749,9 @@ TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStrea mDispatcher->pilferPointers(monitor.getToken()); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); } @@ -1570,7 +1760,7 @@ TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); std::optional<uint32_t> consumeSeq = monitor.receiveEvent(); ASSERT_TRUE(consumeSeq); @@ -1581,7 +1771,7 @@ TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { } TEST_F(InputDispatcherTest, TestMoveEvent) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); @@ -1612,52 +1802,58 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { * and the action of enabling / disabling. */ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocus(true); + window->setFocusable(true); SCOPED_TRACE("Check default value of touch mode"); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Remove the window to trigger focus loss"); - window->setFocus(false); + window->setFocusable(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Disable touch mode"); mDispatcher->setInTouchMode(false); - window->setFocus(true); + window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); window->consumeFocusEvent(true /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Remove the window to trigger focus loss"); - window->setFocus(false); + window->setFocusable(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Enable touch mode again"); mDispatcher->setInTouchMode(true); - window->setFocus(true); + window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); window->assertNoEvents(); } TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocus(true); + window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true /*hasFocus*/, true /*inTouchMode*/); NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN); @@ -1687,7 +1883,7 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { } TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); @@ -1723,12 +1919,214 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState); } +/** + * Ensure that separate calls to sign the same data are generating the same key. + * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance + * that a specific key and data combination would produce INVALID_HMAC, which would cause flaky + * tests. + */ +TEST_F(InputDispatcherTest, GeneratedHmac_IsConsistent) { + KeyEvent event = getTestKeyEvent(); + VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event); + + std::array<uint8_t, 32> hmac1 = mDispatcher->sign(verifiedEvent); + std::array<uint8_t, 32> hmac2 = mDispatcher->sign(verifiedEvent); + ASSERT_EQ(hmac1, hmac2); +} + +/** + * Ensure that changes in VerifiedKeyEvent produce a different hmac. + */ +TEST_F(InputDispatcherTest, GeneratedHmac_ChangesWhenFieldsChange) { + KeyEvent event = getTestKeyEvent(); + VerifiedKeyEvent verifiedEvent = verifiedKeyEventFromKeyEvent(event); + std::array<uint8_t, 32> initialHmac = mDispatcher->sign(verifiedEvent); + + verifiedEvent.deviceId += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.source += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.eventTimeNanos += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.displayId += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.action += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.downTimeNanos += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.flags += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.keyCode += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.scanCode += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.metaState += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); + + verifiedEvent.repeatCount += 1; + ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent)); +} + +TEST_F(InputDispatcherTest, SetFocusedWindow) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> windowTop = + new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> windowSecond = + new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + // Top window is also focusable but is not granted focus. + windowTop->setFocusable(true); + windowSecond->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); + setFocusedWindow(windowSecond); + + windowSecond->consumeFocusEvent(true); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + + // Focused window should receive event. + windowSecond->consumeKeyDown(ADISPLAY_ID_NONE); + windowTop->assertNoEvents(); +} + +TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestInvalidChannel) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + window->setFocusable(true); + // Release channel for window is no longer valid. + window->releaseChannel(); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + + // Test inject a key down, should timeout. + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::TIMED_OUT"; + + // window channel is invalid, so it should not receive any input event. + window->assertNoEvents(); +} + +TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestNoFocusableWindow) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + // Window is not focusable. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + setFocusedWindow(window); + + // Test inject a key down, should timeout. + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::TIMED_OUT"; + + // window is invalid, so it should not receive any input event. + window->assertNoEvents(); +} + +TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> windowTop = + new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> windowSecond = + new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + windowTop->setFocusable(true); + windowSecond->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); + setFocusedWindow(windowTop); + windowTop->consumeFocusEvent(true); + + setFocusedWindow(windowSecond, windowTop); + windowSecond->consumeFocusEvent(true); + windowTop->consumeFocusEvent(false); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + + // Focused window should receive event. + windowSecond->consumeKeyDown(ADISPLAY_ID_NONE); +} + +TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> windowTop = + new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> windowSecond = + new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + windowTop->setFocusable(true); + windowSecond->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}}); + setFocusedWindow(windowSecond, windowTop); + + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::TIMED_OUT"; + + // Event should be dropped. + windowTop->assertNoEvents(); + windowSecond->assertNoEvents(); +} + +TEST_F(InputDispatcherTest, SetFocusedWindow_DeferInvisibleWindow) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> previousFocusedWindow = + new FakeWindowHandle(application, mDispatcher, "previousFocusedWindow", + ADISPLAY_ID_DEFAULT); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + window->setFocusable(true); + previousFocusedWindow->setFocusable(true); + window->setVisible(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, previousFocusedWindow}}}); + setFocusedWindow(previousFocusedWindow); + previousFocusedWindow->consumeFocusEvent(true); + + // Requesting focus on invisible window takes focus from currently focused window. + setFocusedWindow(window); + previousFocusedWindow->consumeFocusEvent(false); + + // Injected key goes to pending queue. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, + ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE)); + + // Window does not get focus event or key down. + window->assertNoEvents(); + + // Window becomes visible. + window->setVisible(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + // Window receives focus event. + window->consumeFocusEvent(true); + // Focused window receives key down. + window->consumeKeyDown(ADISPLAY_ID_DEFAULT); +} + class InputDispatcherKeyRepeatTest : public InputDispatcherTest { protected: static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms static constexpr nsecs_t KEY_REPEAT_DELAY = 40 * 1000000; // 40 ms - sp<FakeApplicationHandle> mApp; + std::shared_ptr<FakeApplicationHandle> mApp; sp<FakeWindowHandle> mWindow; virtual void SetUp() override { @@ -1742,17 +2140,18 @@ protected: } void setUpWindow() { - mApp = new FakeApplicationHandle(); + mApp = std::make_shared<FakeApplicationHandle>(); mWindow = new FakeWindowHandle(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - mWindow->setFocus(true); + mWindow->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); - + setFocusedWindow(mWindow); mWindow->consumeFocusEvent(true); } - void sendAndConsumeKeyDown() { + void sendAndConsumeKeyDown(int32_t deviceId) { NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + keyArgs.deviceId = deviceId; keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event mDispatcher->notifyKey(&keyArgs); @@ -1774,8 +2173,9 @@ protected: EXPECT_EQ(repeatCount, repeatKeyEvent->getRepeatCount()); } - void sendAndConsumeKeyUp() { + void sendAndConsumeKeyUp(int32_t deviceId) { NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + keyArgs.deviceId = deviceId; keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event mDispatcher->notifyKey(&keyArgs); @@ -1786,21 +2186,59 @@ protected: }; TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) { - sendAndConsumeKeyDown(); + sendAndConsumeKeyDown(1 /* deviceId */); + for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { + expectKeyRepeatOnce(repeatCount); + } +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeatFromTwoDevices) { + sendAndConsumeKeyDown(1 /* deviceId */); + for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { + expectKeyRepeatOnce(repeatCount); + } + sendAndConsumeKeyDown(2 /* deviceId */); + /* repeatCount will start from 1 for deviceId 2 */ for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { expectKeyRepeatOnce(repeatCount); } } TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterUp) { - sendAndConsumeKeyDown(); + sendAndConsumeKeyDown(1 /* deviceId */); expectKeyRepeatOnce(1 /*repeatCount*/); - sendAndConsumeKeyUp(); + sendAndConsumeKeyUp(1 /* deviceId */); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatAfterStaleDeviceKeyUp) { + sendAndConsumeKeyDown(1 /* deviceId */); + expectKeyRepeatOnce(1 /*repeatCount*/); + sendAndConsumeKeyDown(2 /* deviceId */); + expectKeyRepeatOnce(1 /*repeatCount*/); + // Stale key up from device 1. + sendAndConsumeKeyUp(1 /* deviceId */); + // Device 2 is still down, keep repeating + expectKeyRepeatOnce(2 /*repeatCount*/); + expectKeyRepeatOnce(3 /*repeatCount*/); + // Device 2 key up + sendAndConsumeKeyUp(2 /* deviceId */); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_KeyRepeatStopsAfterRepeatingKeyUp) { + sendAndConsumeKeyDown(1 /* deviceId */); + expectKeyRepeatOnce(1 /*repeatCount*/); + sendAndConsumeKeyDown(2 /* deviceId */); + expectKeyRepeatOnce(1 /*repeatCount*/); + // Device 2 which holds the key repeating goes up, expect the repeating to stop. + sendAndConsumeKeyUp(2 /* deviceId */); + // Device 1 still holds key down, but the repeating was already stopped mWindow->assertNoEvents(); } TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) { - sendAndConsumeKeyDown(); + sendAndConsumeKeyDown(1 /* deviceId */); for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { InputEvent* repeatEvent = mWindow->consume(); ASSERT_NE(nullptr, repeatEvent) << "Didn't receive event with repeat count " << repeatCount; @@ -1810,7 +2248,7 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFrom } TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) { - sendAndConsumeKeyDown(); + sendAndConsumeKeyDown(1 /* deviceId */); std::unordered_set<int32_t> idSet; for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { @@ -1829,17 +2267,18 @@ public: virtual void SetUp() override { InputDispatcherTest::SetUp(); - application1 = new FakeApplicationHandle(); + application1 = std::make_shared<FakeApplicationHandle>(); windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1", ADISPLAY_ID_DEFAULT); // Set focus window for primary display, but focused display would be second one. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1); - windowInPrimary->setFocus(true); + windowInPrimary->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary}}}); + setFocusedWindow(windowInPrimary); windowInPrimary->consumeFocusEvent(true); - application2 = new FakeApplicationHandle(); + application2 = std::make_shared<FakeApplicationHandle>(); windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2", SECOND_DISPLAY_ID); // Set focus to second display window. @@ -1847,66 +2286,67 @@ public: mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID); // Set focus window for second display. mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2); - windowInSecondary->setFocus(true); + windowInSecondary->setFocusable(true); mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {windowInSecondary}}}); + setFocusedWindow(windowInSecondary); windowInSecondary->consumeFocusEvent(true); } virtual void TearDown() override { InputDispatcherTest::TearDown(); - application1.clear(); + application1.reset(); windowInPrimary.clear(); - application2.clear(); + application2.reset(); windowInSecondary.clear(); } protected: - sp<FakeApplicationHandle> application1; + std::shared_ptr<FakeApplicationHandle> application1; sp<FakeWindowHandle> windowInPrimary; - sp<FakeApplicationHandle> application2; + std::shared_ptr<FakeApplicationHandle> application2; sp<FakeWindowHandle> windowInSecondary; }; TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) { // Test touch down on primary display. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); windowInSecondary->assertNoEvents(); // Test touch down on second display. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->assertNoEvents(); windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); } TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) { // Test inject a key down with display id specified. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) - << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT); windowInSecondary->assertNoEvents(); // Test inject a key down without display id specified. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) - << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->assertNoEvents(); windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); // Remove all windows in secondary display. mDispatcher->setInputWindows({{SECOND_DISPLAY_ID, {}}}); - // Expect old focus should receive a cancel event. + // Old focus should receive a cancel event. windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE, AKEY_EVENT_FLAG_CANCELED); // Test inject a key down, should timeout because of no target window. - ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, injectKeyDown(mDispatcher)) - << "Inject key event should return INPUT_EVENT_INJECTION_TIMED_OUT"; + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::TIMED_OUT"; windowInPrimary->assertNoEvents(); windowInSecondary->consumeFocusEvent(false); windowInSecondary->assertNoEvents(); @@ -1920,18 +2360,18 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); // Test touch down on primary display. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT); monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT); windowInSecondary->assertNoEvents(); monitorInSecondary.assertNoEvents(); // Test touch down on second display. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->assertNoEvents(); monitorInPrimary.assertNoEvents(); windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); @@ -1940,9 +2380,9 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { // Test inject a non-pointer motion event. // If specific a display, it will dispatch to the focused window of particular display, // or it will dispatch to the focused window of focused display. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher, - AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->assertNoEvents(); monitorInPrimary.assertNoEvents(); windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE); @@ -1958,14 +2398,31 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) { FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID); // Test inject a key down. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)) - << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; windowInPrimary->assertNoEvents(); monitorInPrimary.assertNoEvents(); windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE); monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE); } +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) { + sp<FakeWindowHandle> secondWindowInPrimary = + new FakeWindowHandle(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT); + secondWindowInPrimary->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowInPrimary, secondWindowInPrimary}}}); + setFocusedWindow(secondWindowInPrimary); + windowInPrimary->consumeFocusEvent(false); + secondWindowInPrimary->consumeFocusEvent(true); + + // Test inject a key down. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + windowInPrimary->assertNoEvents(); + windowInSecondary->assertNoEvents(); + secondWindowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT); +} + class InputFilterTest : public InputDispatcherTest { protected: static constexpr int32_t SECOND_DISPLAY_ID = 1; @@ -2043,25 +2500,27 @@ class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = + std::make_shared<FakeApplicationHandle>(); mUnfocusedWindow = new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30)); // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this // window. - mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); mFocusedWindow = new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT); mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); - mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mFocusedWindow->setFocus(true); + mFocusedWindow->setFocusable(true); // Expect one focus window exist in display. mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + setFocusedWindow(mFocusedWindow); mFocusedWindow->consumeFocusEvent(true); } @@ -2082,10 +2541,10 @@ protected: // DOWN on the window that doesn't have focus. Ensure the window that didn't have focus received // the onPointerDownOutsideFocus callback. TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {20, 20})) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; mUnfocusedWindow->consumeMotionDown(); ASSERT_TRUE(mDispatcher->waitForIdle()); @@ -2096,9 +2555,9 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Succe // DOWN on the window that doesn't have focus. Ensure no window received the // onPointerDownOutsideFocus callback. TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, {20, 20})) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; mFocusedWindow->consumeMotionDown(); ASSERT_TRUE(mDispatcher->waitForIdle()); @@ -2108,8 +2567,8 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPo // Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't // have focus. Ensure no window received the onPointerDownOutsideFocus callback. TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) - << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); ASSERT_TRUE(mDispatcher->waitForIdle()); @@ -2121,10 +2580,10 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMo // onPointerDownOutsideFocus callback. TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, FOCUSED_WINDOW_TOUCH_POINT)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; mFocusedWindow->consumeMotionDown(); ASSERT_TRUE(mDispatcher->waitForIdle()); @@ -2137,19 +2596,20 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); - sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> application = + std::make_shared<FakeApplicationHandle>(); mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1", ADISPLAY_ID_DEFAULT); // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window. // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows. - mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | - InputWindowInfo::FLAG_SPLIT_TOUCH); + mWindow1->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); mWindow1->setFrame(Rect(0, 0, 100, 100)); mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2", ADISPLAY_ID_DEFAULT, mWindow1->getToken()); - mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | - InputWindowInfo::FLAG_SPLIT_TOUCH); + mWindow2->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); mWindow2->setFrame(Rect(100, 100, 200, 200)); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow1, mWindow2}}}); @@ -2161,9 +2621,8 @@ protected: // Helper function to convert the point from screen coordinates into the window's space static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) { - float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft); - float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop); - return {x, y}; + vec2 vals = windowInfo->transform.transform(point.x, point.y); + return {vals.x, vals.y}; } void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction, @@ -2193,133 +2652,123 @@ protected: << ", got " << motionEvent.getY(i); } } + + void touchAndAssertPositions(int32_t action, std::vector<PointF> touchedPoints, + std::vector<PointF> expectedPoints) { + NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, touchedPoints); + mDispatcher->notifyMotion(&motionArgs); + + // Always consume from window1 since it's the window that has the InputReceiver + consumeMotionEvent(mWindow1, action, expectedPoints); + } }; TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) { // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {touchedPoint}); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); // Release touch on Window 1 - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {touchedPoint}); - mDispatcher->notifyMotion(&motionArgs); - // consume the UP event - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); + touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); - - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {touchedPoint}); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); } -TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) { +TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) { + // Set scale value for window2 mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {touchedPoint}); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); - + touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); // Release touch on Window 1 - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {touchedPoint}); - mDispatcher->notifyMotion(&motionArgs); - // consume the UP event - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint}); + touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); + touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, {touchedPoint}); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint}); + // Update the transform so rotation is set + mWindow2->setWindowTransform(0, -1, 1, 0); + expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); + touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); } -TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) { +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) { mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + touchedPoints.push_back(PointF{150, 150}); + expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints); - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); + // Release Window 2 + int32_t actionPointerUp = + AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints); + expectedPoints.pop_back(); - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + // Update the transform so rotation is set for Window 2 + mWindow2->setWindowTransform(0, -1, 1, 0); + expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints); } -TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) { +TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) { mWindow2->setWindowScale(0.5f, 0.5f); // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); + touchedPoints.push_back(PointF{150, 150}); + expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); + touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); + // Release Window 2 + int32_t actionPointerUp = + AMOTION_EVENT_ACTION_POINTER_UP + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + touchAndAssertPositions(actionPointerUp, touchedPoints, expectedPoints); + expectedPoints.pop_back(); + + // Touch Window 2 + mWindow2->setWindowTransform(0, -1, 1, 0); + expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints); + + // Move both windows + touchedPoints = {{20, 20}, {175, 175}}; + expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), + getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; + + touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { @@ -2328,57 +2777,44 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - - NotifyMotionArgs motionArgs = - generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints); + touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 int32_t actionPointerDown = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - touchedPoints.emplace_back(PointF{150, 150}); - expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); + touchedPoints.push_back(PointF{150, 150}); + expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - // Consuming from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints); + touchAndAssertPositions(actionPointerDown, touchedPoints, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, - ADISPLAY_ID_DEFAULT, touchedPoints); - mDispatcher->notifyMotion(&motionArgs); - - consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints); + touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); } class InputDispatcherSingleWindowAnr : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); - mApplication = new FakeApplicationHandle(); + mApplication = std::make_shared<FakeApplicationHandle>(); mApplication->setDispatchingTimeout(20ms); mWindow = new FakeWindowHandle(mApplication, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); mWindow->setFrame(Rect(0, 0, 30, 30)); - mWindow->setDispatchingTimeout(10ms); - mWindow->setFocus(true); + mWindow->setDispatchingTimeout(30ms); + mWindow->setFocusable(true); // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this // window. - mWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + setFocusedWindow(mWindow); mWindow->consumeFocusEvent(true); } @@ -2388,15 +2824,15 @@ class InputDispatcherSingleWindowAnr : public InputDispatcherTest { } protected: - sp<FakeApplicationHandle> mApplication; + std::shared_ptr<FakeApplicationHandle> mApplication; sp<FakeWindowHandle> mWindow; static constexpr PointF WINDOW_LOCATION = {20, 20}; void tapOnWindow() { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); } @@ -2413,17 +2849,37 @@ TEST_F(InputDispatcherSingleWindowAnr, WhenTouchIsConsumed_NoAnr) { // Send a regular key and respond, which should not cause an ANR. TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)); mWindow->consumeKeyDown(ADISPLAY_ID_NONE); ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertNotifyAnrWasNotCalled(); } +TEST_F(InputDispatcherSingleWindowAnr, WhenFocusedApplicationChanges_NoAnr) { + mWindow->setFocusable(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); + mWindow->consumeFocusEvent(false); + + InputEventInjectionResult result = + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, + InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); + // Key will not go to window because we have no focused window. + // The 'no focused window' ANR timer should start instead. + + // Now, the focused application goes away. + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, nullptr); + // The key should get dropped and there should be no ANR. + + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyAnrWasNotCalled(); +} + // Send an event to the app and have the app not respond right away. // When ANR is raised, policy will tell the dispatcher to cancel the events for that window. // So InputDispatcher will enqueue ACTION_CANCEL event as well. TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); @@ -2442,7 +2898,7 @@ TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { // Send a key to the app and have the app not respond right away. TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { // Inject a key, and don't respond - expect that ANR is called. - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)); std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); @@ -2452,12 +2908,12 @@ TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { // We have a focused application, but no focused window TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) { - mWindow->setFocus(false); + mWindow->setFocusable(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); mWindow->consumeFocusEvent(false); // taps on the window work as normal - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown()); @@ -2467,10 +2923,10 @@ TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) { // Once a focused event arrives, we get an ANR for this application // We specify the injection timeout to be smaller than the application timeout, to ensure that // injection times out (instead of failing). - const int32_t result = + const InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); - ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + InputEventInjectionSync::WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); ASSERT_TRUE(mDispatcher->waitForIdle()); @@ -2480,7 +2936,7 @@ TEST_F(InputDispatcherSingleWindowAnr, FocusedApplication_NoFocusedWindow) { // If the policy wants to keep waiting on the focused window to be added, make sure // that this timeout extension is honored and ANR is raised again. TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) { - mWindow->setFocus(false); + mWindow->setFocusable(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); mWindow->consumeFocusEvent(false); const std::chrono::duration timeout = 5ms; @@ -2489,10 +2945,10 @@ TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) { // Once a focused event arrives, we get an ANR for this application // We specify the injection timeout to be smaller than the application timeout, to ensure that // injection times out (instead of failing). - const int32_t result = + const InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); - ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + InputEventInjectionSync::WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); const std::chrono::duration appTimeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyAnrWasCalled(appTimeout, mApplication, nullptr /*windowToken*/); @@ -2508,21 +2964,21 @@ TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_ExtendsAnr) { // We have a focused application, but no focused window TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DropsFocusedEvents) { - mWindow->setFocus(false); + mWindow->setFocusable(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow}}}); mWindow->consumeFocusEvent(false); // Once a focused event arrives, we get an ANR for this application - const int32_t result = + const InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); - ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + InputEventInjectionSync::WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyAnrWasCalled(timeout, mApplication, nullptr /*windowToken*/); // Future focused events get dropped right away - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, injectKeyDown(mDispatcher)); + ASSERT_EQ(InputEventInjectionResult::FAILED, injectKeyDown(mDispatcher)); ASSERT_TRUE(mDispatcher->waitForIdle()); mWindow->assertNoEvents(); } @@ -2542,14 +2998,14 @@ TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) ADISPLAY_ID_DEFAULT, WINDOW_LOCATION, {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION}, - 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime); + 500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime); // Now send ACTION_UP, with identical timestamp injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION, {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION}, - 500ms, INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, currentTime); + 500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime); // We have now sent down and up. Let's consume first event and then ANR on the second. mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); @@ -2564,9 +3020,10 @@ TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnr FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, true /*isGestureMonitor*/); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)); mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT)); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher, ADISPLAY_ID_DEFAULT)); // Stuck on the ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); @@ -2642,7 +3099,7 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_CanExtendTimeout) { const std::chrono::duration timeout = 5ms; mFakePolicy->setAnrTimeout(timeout); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); @@ -2690,10 +3147,10 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { // window even if motions are still being processed. But because the injection timeout is short, // we will receive INJECTION_TIMED_OUT as the result. - int32_t result = + InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, ADISPLAY_ID_DEFAULT, - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT, 10ms); - ASSERT_EQ(INPUT_EVENT_INJECTION_TIMED_OUT, result); + InputEventInjectionSync::WAIT_FOR_RESULT, 10ms); + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); // Key will not be sent to the window, yet, because the window is still processing events // and the key remains pending, waiting for the touch events to be processed std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent(); @@ -2725,9 +3182,9 @@ TEST_F(InputDispatcherSingleWindowAnr, ASSERT_TRUE(upSequenceNum); // Don't finish the events yet, and send a key // Injection is async, so it will succeed - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, - ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE)); + ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE)); // At this point, key is still pending, and should not be sent to the application yet. std::optional<uint32_t> keySequenceNum = mWindow->receiveEvent(); ASSERT_FALSE(keySequenceNum); @@ -2747,7 +3204,7 @@ class InputDispatcherMultiWindowAnr : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); - mApplication = new FakeApplicationHandle(); + mApplication = std::make_shared<FakeApplicationHandle>(); mApplication->setDispatchingTimeout(10ms); mUnfocusedWindow = new FakeWindowHandle(mApplication, mDispatcher, "Unfocused", ADISPLAY_ID_DEFAULT); @@ -2755,23 +3212,24 @@ class InputDispatcherMultiWindowAnr : public InputDispatcherTest { // Adding FLAG_NOT_TOUCH_MODAL to ensure taps outside this window are not sent to this // window. // Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped - mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | - InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH | - InputWindowInfo::FLAG_SPLIT_TOUCH); + mUnfocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH | + InputWindowInfo::Flag::SPLIT_TOUCH); mFocusedWindow = new FakeWindowHandle(mApplication, mDispatcher, "Focused", ADISPLAY_ID_DEFAULT); - mFocusedWindow->setDispatchingTimeout(10ms); + mFocusedWindow->setDispatchingTimeout(30ms); mFocusedWindow->setFrame(Rect(50, 50, 100, 100)); - mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL | - InputWindowInfo::FLAG_SPLIT_TOUCH); + mFocusedWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL | + InputWindowInfo::Flag::SPLIT_TOUCH); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication); - mFocusedWindow->setFocus(true); + mFocusedWindow->setFocusable(true); // Expect one focus window exist in display. mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); + setFocusedWindow(mFocusedWindow); mFocusedWindow->consumeFocusEvent(true); } @@ -2783,7 +3241,7 @@ class InputDispatcherMultiWindowAnr : public InputDispatcherTest { } protected: - sp<FakeApplicationHandle> mApplication; + std::shared_ptr<FakeApplicationHandle> mApplication; sp<FakeWindowHandle> mUnfocusedWindow; sp<FakeWindowHandle> mFocusedWindow; static constexpr PointF UNFOCUSED_WINDOW_LOCATION = {20, 20}; @@ -2796,10 +3254,10 @@ protected: private: void tap(const PointF& location) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, location)); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, location)); } @@ -2808,10 +3266,10 @@ private: // If we have 2 windows that are both unresponsive, the one with the shortest timeout // should be ANR'd first. TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, FOCUSED_WINDOW_LOCATION)) - << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED"; + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; mFocusedWindow->consumeMotionDown(); mUnfocusedWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_OUTSIDE, ADISPLAY_ID_DEFAULT, 0 /*flags*/); @@ -2819,7 +3277,7 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { ASSERT_TRUE(mDispatcher->waitForIdle()); mFakePolicy->assertNotifyAnrWasNotCalled(); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, FOCUSED_WINDOW_LOCATION)); std::optional<uint32_t> unfocusedSequenceNum = mUnfocusedWindow->receiveEvent(); @@ -2847,9 +3305,9 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout tapOnFocusedWindow(); // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window - std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData1 = + std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData1 = mFakePolicy->getNotifyAnrData(10ms); - std::pair<sp<InputApplicationHandle>, sp<IBinder>> anrData2 = + std::pair<std::shared_ptr<InputApplicationHandle>, sp<IBinder>> anrData2 = mFakePolicy->getNotifyAnrData(0ms); // We don't know which window will ANR first. But both of them should happen eventually. @@ -2881,10 +3339,10 @@ TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { // Tap once again // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, FOCUSED_WINDOW_LOCATION)); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, FOCUSED_WINDOW_LOCATION)); // Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a @@ -2903,7 +3361,7 @@ TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { // If you tap outside of all windows, there will not be ANR TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) { - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, LOCATION_OUTSIDE_ALL_WINDOWS)); ASSERT_TRUE(mDispatcher->waitForIdle()); @@ -2915,7 +3373,7 @@ TEST_F(InputDispatcherMultiWindowAnr, Window_CanBePaused) { mFocusedWindow->setPaused(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mUnfocusedWindow, mFocusedWindow}}}); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, + ASSERT_EQ(InputEventInjectionResult::FAILED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, FOCUSED_WINDOW_LOCATION)); @@ -2955,19 +3413,20 @@ TEST_F(InputDispatcherMultiWindowAnr, PendingKey_GoesToNewlyFocusedWindow) { // Injection will succeed because we will eventually give up and send the key to the focused // window even if motions are still being processed. - int32_t result = + InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, - INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result); + InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); // Key will not be sent to the window, yet, because the window is still processing events // and the key remains pending, waiting for the touch events to be processed std::optional<uint32_t> keySequenceNum = mFocusedWindow->receiveEvent(); ASSERT_FALSE(keySequenceNum); // Switch the focus to the "unfocused" window that we tapped. Expect the key to go there - mFocusedWindow->setFocus(false); - mUnfocusedWindow->setFocus(true); + mFocusedWindow->setFocusable(false); + mUnfocusedWindow->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); + setFocusedWindow(mUnfocusedWindow); // Focus events should precede the key events mUnfocusedWindow->consumeFocusEvent(true); @@ -3044,11 +3503,12 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { * but in some cases the policy may not update the focused application. */ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) { - sp<FakeApplicationHandle> focusedApplication = new FakeApplicationHandle(); + std::shared_ptr<FakeApplicationHandle> focusedApplication = + std::make_shared<FakeApplicationHandle>(); focusedApplication->setDispatchingTimeout(60ms); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication); // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused. - mFocusedWindow->setFocus(false); + mFocusedWindow->setFocusable(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); mFocusedWindow->consumeFocusEvent(false); @@ -3056,10 +3516,10 @@ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_ // Send a key. The ANR timer should start because there is no focused window. // 'focusedApplication' will get blamed if this timer completes. // Key will not be sent anywhere because we have no focused window. It will remain pending. - int32_t result = + InputEventInjectionResult result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, - INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/); - ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result); + InputEventInjectionSync::NONE, 10ms /*injectionTimeout*/); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result); // Wait until dispatcher starts the "no focused window" timer. If we don't wait here, // then the injected touches won't cause the focused event to get dropped. @@ -3079,8 +3539,9 @@ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_ // We do not consume the motion right away, because that would require dispatcher to first // process (== drop) the key event, and by that time, ANR will be raised. // Set the focused window first. - mFocusedWindow->setFocus(true); + mFocusedWindow->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); + setFocusedWindow(mFocusedWindow); mFocusedWindow->consumeFocusEvent(true); // We do not call "setFocusedApplication" here, even though the newly focused window belongs // to another application. This could be a bug / behaviour in the policy. @@ -3092,4 +3553,220 @@ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled()); } +// These tests ensure we cannot send touch events to a window that's positioned behind a window +// that has feature NO_INPUT_CHANNEL. +// Layout: +// Top (closest to user) +// mNoInputWindow (above all windows) +// mBottomWindow +// Bottom (furthest from user) +class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest { + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApplication = std::make_shared<FakeApplicationHandle>(); + mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher, + "Window without input channel", ADISPLAY_ID_DEFAULT, + std::make_optional<sp<IBinder>>(nullptr) /*token*/); + + mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); + // It's perfectly valid for this window to not have an associated input channel + + mBottomWindow = new FakeWindowHandle(mApplication, mDispatcher, "Bottom window", + ADISPLAY_ID_DEFAULT); + mBottomWindow->setFrame(Rect(0, 0, 100, 100)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}}); + } + +protected: + std::shared_ptr<FakeApplicationHandle> mApplication; + sp<FakeWindowHandle> mNoInputWindow; + sp<FakeWindowHandle> mBottomWindow; +}; + +TEST_F(InputDispatcherMultiWindowOcclusionTests, NoInputChannelFeature_DropsTouches) { + PointF touchedPoint = {10, 10}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + + mNoInputWindow->assertNoEvents(); + // Even though the window 'mNoInputWindow' positioned above 'mBottomWindow' does not have + // an input channel, it is not marked as FLAG_NOT_TOUCHABLE, + // and therefore should prevent mBottomWindow from receiving touches + mBottomWindow->assertNoEvents(); +} + +/** + * If a window has feature NO_INPUT_CHANNEL, and somehow (by mistake) still has an input channel, + * ensure that this window does not receive any touches, and blocks touches to windows underneath. + */ +TEST_F(InputDispatcherMultiWindowOcclusionTests, + NoInputChannelFeature_DropsTouchesWithValidChannel) { + mNoInputWindow = new FakeWindowHandle(mApplication, mDispatcher, + "Window with input channel and NO_INPUT_CHANNEL", + ADISPLAY_ID_DEFAULT); + + mNoInputWindow->setInputFeatures(InputWindowInfo::Feature::NO_INPUT_CHANNEL); + mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mNoInputWindow, mBottomWindow}}}); + + PointF touchedPoint = {10, 10}; + + NotifyMotionArgs motionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {touchedPoint}); + mDispatcher->notifyMotion(&motionArgs); + + mNoInputWindow->assertNoEvents(); + mBottomWindow->assertNoEvents(); +} + +class InputDispatcherMirrorWindowFocusTests : public InputDispatcherTest { +protected: + std::shared_ptr<FakeApplicationHandle> mApp; + sp<FakeWindowHandle> mWindow; + sp<FakeWindowHandle> mMirror; + + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + mApp = std::make_shared<FakeApplicationHandle>(); + mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); + mMirror = new FakeWindowHandle(mApp, mDispatcher, "TestWindowMirror", ADISPLAY_ID_DEFAULT, + mWindow->getToken()); + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); + mWindow->setFocusable(true); + mMirror->setFocusable(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}}); + } +}; + +TEST_F(InputDispatcherMirrorWindowFocusTests, CanGetFocus) { + // Request focus on a mirrored window + setFocusedWindow(mMirror); + + // window gets focused + mWindow->consumeFocusEvent(true); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); +} + +// A focused & mirrored window remains focused only if the window and its mirror are both +// focusable. +TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) { + setFocusedWindow(mMirror); + + // window gets focused + mWindow->consumeFocusEvent(true); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyUp(ADISPLAY_ID_NONE); + + mMirror->setFocusable(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}}); + + // window loses focus since one of the windows associated with the token in not focusable + mWindow->consumeFocusEvent(false); + + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::TIMED_OUT"; + mWindow->assertNoEvents(); +} + +// A focused & mirrored window remains focused until the window and its mirror both become +// invisible. +TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAnyWindowVisible) { + setFocusedWindow(mMirror); + + // window gets focused + mWindow->consumeFocusEvent(true); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyUp(ADISPLAY_ID_NONE); + + mMirror->setVisible(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyUp(ADISPLAY_ID_NONE); + + mWindow->setVisible(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}}); + + // window loses focus only after all windows associated with the token become invisible. + mWindow->consumeFocusEvent(false); + + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::TIMED_OUT"; + mWindow->assertNoEvents(); +} + +// A focused & mirrored window remains focused until both windows are removed. +TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedWhileWindowsAlive) { + setFocusedWindow(mMirror); + + // window gets focused + mWindow->consumeFocusEvent(true); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyUp(ADISPLAY_ID_NONE); + + // single window is removed but the window token remains focused + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mMirror}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + mWindow->consumeKeyUp(ADISPLAY_ID_NONE); + + // Both windows are removed + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}}); + mWindow->consumeFocusEvent(false); + + ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::TIMED_OUT"; + mWindow->assertNoEvents(); +} + +// Focus request can be pending until one window becomes visible. +TEST_F(InputDispatcherMirrorWindowFocusTests, DeferFocusWhenInvisible) { + // Request focus on an invisible mirror. + mWindow->setVisible(false); + mMirror->setVisible(false); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}}); + setFocusedWindow(mMirror); + + // Injected key goes to pending queue. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /* repeatCount */, + ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE)); + + mMirror->setVisible(true); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mMirror}}}); + + // window gets focused + mWindow->consumeFocusEvent(true); + // window gets the pending key event + mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); +} } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputFlingerService_test.cpp b/services/inputflinger/tests/InputFlingerService_test.cpp new file mode 100644 index 0000000000..c368e79f41 --- /dev/null +++ b/services/inputflinger/tests/InputFlingerService_test.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <BnInputFlingerQuery.h> +#include <IInputFlingerQuery.h> + +#include <android/os/BnInputFlinger.h> +#include <android/os/BnSetInputWindowsListener.h> +#include <android/os/IInputFlinger.h> +#include <android/os/ISetInputWindowsListener.h> + +#include <binder/Binder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> + +#include <input/Input.h> +#include <input/InputTransport.h> +#include <input/InputWindow.h> + +#include <gtest/gtest.h> +#include <inttypes.h> +#include <linux/uinput.h> +#include <log/log.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <chrono> +#include <thread> +#include <unordered_map> + +#define TAG "InputFlingerServiceTest" + +using android::os::BnInputFlinger; +using android::os::BnSetInputWindowsListener; +using android::os::IInputFlinger; +using android::os::ISetInputWindowsListener; + +using std::chrono_literals::operator""ms; +using std::chrono_literals::operator""s; + +namespace android { + +static const sp<IBinder> TestInfoToken = new BBinder(); +static const sp<IBinder> FocusedTestInfoToken = new BBinder(); +static constexpr int32_t TestInfoId = 1; +static const std::string TestInfoName = "InputFlingerServiceTestInputWindowInfo"; +static constexpr Flags<InputWindowInfo::Flag> TestInfoFlags = InputWindowInfo::Flag::NOT_FOCUSABLE; +static constexpr InputWindowInfo::Type TestInfoType = InputWindowInfo::Type::INPUT_METHOD; +static constexpr std::chrono::duration TestInfoDispatchingTimeout = 2532ms; +static constexpr int32_t TestInfoFrameLeft = 93; +static constexpr int32_t TestInfoFrameTop = 34; +static constexpr int32_t TestInfoFrameRight = 16; +static constexpr int32_t TestInfoFrameBottom = 19; +static constexpr int32_t TestInfoSurfaceInset = 17; +static constexpr float TestInfoGlobalScaleFactor = 0.3; +static constexpr float TestInfoWindowXScale = 0.4; +static constexpr float TestInfoWindowYScale = 0.5; +static const Rect TestInfoTouchableRegionRect = {100 /* left */, 150 /* top */, 400 /* right */, + 450 /* bottom */}; +static const Region TestInfoTouchableRegion(TestInfoTouchableRegionRect); +static constexpr bool TestInfoVisible = false; +static constexpr bool TestInfoTrustedOverlay = true; +static constexpr bool TestInfoFocusable = false; +static constexpr bool TestInfoHasWallpaper = false; +static constexpr bool TestInfoPaused = false; +static constexpr int32_t TestInfoOwnerPid = 19; +static constexpr int32_t TestInfoOwnerUid = 24; +static constexpr InputWindowInfo::Feature TestInfoInputFeatures = + InputWindowInfo::Feature::NO_INPUT_CHANNEL; +static constexpr int32_t TestInfoDisplayId = 34; +static constexpr int32_t TestInfoPortalToDisplayId = 2; +static constexpr bool TestInfoReplaceTouchableRegionWithCrop = true; +static const sp<IBinder> TestInfoTouchableRegionCropHandle = new BBinder(); + +static const std::string TestAppInfoName = "InputFlingerServiceTestInputApplicationInfo"; +static const sp<IBinder> TestAppInfoToken = new BBinder(); +static constexpr std::chrono::duration TestAppInfoDispatchingTimeout = 12345678ms; + +static const String16 kTestServiceName = String16("InputFlingerService"); +static const String16 kQueryServiceName = String16("InputFlingerQueryService"); + +struct SetInputWindowsListener; +// --- InputFlingerServiceTest --- +class InputFlingerServiceTest : public testing::Test { +public: + void SetUp() override; + void TearDown() override; + +protected: + void InitializeInputFlinger(); + void setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos); + void setFocusedWindow(const sp<IBinder> token, const sp<IBinder> focusedToken, + nsecs_t timestampNanos); + + void setInputWindowsFinished(); + void verifyInputWindowInfo(const InputWindowInfo& info) const; + InputWindowInfo& getInfo() const { return const_cast<InputWindowInfo&>(mInfo); } + + sp<IInputFlinger> mService; + sp<IInputFlingerQuery> mQuery; + +private: + sp<SetInputWindowsListener> mSetInputWindowsListener; + std::unique_ptr<InputChannel> mServerChannel, mClientChannel; + InputWindowInfo mInfo; + std::mutex mLock; + std::condition_variable mSetInputWindowsFinishedCondition; +}; + +struct SetInputWindowsListener : BnSetInputWindowsListener { + explicit SetInputWindowsListener(std::function<void()> cbFunc) : mCbFunc(cbFunc) {} + + binder::Status onSetInputWindowsFinished() override; + + std::function<void()> mCbFunc; +}; + +class TestInputManager : public BnInputFlinger { +protected: + virtual ~TestInputManager(){}; + +public: + TestInputManager(){}; + + binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles); + binder::Status getInputChannels(std::vector<::android::InputChannel>* channels); + binder::Status getLastFocusRequest(FocusRequest*); + + status_t dump(int fd, const Vector<String16>& args) override; + + binder::Status setInputWindows( + const std::vector<InputWindowInfo>& handles, + const sp<ISetInputWindowsListener>& setInputWindowsListener) override; + + binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; + binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override; + binder::Status setFocusedWindow(const FocusRequest&) override; + + void reset(); + +private: + mutable Mutex mLock; + std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mHandlesPerDisplay; + std::vector<std::shared_ptr<InputChannel>> mInputChannels; + FocusRequest mFocusRequest; +}; + +class TestInputQuery : public BnInputFlingerQuery { +public: + TestInputQuery(sp<android::TestInputManager> manager) : mManager(manager){}; + binder::Status getInputWindows(std::vector<::android::InputWindowInfo>* inputHandles) override; + binder::Status getInputChannels(std::vector<::android::InputChannel>* channels) override; + binder::Status getLastFocusRequest(FocusRequest*) override; + binder::Status resetInputManager() override; + +private: + sp<android::TestInputManager> mManager; +}; + +binder::Status TestInputQuery::getInputWindows( + std::vector<::android::InputWindowInfo>* inputHandles) { + return mManager->getInputWindows(inputHandles); +} + +binder::Status TestInputQuery::getInputChannels(std::vector<::android::InputChannel>* channels) { + return mManager->getInputChannels(channels); +} + +binder::Status TestInputQuery::getLastFocusRequest(FocusRequest* request) { + return mManager->getLastFocusRequest(request); +} + +binder::Status TestInputQuery::resetInputManager() { + mManager->reset(); + return binder::Status::ok(); +} + +binder::Status SetInputWindowsListener::onSetInputWindowsFinished() { + if (mCbFunc != nullptr) { + mCbFunc(); + } + return binder::Status::ok(); +} + +binder::Status TestInputManager::setInputWindows( + const std::vector<InputWindowInfo>& infos, + const sp<ISetInputWindowsListener>& setInputWindowsListener) { + AutoMutex _l(mLock); + + for (const auto& info : infos) { + mHandlesPerDisplay.emplace(info.displayId, std::vector<sp<InputWindowHandle>>()); + mHandlesPerDisplay[info.displayId].push_back(new InputWindowHandle(info)); + } + if (setInputWindowsListener) { + setInputWindowsListener->onSetInputWindowsFinished(); + } + return binder::Status::ok(); +} + +binder::Status TestInputManager::createInputChannel(const std::string& name, + InputChannel* outChannel) { + AutoMutex _l(mLock); + std::unique_ptr<InputChannel> serverChannel; + std::unique_ptr<InputChannel> clientChannel; + InputChannel::openInputChannelPair(name, serverChannel, clientChannel); + + clientChannel->copyTo(*outChannel); + + mInputChannels.emplace_back(std::move(serverChannel)); + + return binder::Status::ok(); +} + +binder::Status TestInputManager::removeInputChannel(const sp<IBinder>& connectionToken) { + AutoMutex _l(mLock); + + auto it = std::find_if(mInputChannels.begin(), mInputChannels.end(), + [&](std::shared_ptr<InputChannel>& c) { + return c->getConnectionToken() == connectionToken; + }); + if (it != mInputChannels.end()) { + mInputChannels.erase(it); + } + + return binder::Status::ok(); +} + +status_t TestInputManager::dump(int fd, const Vector<String16>& args) { + std::string dump; + + dump += " InputFlinger dump\n"; + + ::write(fd, dump.c_str(), dump.size()); + return NO_ERROR; +} + +binder::Status TestInputManager::getInputWindows( + std::vector<::android::InputWindowInfo>* inputInfos) { + for (auto& [displayId, inputHandles] : mHandlesPerDisplay) { + for (auto& inputHandle : inputHandles) { + inputInfos->push_back(*inputHandle->getInfo()); + } + } + return binder::Status::ok(); +} + +binder::Status TestInputManager::getInputChannels(std::vector<::android::InputChannel>* channels) { + channels->clear(); + for (std::shared_ptr<InputChannel>& channel : mInputChannels) { + channels->push_back(*channel); + } + return binder::Status::ok(); +} + +binder::Status TestInputManager::getLastFocusRequest(FocusRequest* request) { + *request = mFocusRequest; + return binder::Status::ok(); +} + +binder::Status TestInputManager::setFocusedWindow(const FocusRequest& request) { + mFocusRequest = request; + return binder::Status::ok(); +} + +void TestInputManager::reset() { + mHandlesPerDisplay.clear(); + mInputChannels.clear(); + mFocusRequest = FocusRequest(); +} + +void InputFlingerServiceTest::SetUp() { + mSetInputWindowsListener = new SetInputWindowsListener([&]() { + std::unique_lock<std::mutex> lock(mLock); + mSetInputWindowsFinishedCondition.notify_all(); + }); + InputChannel::openInputChannelPair("testchannels", mServerChannel, mClientChannel); + + mInfo.token = TestInfoToken; + mInfo.id = TestInfoId; + mInfo.name = TestInfoName; + mInfo.flags = TestInfoFlags; + mInfo.type = TestInfoType; + mInfo.dispatchingTimeout = TestInfoDispatchingTimeout; + mInfo.frameLeft = TestInfoFrameLeft; + mInfo.frameTop = TestInfoFrameTop; + mInfo.frameRight = TestInfoFrameRight; + mInfo.frameBottom = TestInfoFrameBottom; + mInfo.surfaceInset = TestInfoSurfaceInset; + mInfo.globalScaleFactor = TestInfoGlobalScaleFactor; + mInfo.transform.set({TestInfoWindowXScale, 0, TestInfoFrameLeft, 0, TestInfoWindowYScale, + TestInfoFrameTop, 0, 0, 1}); + mInfo.touchableRegion = TestInfoTouchableRegion; + mInfo.visible = TestInfoVisible; + mInfo.trustedOverlay = TestInfoTrustedOverlay; + mInfo.focusable = TestInfoFocusable; + + mInfo.hasWallpaper = TestInfoHasWallpaper; + mInfo.paused = TestInfoPaused; + mInfo.ownerPid = TestInfoOwnerPid; + mInfo.ownerUid = TestInfoOwnerUid; + mInfo.inputFeatures = TestInfoInputFeatures; + mInfo.displayId = TestInfoDisplayId; + mInfo.portalToDisplayId = TestInfoPortalToDisplayId; + mInfo.replaceTouchableRegionWithCrop = TestInfoReplaceTouchableRegionWithCrop; + mInfo.touchableRegionCropHandle = TestInfoTouchableRegionCropHandle; + + mInfo.applicationInfo.name = TestAppInfoName; + mInfo.applicationInfo.token = TestAppInfoToken; + mInfo.applicationInfo.dispatchingTimeoutMillis = + std::chrono::duration_cast<std::chrono::milliseconds>(TestAppInfoDispatchingTimeout) + .count(); + + InitializeInputFlinger(); +} + +void InputFlingerServiceTest::TearDown() { + mQuery->resetInputManager(); +} + +void InputFlingerServiceTest::verifyInputWindowInfo(const InputWindowInfo& info) const { + EXPECT_EQ(mInfo, info); +} + +void InputFlingerServiceTest::InitializeInputFlinger() { + sp<IBinder> input(defaultServiceManager()->waitForService(kTestServiceName)); + ASSERT_TRUE(input != nullptr); + mService = interface_cast<IInputFlinger>(input); + + input = defaultServiceManager()->waitForService(kQueryServiceName); + ASSERT_TRUE(input != nullptr); + mQuery = interface_cast<IInputFlingerQuery>(input); +} + +void InputFlingerServiceTest::setInputWindowsByInfos(const std::vector<InputWindowInfo>& infos) { + std::unique_lock<std::mutex> lock(mLock); + mService->setInputWindows(infos, mSetInputWindowsListener); + // Verify listener call + EXPECT_NE(mSetInputWindowsFinishedCondition.wait_for(lock, 1s), std::cv_status::timeout); +} + +void InputFlingerServiceTest::setFocusedWindow(const sp<IBinder> token, + const sp<IBinder> focusedToken, + nsecs_t timestampNanos) { + FocusRequest request; + request.token = TestInfoToken; + request.focusedToken = focusedToken; + request.timestamp = timestampNanos; + mService->setFocusedWindow(request); + // call set input windows and wait for the callback to drain the queue. + setInputWindowsByInfos(std::vector<InputWindowInfo>()); +} + +/** + * Test InputFlinger service interface SetInputWindows + */ +TEST_F(InputFlingerServiceTest, InputWindow_SetInputWindows) { + std::vector<InputWindowInfo> infos = {getInfo()}; + setInputWindowsByInfos(infos); + + // Verify input windows from service + std::vector<::android::InputWindowInfo> windowInfos; + mQuery->getInputWindows(&windowInfos); + for (const ::android::InputWindowInfo& windowInfo : windowInfos) { + verifyInputWindowInfo(windowInfo); + } +} + +/** + * Test InputFlinger service interface createInputChannel + */ +TEST_F(InputFlingerServiceTest, CreateInputChannelReturnsUnblockedFd) { + // Test that the unblocked file descriptor flag is kept across processes over binder + // transactions. + + InputChannel channel; + ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk()); + + const base::unique_fd& fd = channel.getFd(); + ASSERT_TRUE(fd.ok()); + + const int result = fcntl(fd, F_GETFL); + EXPECT_NE(result, -1); + EXPECT_EQ(result & O_NONBLOCK, O_NONBLOCK); +} + +TEST_F(InputFlingerServiceTest, InputWindow_CreateInputChannel) { + InputChannel channel; + ASSERT_TRUE(mService->createInputChannel("testchannels", &channel).isOk()); + + std::vector<::android::InputChannel> channels; + mQuery->getInputChannels(&channels); + ASSERT_EQ(channels.size(), 1UL); + EXPECT_EQ(channels[0].getConnectionToken(), channel.getConnectionToken()); + + mService->removeInputChannel(channel.getConnectionToken()); + mQuery->getInputChannels(&channels); + EXPECT_EQ(channels.size(), 0UL); +} + +TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindow) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + setFocusedWindow(TestInfoToken, nullptr /* focusedToken */, now); + + FocusRequest request; + mQuery->getLastFocusRequest(&request); + + EXPECT_EQ(request.token, TestInfoToken); + EXPECT_EQ(request.focusedToken, nullptr); + EXPECT_EQ(request.timestamp, now); +} + +TEST_F(InputFlingerServiceTest, InputWindow_setFocusedWindowWithFocusedToken) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + setFocusedWindow(TestInfoToken, FocusedTestInfoToken, now); + + FocusRequest request; + mQuery->getLastFocusRequest(&request); + + EXPECT_EQ(request.token, TestInfoToken); + EXPECT_EQ(request.focusedToken, FocusedTestInfoToken); + EXPECT_EQ(request.timestamp, now); +} + +} // namespace android + +int main(int argc, char** argv) { + pid_t forkPid = fork(); + + if (forkPid == 0) { + // Server process + android::sp<android::TestInputManager> manager = new android::TestInputManager(); + android::sp<android::TestInputQuery> query = new android::TestInputQuery(manager); + + android::defaultServiceManager()->addService(android::kTestServiceName, manager, + false /*allowIsolated*/); + android::defaultServiceManager()->addService(android::kQueryServiceName, query, + false /*allowIsolated*/); + android::ProcessState::self()->startThreadPool(); + android::IPCThreadState::self()->joinThreadPool(); + } else { + android::ProcessState::self()->startThreadPool(); + ::testing::InitGoogleTest(&argc, argv); + int result = RUN_ALL_TESTS(); + kill(forkPid, SIGKILL); + return result; + } + return 0; +} diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 58f83b518d..a72d5c386f 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -33,10 +33,13 @@ #include <math.h> #include <memory> +#include "input/DisplayViewport.h" +#include "input/Input.h" namespace android { using std::chrono_literals::operator""ms; +using namespace android::flag_operators; // Timeout for waiting for an expected event static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms; @@ -101,29 +104,23 @@ public: mMaxY = maxY; } - virtual void setPosition(float x, float y) { + void setPosition(float x, float y) override { mX = x; mY = y; } - virtual void setButtonState(int32_t buttonState) { - mButtonState = buttonState; - } + void setButtonState(int32_t buttonState) override { mButtonState = buttonState; } - virtual int32_t getButtonState() const { - return mButtonState; - } + int32_t getButtonState() const override { return mButtonState; } - virtual void getPosition(float* outX, float* outY) const { + void getPosition(float* outX, float* outY) const override { *outX = mX; *outY = mY; } - virtual int32_t getDisplayId() const { - return mDisplayId; - } + int32_t getDisplayId() const override { return mDisplayId; } - virtual void setDisplayViewport(const DisplayViewport& viewport) { + void setDisplayViewport(const DisplayViewport& viewport) override { mDisplayId = viewport.displayId; } @@ -132,7 +129,7 @@ public: } private: - virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const { + bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const override { *outMinX = mMinX; *outMinY = mMinY; *outMaxX = mMaxX; @@ -140,7 +137,7 @@ private: return mHaveBounds; } - virtual void move(float deltaX, float deltaY) { + void move(float deltaX, float deltaY) override { mX += deltaX; if (mX < mMinX) mX = mMinX; if (mX > mMaxX) mX = mMaxX; @@ -149,17 +146,14 @@ private: if (mY > mMaxY) mY = mMaxY; } - virtual void fade(Transition) { - } + void fade(Transition) override {} - virtual void unfade(Transition) { - } + void unfade(Transition) override {} - virtual void setPresentation(Presentation) { - } + void setPresentation(Presentation) override {} - virtual void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits, - int32_t displayId) { + void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits, + int32_t displayId) override { std::vector<int32_t> newSpots; // Add spots for fingers that are down. for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { @@ -170,8 +164,7 @@ private: mSpotsByDisplay[displayId] = newSpots; } - virtual void clearSpots() { - } + void clearSpots() override {} std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; }; @@ -191,7 +184,7 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface { TouchAffineTransformation transform; protected: - virtual ~FakeInputReaderPolicy() { } + virtual ~FakeInputReaderPolicy() {} public: FakeInputReaderPolicy() { @@ -324,28 +317,27 @@ private: return v; } - virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) { + void getReaderConfiguration(InputReaderConfiguration* outConfig) override { *outConfig = mConfig; } - virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) { + std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override { return mPointerControllers[deviceId]; } - virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) { + void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override { std::scoped_lock<std::mutex> lock(mLock); mInputDevices = inputDevices; mInputDevicesChanged = true; mDevicesChangedCondition.notify_all(); } - virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) { + std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( + const InputDeviceIdentifier&) override { return nullptr; } - virtual std::string getDeviceAlias(const InputDeviceIdentifier&) { - return ""; - } + std::string getDeviceAlias(const InputDeviceIdentifier&) override { return ""; } void waitForInputDevices(std::function<void(bool)> processDevicesChanged) { std::unique_lock<std::mutex> lock(mLock); @@ -370,7 +362,7 @@ class FakeEventHub : public EventHubInterface { struct Device { InputDeviceIdentifier identifier; - uint32_t classes; + Flags<InputDeviceClass> classes; PropertyMap configuration; KeyedVector<int, RawAbsoluteAxisInfo> absoluteAxes; KeyedVector<int, bool> relativeAxes; @@ -394,9 +386,7 @@ class FakeEventHub : public EventHubInterface { return OK; } - explicit Device(uint32_t classes) : - classes(classes), enabled(true) { - } + explicit Device(Flags<InputDeviceClass> classes) : classes(classes), enabled(true) {} }; std::mutex mLock; @@ -416,7 +406,7 @@ public: FakeEventHub() { } - void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) { + void addDevice(int32_t deviceId, const std::string& name, Flags<InputDeviceClass> classes) { Device* device = new Device(classes); device->identifier.name = name; mDevices.add(deviceId, device); @@ -592,29 +582,27 @@ private: return index >= 0 ? mDevices.valueAt(index) : nullptr; } - virtual uint32_t getDeviceClasses(int32_t deviceId) const { + Flags<InputDeviceClass> getDeviceClasses(int32_t deviceId) const override { Device* device = getDevice(deviceId); - return device ? device->classes : 0; + return device ? device->classes : Flags<InputDeviceClass>(0); } - virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const { + InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override { Device* device = getDevice(deviceId); return device ? device->identifier : InputDeviceIdentifier(); } - virtual int32_t getDeviceControllerNumber(int32_t) const { - return 0; - } + int32_t getDeviceControllerNumber(int32_t) const override { return 0; } - virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { + void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override { Device* device = getDevice(deviceId); if (device) { *outConfiguration = device->configuration; } } - virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { + status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, + RawAbsoluteAxisInfo* outAxisInfo) const override { Device* device = getDevice(deviceId); if (device && device->enabled) { ssize_t index = device->absoluteAxes.indexOfKey(axis); @@ -627,7 +615,7 @@ private: return -1; } - virtual bool hasRelativeAxis(int32_t deviceId, int axis) const { + bool hasRelativeAxis(int32_t deviceId, int axis) const override { Device* device = getDevice(deviceId); if (device) { return device->relativeAxes.indexOfKey(axis) >= 0; @@ -635,13 +623,10 @@ private: return false; } - virtual bool hasInputProperty(int32_t, int) const { - return false; - } + bool hasInputProperty(int32_t, int) const override { return false; } - virtual status_t mapKey(int32_t deviceId, - int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const { + status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const override { Device* device = getDevice(deviceId); if (device) { const KeyInfo* key = getKey(device, scanCode, usageCode); @@ -677,15 +662,13 @@ private: return nullptr; } - virtual status_t mapAxis(int32_t, int32_t, AxisInfo*) const { - return NAME_NOT_FOUND; - } + status_t mapAxis(int32_t, int32_t, AxisInfo*) const override { return NAME_NOT_FOUND; } - virtual void setExcludedDevices(const std::vector<std::string>& devices) { + void setExcludedDevices(const std::vector<std::string>& devices) override { mExcludedDevices = devices; } - virtual size_t getEvents(int, RawEvent* buffer, size_t) { + size_t getEvents(int, RawEvent* buffer, size_t) override { std::scoped_lock<std::mutex> lock(mLock); if (mEvents.empty()) { return 0; @@ -697,7 +680,7 @@ private: return 1; } - virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) { + std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override { auto it = mVideoFrames.find(deviceId); if (it != mVideoFrames.end()) { std::vector<TouchVideoFrame> frames = std::move(it->second); @@ -707,7 +690,7 @@ private: return {}; } - virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const { + int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override { Device* device = getDevice(deviceId); if (device) { ssize_t index = device->scanCodeStates.indexOfKey(scanCode); @@ -718,7 +701,7 @@ private: return AKEY_STATE_UNKNOWN; } - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const { + int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override { Device* device = getDevice(deviceId); if (device) { ssize_t index = device->keyCodeStates.indexOfKey(keyCode); @@ -729,7 +712,7 @@ private: return AKEY_STATE_UNKNOWN; } - virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const { + int32_t getSwitchState(int32_t deviceId, int32_t sw) const override { Device* device = getDevice(deviceId); if (device) { ssize_t index = device->switchStates.indexOfKey(sw); @@ -740,8 +723,8 @@ private: return AKEY_STATE_UNKNOWN; } - virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const { + status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const override { Device* device = getDevice(deviceId); if (device) { ssize_t index = device->absoluteAxisValue.indexOfKey(axis); @@ -754,22 +737,22 @@ private: return -1; } - virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const { + // Return true if the device has non-empty key layout. + bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) const override { bool result = false; Device* device = getDevice(deviceId); if (device) { + result = device->keysByScanCode.size() > 0 || device->keysByUsageCode.size() > 0; for (size_t i = 0; i < numCodes; i++) { for (size_t j = 0; j < device->keysByScanCode.size(); j++) { if (keyCodes[i] == device->keysByScanCode.valueAt(j).keyCode) { outFlags[i] = 1; - result = true; } } for (size_t j = 0; j < device->keysByUsageCode.size(); j++) { if (keyCodes[i] == device->keysByUsageCode.valueAt(j).keyCode) { outFlags[i] = 1; - result = true; } } } @@ -777,7 +760,7 @@ private: return result; } - virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const { + bool hasScanCode(int32_t deviceId, int32_t scanCode) const override { Device* device = getDevice(deviceId); if (device) { ssize_t index = device->keysByScanCode.indexOfKey(scanCode); @@ -786,12 +769,12 @@ private: return false; } - virtual bool hasLed(int32_t deviceId, int32_t led) const { + bool hasLed(int32_t deviceId, int32_t led) const override { Device* device = getDevice(deviceId); return device && device->leds.indexOfKey(led) >= 0; } - virtual void setLedState(int32_t deviceId, int32_t led, bool on) { + void setLedState(int32_t deviceId, int32_t led, bool on) override { Device* device = getDevice(deviceId); if (device) { ssize_t index = device->leds.indexOfKey(led); @@ -805,8 +788,8 @@ private: } } - virtual void getVirtualKeyDefinitions(int32_t deviceId, - std::vector<VirtualKeyDefinition>& outVirtualKeys) const { + void getVirtualKeyDefinitions( + int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override { outVirtualKeys.clear(); Device* device = getDevice(deviceId); @@ -815,146 +798,31 @@ private: } } - virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t) const { + const std::shared_ptr<KeyCharacterMap> getKeyCharacterMap(int32_t) const override { return nullptr; } - virtual bool setKeyboardLayoutOverlay(int32_t, const sp<KeyCharacterMap>&) { + bool setKeyboardLayoutOverlay(int32_t, std::shared_ptr<KeyCharacterMap>) override { return false; } - virtual void vibrate(int32_t, nsecs_t) { - } + void vibrate(int32_t, const VibrationElement&) override {} - virtual void cancelVibrate(int32_t) { - } + void cancelVibrate(int32_t) override {} virtual bool isExternal(int32_t) const { return false; } - virtual void dump(std::string&) { - } + void dump(std::string&) override {} - virtual void monitor() { - } + void monitor() override {} - virtual void requestReopenDevices() { - } + void requestReopenDevices() override {} - virtual void wake() { - } + void wake() override {} }; - -// --- FakeInputReaderContext --- - -class FakeInputReaderContext : public InputReaderContext { - std::shared_ptr<EventHubInterface> mEventHub; - sp<InputReaderPolicyInterface> mPolicy; - sp<InputListenerInterface> mListener; - int32_t mGlobalMetaState; - bool mUpdateGlobalMetaStateWasCalled; - int32_t mGeneration; - int32_t mNextId; - std::weak_ptr<PointerControllerInterface> mPointerController; - -public: - FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) - : mEventHub(eventHub), - mPolicy(policy), - mListener(listener), - mGlobalMetaState(0), - mNextId(1) {} - - virtual ~FakeInputReaderContext() { } - - void assertUpdateGlobalMetaStateWasCalled() { - ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled) - << "Expected updateGlobalMetaState() to have been called."; - mUpdateGlobalMetaStateWasCalled = false; - } - - void setGlobalMetaState(int32_t state) { - mGlobalMetaState = state; - } - - uint32_t getGeneration() { - return mGeneration; - } - - void updatePointerDisplay() { - std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock(); - if (controller != nullptr) { - InputReaderConfiguration config; - mPolicy->getReaderConfiguration(&config); - auto viewport = config.getDisplayViewportById(config.defaultPointerDisplayId); - if (viewport) { - controller->setDisplayViewport(*viewport); - } - } - } - -private: - virtual void updateGlobalMetaState() { - mUpdateGlobalMetaStateWasCalled = true; - } - - virtual int32_t getGlobalMetaState() { - return mGlobalMetaState; - } - - virtual EventHubInterface* getEventHub() { - return mEventHub.get(); - } - - virtual InputReaderPolicyInterface* getPolicy() { - return mPolicy.get(); - } - - virtual InputListenerInterface* getListener() { - return mListener.get(); - } - - virtual void disableVirtualKeysUntil(nsecs_t) { - } - - virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; } - - virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) { - std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock(); - if (controller == nullptr) { - controller = mPolicy->obtainPointerController(deviceId); - mPointerController = controller; - updatePointerDisplay(); - } - return controller; - } - - virtual void fadePointer() { - } - - virtual void requestTimeoutAtTime(nsecs_t) { - } - - virtual int32_t bumpGeneration() { - return ++mGeneration; - } - - virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) { - - } - - virtual void dispatchExternalStylusState(const StylusState&) { - - } - - virtual int32_t getNextId() { return mNextId++; } -}; - - // --- FakeInputMapper --- class FakeInputMapper : public InputMapper { @@ -984,7 +852,7 @@ public: mResetWasCalled(false), mProcessWasCalled(false) {} - virtual ~FakeInputMapper() { } + virtual ~FakeInputMapper() {} void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; @@ -1053,11 +921,9 @@ public: } private: - virtual uint32_t getSources() { - return mSources; - } + uint32_t getSources() override { return mSources; } - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) { + void populateDeviceInfo(InputDeviceInfo* deviceInfo) override { InputMapper::populateDeviceInfo(deviceInfo); if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) { @@ -1065,7 +931,7 @@ private: } } - virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) { + void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) override { std::scoped_lock<std::mutex> lock(mLock); mConfigureWasCalled = true; @@ -1078,45 +944,45 @@ private: mStateChangedCondition.notify_all(); } - virtual void reset(nsecs_t) { + void reset(nsecs_t) override { std::scoped_lock<std::mutex> lock(mLock); mResetWasCalled = true; mStateChangedCondition.notify_all(); } - virtual void process(const RawEvent* rawEvent) { + void process(const RawEvent* rawEvent) override { std::scoped_lock<std::mutex> lock(mLock); mLastEvent = *rawEvent; mProcessWasCalled = true; mStateChangedCondition.notify_all(); } - virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) { + int32_t getKeyCodeState(uint32_t, int32_t keyCode) override { ssize_t index = mKeyCodeStates.indexOfKey(keyCode); return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; } - virtual int32_t getScanCodeState(uint32_t, int32_t scanCode) { + int32_t getScanCodeState(uint32_t, int32_t scanCode) override { ssize_t index = mScanCodeStates.indexOfKey(scanCode); return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; } - virtual int32_t getSwitchState(uint32_t, int32_t switchCode) { + int32_t getSwitchState(uint32_t, int32_t switchCode) override { ssize_t index = mSwitchStates.indexOfKey(switchCode); return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN; } - virtual bool markSupportedKeyCodes(uint32_t, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; + // Return true if the device has non-empty key layout. + bool markSupportedKeyCodes(uint32_t, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) override { for (size_t i = 0; i < numCodes; i++) { for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) { if (keyCodes[i] == mSupportedKeyCodes[j]) { outFlags[i] = 1; - result = true; } } } + bool result = mSupportedKeyCodes.size() > 0; return result; } @@ -1139,17 +1005,17 @@ private: // --- InstrumentedInputReader --- class InstrumentedInputReader : public InputReader { - std::shared_ptr<InputDevice> mNextDevice; + std::queue<std::shared_ptr<InputDevice>> mNextDevices; public: InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) - : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {} + : InputReader(eventHub, policy, listener), mFakeContext(this) {} virtual ~InstrumentedInputReader() {} - void setNextDevice(std::shared_ptr<InputDevice> device) { mNextDevice = device; } + void pushNextDevice(std::shared_ptr<InputDevice> device) { mNextDevices.push(device); } std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, const std::string& location = "") { @@ -1157,7 +1023,7 @@ public: identifier.name = name; identifier.location = location; int32_t generation = deviceId + 1; - return std::make_shared<InputDevice>(&mContext, deviceId, generation, identifier); + return std::make_shared<InputDevice>(&mFakeContext, deviceId, generation, identifier); } // Make the protected loopOnce method accessible to tests. @@ -1166,15 +1032,58 @@ public: protected: virtual std::shared_ptr<InputDevice> createDeviceLocked( int32_t eventHubId, const InputDeviceIdentifier& identifier) { - if (mNextDevice) { - std::shared_ptr<InputDevice> device(mNextDevice); - mNextDevice = nullptr; + if (!mNextDevices.empty()) { + std::shared_ptr<InputDevice> device(std::move(mNextDevices.front())); + mNextDevices.pop(); return device; } return InputReader::createDeviceLocked(eventHubId, identifier); } + // --- FakeInputReaderContext --- + class FakeInputReaderContext : public ContextImpl { + int32_t mGlobalMetaState; + bool mUpdateGlobalMetaStateWasCalled; + int32_t mGeneration; + + public: + FakeInputReaderContext(InputReader* reader) + : ContextImpl(reader), + mGlobalMetaState(0), + mUpdateGlobalMetaStateWasCalled(false), + mGeneration(1) {} + + virtual ~FakeInputReaderContext() {} + + void assertUpdateGlobalMetaStateWasCalled() { + ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled) + << "Expected updateGlobalMetaState() to have been called."; + mUpdateGlobalMetaStateWasCalled = false; + } + + void setGlobalMetaState(int32_t state) { mGlobalMetaState = state; } + + uint32_t getGeneration() { return mGeneration; } + + void updateGlobalMetaState() override { + mUpdateGlobalMetaStateWasCalled = true; + ContextImpl::updateGlobalMetaState(); + } + + int32_t getGlobalMetaState() override { + return mGlobalMetaState | ContextImpl::getGlobalMetaState(); + } + + int32_t bumpGeneration() override { + mGeneration = ContextImpl::bumpGeneration(); + return mGeneration; + } + } mFakeContext; + friend class InputReaderTest; + +public: + FakeInputReaderContext* getContext() { return &mFakeContext; } }; // --- InputReaderPolicyTest --- @@ -1182,8 +1091,8 @@ class InputReaderPolicyTest : public testing::Test { protected: sp<FakeInputReaderPolicy> mFakePolicy; - virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); } - virtual void TearDown() override { mFakePolicy.clear(); } + void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); } + void TearDown() override { mFakePolicy.clear(); } }; /** @@ -1198,20 +1107,21 @@ TEST_F(InputReaderPolicyTest, Viewports_GetCleared) { // We didn't add any viewports yet, so there shouldn't be any. std::optional<DisplayViewport> internalViewport = - mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); ASSERT_FALSE(internalViewport); // Add an internal viewport, then clear it mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL); + DISPLAY_ORIENTATION_0, uniqueId, NO_PORT, + ViewportType::INTERNAL); // Check matching by uniqueId internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId); ASSERT_TRUE(internalViewport); - ASSERT_EQ(ViewportType::VIEWPORT_INTERNAL, internalViewport->type); + ASSERT_EQ(ViewportType::INTERNAL, internalViewport->type); // Check matching by viewport type - internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); ASSERT_TRUE(internalViewport); ASSERT_EQ(uniqueId, internalViewport->uniqueId); @@ -1219,7 +1129,7 @@ TEST_F(InputReaderPolicyTest, Viewports_GetCleared) { // Make sure nothing is found after clear internalViewport = mFakePolicy->getDisplayViewportByUniqueId(uniqueId); ASSERT_FALSE(internalViewport); - internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + internalViewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); ASSERT_FALSE(internalViewport); } @@ -1233,26 +1143,30 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByType) { // Add an internal viewport mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, ViewportType::VIEWPORT_INTERNAL); + DISPLAY_ORIENTATION_0, internalUniqueId, NO_PORT, + ViewportType::INTERNAL); // Add an external viewport mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, ViewportType::VIEWPORT_EXTERNAL); + DISPLAY_ORIENTATION_0, externalUniqueId, NO_PORT, + ViewportType::EXTERNAL); // Add an virtual viewport mFakePolicy->addDisplayViewport(virtualDisplayId1, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, ViewportType::VIEWPORT_VIRTUAL); + DISPLAY_ORIENTATION_0, virtualUniqueId1, NO_PORT, + ViewportType::VIRTUAL); // Add another virtual viewport mFakePolicy->addDisplayViewport(virtualDisplayId2, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, ViewportType::VIEWPORT_VIRTUAL); + DISPLAY_ORIENTATION_0, virtualUniqueId2, NO_PORT, + ViewportType::VIRTUAL); // Check matching by type for internal std::optional<DisplayViewport> internalViewport = - mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); ASSERT_TRUE(internalViewport); ASSERT_EQ(internalUniqueId, internalViewport->uniqueId); // Check matching by type for external std::optional<DisplayViewport> externalViewport = - mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_EXTERNAL); + mFakePolicy->getDisplayViewportByType(ViewportType::EXTERNAL); ASSERT_TRUE(externalViewport); ASSERT_EQ(externalUniqueId, externalViewport->uniqueId); @@ -1260,7 +1174,7 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByType) { std::optional<DisplayViewport> virtualViewport1 = mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId1); ASSERT_TRUE(virtualViewport1); - ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport1->type); + ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport1->type); ASSERT_EQ(virtualUniqueId1, virtualViewport1->uniqueId); ASSERT_EQ(virtualDisplayId1, virtualViewport1->displayId); @@ -1268,7 +1182,7 @@ TEST_F(InputReaderPolicyTest, Viewports_GetByType) { std::optional<DisplayViewport> virtualViewport2 = mFakePolicy->getDisplayViewportByUniqueId(virtualUniqueId2); ASSERT_TRUE(virtualViewport2); - ASSERT_EQ(ViewportType::VIEWPORT_VIRTUAL, virtualViewport2->type); + ASSERT_EQ(ViewportType::VIRTUAL, virtualViewport2->type); ASSERT_EQ(virtualUniqueId2, virtualViewport2->uniqueId); ASSERT_EQ(virtualDisplayId2, virtualViewport2->displayId); } @@ -1285,8 +1199,8 @@ TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) { constexpr int32_t displayId1 = 2; constexpr int32_t displayId2 = 3; - std::vector<ViewportType> types = {ViewportType::VIEWPORT_INTERNAL, - ViewportType::VIEWPORT_EXTERNAL, ViewportType::VIEWPORT_VIRTUAL}; + std::vector<ViewportType> types = {ViewportType::INTERNAL, ViewportType::EXTERNAL, + ViewportType::VIRTUAL}; for (const ViewportType& type : types) { mFakePolicy->clearViewports(); // Add a viewport @@ -1321,10 +1235,51 @@ TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) { } /** + * When we have multiple internal displays make sure we always return the default display when + * querying by type. + */ +TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) { + const std::string uniqueId1 = "uniqueId1"; + const std::string uniqueId2 = "uniqueId2"; + constexpr int32_t nonDefaultDisplayId = 2; + static_assert(nonDefaultDisplayId != ADISPLAY_ID_DEFAULT, + "Test display ID should not be ADISPLAY_ID_DEFAULT"); + + // Add the default display first and ensure it gets returned. + mFakePolicy->clearViewports(); + mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT, + ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT, + ViewportType::INTERNAL); + + std::optional<DisplayViewport> viewport = + mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); + ASSERT_TRUE(viewport); + ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId); + ASSERT_EQ(ViewportType::INTERNAL, viewport->type); + + // Add the default display second to make sure order doesn't matter. + mFakePolicy->clearViewports(); + mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, uniqueId2, NO_PORT, + ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, uniqueId1, NO_PORT, + ViewportType::INTERNAL); + + viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); + ASSERT_TRUE(viewport); + ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId); + ASSERT_EQ(ViewportType::INTERNAL, viewport->type); +} + +/** * Check getDisplayViewportByPort */ TEST_F(InputReaderPolicyTest, Viewports_GetByPort) { - constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL; + constexpr ViewportType type = ViewportType::EXTERNAL; const std::string uniqueId1 = "uniqueId1"; const std::string uniqueId2 = "uniqueId2"; constexpr int32_t displayId1 = 1; @@ -1368,7 +1323,7 @@ protected: std::shared_ptr<FakeEventHub> mFakeEventHub; std::unique_ptr<InstrumentedInputReader> mReader; - virtual void SetUp() override { + void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1377,12 +1332,12 @@ protected: mFakeListener); } - virtual void TearDown() override { + void TearDown() override { mFakeListener.clear(); mFakePolicy.clear(); } - void addDevice(int32_t eventHubId, const std::string& name, uint32_t classes, + void addDevice(int32_t eventHubId, const std::string& name, Flags<InputDeviceClass> classes, const PropertyMap* configuration) { mFakeEventHub->addDevice(eventHubId, name, classes); @@ -1407,34 +1362,38 @@ protected: } FakeInputMapper& addDeviceWithFakeInputMapper(int32_t deviceId, int32_t eventHubId, - const std::string& name, uint32_t classes, - uint32_t sources, + const std::string& name, + Flags<InputDeviceClass> classes, uint32_t sources, const PropertyMap* configuration) { std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, name); FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, sources); - mReader->setNextDevice(device); + mReader->pushNextDevice(device); addDevice(eventHubId, name, classes, configuration); return mapper; } }; -TEST_F(InputReaderTest, GetInputDevices) { - ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", - INPUT_DEVICE_CLASS_KEYBOARD, nullptr)); - ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", - 0, nullptr)); // no classes so device will be ignored +TEST_F(InputReaderTest, ReaderGetInputDevices) { + ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0), + nullptr)); // no classes so device will be ignored - std::vector<InputDeviceInfo> inputDevices; - mReader->getInputDevices(inputDevices); + const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices(); ASSERT_EQ(1U, inputDevices.size()); ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId()); ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, inputDevices[0].getKeyboardType()); ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, inputDevices[0].getSources()); ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size()); +} + +TEST_F(InputReaderTest, PolicyGetInputDevices) { + ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored", Flags<InputDeviceClass>(0), + nullptr)); // no classes so device will be ignored // Should also have received a notification describing the new input devices. - inputDevices = mFakePolicy->getInputDevices(); + const std::vector<InputDeviceInfo>& inputDevices = mFakePolicy->getInputDevices(); ASSERT_EQ(1U, inputDevices.size()); ASSERT_EQ(END_RESERVED_ID + 1, inputDevices[0].getId()); ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str()); @@ -1443,14 +1402,35 @@ TEST_F(InputReaderTest, GetInputDevices) { ASSERT_EQ(size_t(0), inputDevices[0].getMotionRanges().size()); } +TEST_F(InputReaderTest, GetMergedInputDevices) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1}; + // Add two subdevices to device + std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake"); + // Must add at least one mapper or the device will be ignored! + device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD); + device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD); + + // Push same device instance for next device to be added, so they'll have same identifier. + mReader->pushNextDevice(device); + mReader->pushNextDevice(device); + ASSERT_NO_FATAL_FAILURE( + addDevice(eventHubIds[0], "fake1", InputDeviceClass::KEYBOARD, nullptr)); + ASSERT_NO_FATAL_FAILURE( + addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr)); + + // Two devices will be merged to one input device as they have same identifier + ASSERT_EQ(1U, mReader->getInputDevices().size()); +} + TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD); constexpr int32_t eventHubId = 1; std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake"); // Must add at least one mapper or the device will be ignored! device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD); - mReader->setNextDevice(device); + mReader->pushNextDevice(device); ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr)); @@ -1482,7 +1462,7 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, @@ -1515,7 +1495,7 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, @@ -1548,7 +1528,7 @@ TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, @@ -1581,7 +1561,7 @@ TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, @@ -1623,7 +1603,7 @@ TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { constexpr int32_t eventHubId = 1; - addDevice(eventHubId, "ignored", INPUT_DEVICE_CLASS_KEYBOARD, nullptr); + addDevice(eventHubId, "ignored", InputDeviceClass::KEYBOARD, nullptr); NotifyConfigurationChangedArgs args; @@ -1633,7 +1613,7 @@ TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChange TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; constexpr int32_t eventHubId = 1; FakeInputMapper& mapper = addDeviceWithFakeInputMapper(deviceId, eventHubId, "fake", deviceClass, @@ -1654,12 +1634,12 @@ TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { TEST_F(InputReaderTest, DeviceReset_RandomId) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; constexpr int32_t eventHubId = 1; std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake"); // Must add at least one mapper or the device will be ignored! device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD); - mReader->setNextDevice(device); + mReader->pushNextDevice(device); ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr)); NotifyDeviceResetArgs resetArgs; @@ -1687,12 +1667,12 @@ TEST_F(InputReaderTest, DeviceReset_RandomId) { TEST_F(InputReaderTest, DeviceReset_GenerateIdWithInputReaderSource) { constexpr int32_t deviceId = 1; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; constexpr int32_t eventHubId = 1; std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake"); // Must add at least one mapper or the device will be ignored! device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD); - mReader->setNextDevice(device); + mReader->pushNextDevice(device); ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr)); NotifyDeviceResetArgs resetArgs; @@ -1702,13 +1682,13 @@ TEST_F(InputReaderTest, DeviceReset_GenerateIdWithInputReaderSource) { TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; - constexpr uint32_t deviceClass = INPUT_DEVICE_CLASS_KEYBOARD; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; constexpr int32_t eventHubId = 1; const char* DEVICE_LOCATION = "USB1"; std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION); FakeInputMapper& mapper = device->addMapper<FakeInputMapper>(eventHubId, AINPUT_SOURCE_TOUCHSCREEN); - mReader->setNextDevice(device); + mReader->pushNextDevice(device); const uint8_t hdmi1 = 1; @@ -1718,9 +1698,11 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { // Add default and second display. mFakePolicy->clearViewports(); mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL); + DISPLAY_ORIENTATION_0, "local:0", NO_PORT, + ViewportType::INTERNAL); mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL); + DISPLAY_ORIENTATION_0, "local:1", hdmi1, + ViewportType::EXTERNAL); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); mReader->loopOnce(); @@ -1743,6 +1725,73 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } +TEST_F(InputReaderTest, WhenEnabledChanges_AllSubdevicesAreUpdated) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; + constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1}; + std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake"); + // Must add at least one mapper or the device will be ignored! + device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD); + device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD); + mReader->pushNextDevice(device); + mReader->pushNextDevice(device); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr)); + + NotifyDeviceResetArgs resetArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(deviceId, resetArgs.deviceId); + ASSERT_TRUE(device->isEnabled()); + ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0])); + ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1])); + + disableDevice(deviceId); + mReader->loopOnce(); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(deviceId, resetArgs.deviceId); + ASSERT_FALSE(device->isEnabled()); + ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0])); + ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[1])); + + enableDevice(deviceId); + mReader->loopOnce(); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(deviceId, resetArgs.deviceId); + ASSERT_TRUE(device->isEnabled()); + ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[0])); + ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1])); +} + +TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToSubdeviceMappers) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD; + constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1}; + // Add two subdevices to device + std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake"); + FakeInputMapper& mapperDevice1 = + device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD); + FakeInputMapper& mapperDevice2 = + device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD); + mReader->pushNextDevice(device); + mReader->pushNextDevice(device); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", deviceClass, nullptr)); + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[1], "fake2", deviceClass, nullptr)); + + mapperDevice1.setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); + mapperDevice2.setKeyCodeState(AKEYCODE_B, AKEY_STATE_DOWN); + + ASSERT_EQ(AKEY_STATE_DOWN, + mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_A)); + ASSERT_EQ(AKEY_STATE_DOWN, + mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_B)); + ASSERT_EQ(AKEY_STATE_UNKNOWN, + mReader->getKeyCodeState(deviceId, AINPUT_SOURCE_KEYBOARD, AKEYCODE_C)); +} + // --- InputReaderIntegrationTest --- // These tests create and interact with the InputReader only through its interface. @@ -1756,7 +1805,7 @@ protected: sp<FakeInputReaderPolicy> mFakePolicy; sp<InputReaderInterface> mReader; - virtual void SetUp() override { + void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); mTestListener = new TestInputListener(2000ms /*eventHappenedTimeout*/, 30ms /*eventDidNotHappenTimeout*/); @@ -1771,7 +1820,7 @@ protected: ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled()); } - virtual void TearDown() override { + void TearDown() override { ASSERT_EQ(mReader->stop(), OK); mTestListener.clear(); mFakePolicy.clear(); @@ -1812,20 +1861,17 @@ TEST_F(InputReaderIntegrationTest, AddNewDevice) { ASSERT_EQ(initialNumDevices + 1, mFakePolicy->getInputDevices().size()); // Find the test device by its name. - std::vector<InputDeviceInfo> inputDevices; - mReader->getInputDevices(inputDevices); - InputDeviceInfo* keyboardInfo = nullptr; - const char* keyboardName = keyboard->getName(); - for (unsigned int i = 0; i < initialNumDevices + 1; i++) { - if (!strcmp(inputDevices[i].getIdentifier().name.c_str(), keyboardName)) { - keyboardInfo = &inputDevices[i]; - break; - } - } - ASSERT_NE(keyboardInfo, nullptr); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, keyboardInfo->getKeyboardType()); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyboardInfo->getSources()); - ASSERT_EQ(0U, keyboardInfo->getMotionRanges().size()); + const std::vector<InputDeviceInfo> inputDevices = mReader->getInputDevices(); + const auto& it = + std::find_if(inputDevices.begin(), inputDevices.end(), + [&keyboard](const InputDeviceInfo& info) { + return info.getIdentifier().name == keyboard->getName(); + }); + + ASSERT_NE(it, inputDevices.end()); + ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, it->getKeyboardType()); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, it->getSources()); + ASSERT_EQ(0U, it->getMotionRanges().size()); keyboard.reset(); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); @@ -1885,12 +1931,12 @@ class TouchIntegrationTest : public InputReaderIntegrationTest { protected: const std::string UNIQUE_ID = "local:0"; - virtual void SetUp() override { + void SetUp() override { InputReaderIntegrationTest::SetUp(); // At least add an internal display. setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT, - ViewportType::VIEWPORT_INTERNAL); + ViewportType::INTERNAL); mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT)); ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged()); @@ -2020,33 +2066,32 @@ protected: static const int32_t DEVICE_ID; static const int32_t DEVICE_GENERATION; static const int32_t DEVICE_CONTROLLER_NUMBER; - static const uint32_t DEVICE_CLASSES; + static const Flags<InputDeviceClass> DEVICE_CLASSES; static const int32_t EVENTHUB_ID; std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; sp<TestInputListener> mFakeListener; - FakeInputReaderContext* mFakeContext; - + std::unique_ptr<InstrumentedInputReader> mReader; std::shared_ptr<InputDevice> mDevice; - virtual void SetUp() override { + void SetUp() override { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); - - mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, 0); + mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, + mFakeListener); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; identifier.location = DEVICE_LOCATION; - mDevice = std::make_shared<InputDevice>(mFakeContext, DEVICE_ID, DEVICE_GENERATION, + mDevice = std::make_shared<InputDevice>(mReader->getContext(), DEVICE_ID, DEVICE_GENERATION, identifier); + mReader->pushNextDevice(mDevice); + mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, Flags<InputDeviceClass>(0)); + mReader->loopOnce(); } - virtual void TearDown() override { - mDevice = nullptr; - delete mFakeContext; + void TearDown() override { mFakeListener.clear(); mFakePolicy.clear(); } @@ -2057,14 +2102,14 @@ const char* InputDeviceTest::DEVICE_LOCATION = "USB1"; const int32_t InputDeviceTest::DEVICE_ID = END_RESERVED_ID + 1000; const int32_t InputDeviceTest::DEVICE_GENERATION = 2; const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0; -const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD - | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK; +const Flags<InputDeviceClass> InputDeviceTest::DEVICE_CLASSES = + InputDeviceClass::KEYBOARD | InputDeviceClass::TOUCH | InputDeviceClass::JOYSTICK; const int32_t InputDeviceTest::EVENTHUB_ID = 1; TEST_F(InputDeviceTest, ImmutableProperties) { ASSERT_EQ(DEVICE_ID, mDevice->getId()); ASSERT_STREQ(DEVICE_NAME, mDevice->getName().c_str()); - ASSERT_EQ(0U, mDevice->getClasses()); + ASSERT_EQ(Flags<InputDeviceClass>(0), mDevice->getClasses()); } TEST_F(InputDeviceTest, WhenDeviceCreated_EnabledIsFalse) { @@ -2233,8 +2278,7 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { // Prepare displays. mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi, - ViewportType::VIEWPORT_INTERNAL); + DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi, ViewportType::INTERNAL); mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), InputReaderConfiguration::CHANGE_DISPLAY_INFO); ASSERT_TRUE(mDevice->isEnabled()); @@ -2260,33 +2304,27 @@ protected: static const int32_t DEVICE_ID; static const int32_t DEVICE_GENERATION; static const int32_t DEVICE_CONTROLLER_NUMBER; - static const uint32_t DEVICE_CLASSES; + static const Flags<InputDeviceClass> DEVICE_CLASSES; static const int32_t EVENTHUB_ID; std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; sp<TestInputListener> mFakeListener; - FakeInputReaderContext* mFakeContext; - InputDevice* mDevice; + std::unique_ptr<InstrumentedInputReader> mReader; + std::shared_ptr<InputDevice> mDevice; - virtual void SetUp(uint32_t classes) { + virtual void SetUp(Flags<InputDeviceClass> classes) { mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); - mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); - InputDeviceIdentifier identifier; - identifier.name = DEVICE_NAME; - identifier.location = DEVICE_LOCATION; - mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, identifier); - - mFakeEventHub->addDevice(EVENTHUB_ID, DEVICE_NAME, classes); + mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, + mFakeListener); + mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); } - virtual void SetUp() override { SetUp(DEVICE_CLASSES); } + void SetUp() override { SetUp(DEVICE_CLASSES); } - virtual void TearDown() override { - delete mDevice; - delete mFakeContext; + void TearDown() override { mFakeListener.clear(); mFakePolicy.clear(); } @@ -2297,16 +2335,33 @@ protected: void configureDevice(uint32_t changes) { if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mFakeContext->updatePointerDisplay(); + mReader->requestRefreshConfiguration(changes); + mReader->loopOnce(); } mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); } + std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, + const std::string& location, int32_t eventHubId, + Flags<InputDeviceClass> classes) { + InputDeviceIdentifier identifier; + identifier.name = name; + identifier.location = location; + std::shared_ptr<InputDevice> device = + std::make_shared<InputDevice>(mReader->getContext(), deviceId, DEVICE_GENERATION, + identifier); + mReader->pushNextDevice(device); + mFakeEventHub->addDevice(eventHubId, name, classes); + mReader->loopOnce(); + return device; + } + template <class T, typename... Args> T& addMapperAndConfigure(Args... args) { T& mapper = mDevice->addMapper<T>(EVENTHUB_ID, args...); configureDevice(0); mDevice->reset(ARBITRARY_TIME); + mapper.reset(ARBITRARY_TIME); return mapper; } @@ -2322,8 +2377,7 @@ protected: mFakePolicy->clearViewports(); } - static void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code, - int32_t value) { + void process(InputMapper& mapper, nsecs_t when, int32_t type, int32_t code, int32_t value) { RawEvent event; event.when = when; event.deviceId = mapper.getDeviceContext().getEventHubId(); @@ -2331,6 +2385,7 @@ protected: event.code = code; event.value = value; mapper.process(&event); + mReader->loopOnce(); } static void assertMotionRange(const InputDeviceInfo& info, @@ -2374,7 +2429,8 @@ const char* InputMapperTest::DEVICE_LOCATION = "USB1"; const int32_t InputMapperTest::DEVICE_ID = END_RESERVED_ID + 1000; const int32_t InputMapperTest::DEVICE_GENERATION = 2; const int32_t InputMapperTest::DEVICE_CONTROLLER_NUMBER = 0; -const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests +const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES = + Flags<InputDeviceClass>(0); // not needed for current tests const int32_t InputMapperTest::EVENTHUB_ID = 1; // --- SwitchInputMapperTest --- @@ -2434,8 +2490,8 @@ protected: * orientation. */ void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) { - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - orientation, UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL); + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID, + NO_PORT, ViewportType::INTERNAL); } void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper, @@ -2471,10 +2527,16 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { const int32_t USAGE_UNKNOWN = 0x07ffff; mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, POLICY_FLAG_WAKE); + mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE); KeyboardInputMapper& mapper = addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); + // Initial metastate to AMETA_NONE. + ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); + mapper.updateMetaState(AKEYCODE_NUM_LOCK); // Key down by scan code. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); @@ -2569,13 +2631,17 @@ TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0); mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); + mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0); KeyboardInputMapper& mapper = addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - // Initial metastate. - ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + // Initial metastate to AMETA_NONE. + ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); + mapper.updateMetaState(AKEYCODE_NUM_LOCK); // Metakey down. process(mapper, ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1); @@ -2583,7 +2649,7 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState()); - ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled()); // Key down. process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1); @@ -2602,7 +2668,7 @@ TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AMETA_NONE, args.metaState); ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); - ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); + ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled()); } TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) { @@ -2740,7 +2806,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { // ^--- already checked by the previous test setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, - UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL); + UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0); @@ -2750,7 +2816,7 @@ TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { constexpr int32_t newDisplayId = 2; clearViewports(); setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, - UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_INTERNAL); + UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); process(mapper, ARBITRARY_TIME, EV_KEY, KEY_UP, 0); @@ -2807,6 +2873,9 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) KeyboardInputMapper& mapper = addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); + // Initialize metastate to AMETA_NUM_LOCK_ON. + ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); + mapper.updateMetaState(AKEYCODE_NUM_LOCK); // Initialization should have turned all of the lights off. ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); @@ -2862,6 +2931,43 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); } +TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) { + mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_BUTTON_A, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_B, 0, AKEYCODE_BUTTON_B, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_X, 0, AKEYCODE_BUTTON_X, 0); + mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0); + + KeyboardInputMapper& mapper = + addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); + + // Initial metastate should be AMETA_NONE as no meta keys added. + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + // Meta state should be AMETA_NONE after reset + mapper.reset(ARBITRARY_TIME); + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + // Meta state should be AMETA_NONE with update, as device doesn't have the keys. + mapper.updateMetaState(AKEYCODE_NUM_LOCK); + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + + NotifyKeyArgs args; + // Press button "A" + process(mapper, ARBITRARY_TIME, EV_KEY, BTN_A, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(AMETA_NONE, args.metaState); + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode); + + // Button up. + process(mapper, ARBITRARY_TIME + 2, EV_KEY, BTN_A, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(AMETA_NONE, args.metaState); + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode); +} + TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { // keyboard 1. mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); @@ -2871,15 +2977,13 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { // keyboard 2. const std::string USB2 = "USB2"; + const std::string DEVICE_NAME2 = "KEYBOARD2"; constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; - InputDeviceIdentifier identifier; - identifier.name = "KEYBOARD2"; - identifier.location = USB2; - std::unique_ptr<InputDevice> device2 = - std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, - identifier); - mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/); + std::shared_ptr<InputDevice> device2 = + newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, + Flags<InputDeviceClass>(0)); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); @@ -2911,9 +3015,9 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { // Prepare second display. constexpr int32_t newDisplayId = 2; setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, - UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL); + UNIQUE_ID, hdmi1, ViewportType::INTERNAL); setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, - SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL); + SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL); // Default device will reconfigure above, need additional reconfiguration for another device. device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), InputReaderConfiguration::CHANGE_DISPLAY_INFO); @@ -2942,13 +3046,80 @@ TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { AKEYCODE_DPAD_LEFT, newDisplayId)); } +TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { + mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + KeyboardInputMapper& mapper = + addMapperAndConfigure<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + // Initial metastate to AMETA_NONE. + ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); + mapper.updateMetaState(AKEYCODE_NUM_LOCK); + + // Initialization should have turned all of the lights off. + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); + + // Toggle caps lock on. + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); + + // Toggle num lock on. + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); + + // Toggle scroll lock on. + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); + process(mapper, ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); + + mFakeEventHub->removeDevice(EVENTHUB_ID); + mReader->loopOnce(); + + // keyboard 2 should default toggle keys. + const std::string USB2 = "USB2"; + const std::string DEVICE_NAME2 = "KEYBOARD2"; + constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; + std::shared_ptr<InputDevice> device2 = + newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, + Flags<InputDeviceClass>(0)); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + KeyboardInputMapper& mapper2 = + device2->addMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); + device2->reset(ARBITRARY_TIME); + + ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL)); + ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML)); + ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, + mapper2.getMetaState()); +} + // --- KeyboardInputMapperTest_ExternalDevice --- class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest { protected: - virtual void SetUp() override { - InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL); - } + void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); } }; TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) { @@ -3036,7 +3207,7 @@ protected: std::shared_ptr<FakePointerController> mFakePointerController; - virtual void SetUp() override { + void SetUp() override { InputMapperTest::SetUp(); mFakePointerController = std::make_shared<FakePointerController>(); @@ -3048,7 +3219,7 @@ protected: void prepareDisplay(int32_t orientation) { const std::string uniqueId = "local:0"; - const ViewportType viewportType = ViewportType::VIEWPORT_INTERNAL; + const ViewportType viewportType = ViewportType::INTERNAL; setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, uniqueId, NO_PORT, viewportType); } @@ -3138,7 +3309,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaStat addConfigurationProperty("cursor.mode", "navigation"); CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyMotionArgs args; @@ -3766,10 +3937,10 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { // Disable pointer capture and check that the device generation got bumped // and events are generated the usual way. - const uint32_t generation = mFakeContext->getGeneration(); + const uint32_t generation = mReader->getContext()->getGeneration(); mFakePolicy->setPointerCapture(false); configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); - ASSERT_TRUE(mFakeContext->getGeneration() != generation); + ASSERT_TRUE(mReader->getContext()->getGeneration() != generation); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); @@ -3793,8 +3964,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { constexpr int32_t SECOND_DISPLAY_ID = 1; const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1"; mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0, - SECOND_DISPLAY_UNIQUE_ID, NO_PORT, - ViewportType::VIEWPORT_EXTERNAL); + SECOND_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::EXTERNAL); mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID); configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); @@ -3921,8 +4091,8 @@ const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = { }; void TouchInputMapperTest::prepareDisplay(int32_t orientation, std::optional<uint8_t> port) { - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, - UNIQUE_ID, port, ViewportType::VIEWPORT_INTERNAL); + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID, + port, ViewportType::INTERNAL); } void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optional<uint8_t> port) { @@ -3931,9 +4101,9 @@ void TouchInputMapperTest::prepareSecondaryDisplay(ViewportType type, std::optio } void TouchInputMapperTest::prepareVirtualDisplay(int32_t orientation) { - setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, - VIRTUAL_DISPLAY_HEIGHT, orientation, - VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::VIEWPORT_VIRTUAL); + setDisplayInfoAndReconfigure(VIRTUAL_DISPLAY_ID, VIRTUAL_DISPLAY_WIDTH, VIRTUAL_DISPLAY_HEIGHT, + orientation, VIRTUAL_DISPLAY_UNIQUE_ID, NO_PORT, + ViewportType::VIRTUAL); } void TouchInputMapperTest::prepareVirtualKeys() { @@ -4185,7 +4355,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNor prepareVirtualKeys(); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyKeyArgs args; @@ -4235,7 +4405,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfB prepareVirtualKeys(); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyKeyArgs keyArgs; @@ -4356,7 +4526,7 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMoves prepareVirtualKeys(); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyMotionArgs motionArgs; @@ -4431,7 +4601,7 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture_VirtualDispl prepareVirtualKeys(); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyMotionArgs motionArgs; @@ -4527,7 +4697,7 @@ TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { prepareVirtualKeys(); SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyMotionArgs motionArgs; @@ -5422,7 +5592,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackin prepareVirtualKeys(); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyMotionArgs motionArgs; @@ -5698,7 +5868,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingId prepareVirtualKeys(); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyMotionArgs motionArgs; @@ -5873,7 +6043,7 @@ TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithSlots) { prepareVirtualKeys(); MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); + mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); NotifyMotionArgs motionArgs; @@ -6786,7 +6956,7 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { const uint8_t hdmi1 = 0; const uint8_t hdmi2 = 1; const std::string secondaryUniqueId = "uniqueId2"; - constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL; + constexpr ViewportType type = ViewportType::EXTERNAL; addConfigurationProperty("touch.deviceType", "touchScreen"); prepareAxes(POSITION); @@ -6827,7 +6997,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); - prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL); + prepareSecondaryDisplay(ViewportType::EXTERNAL); prepareDisplay(DISPLAY_ORIENTATION_0); prepareAxes(POSITION); @@ -6853,15 +7023,13 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Create the second touch screen device, and enable multi fingers. const std::string USB2 = "USB2"; + const std::string DEVICE_NAME2 = "TOUCHSCREEN2"; constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; - InputDeviceIdentifier identifier; - identifier.name = "TOUCHSCREEN2"; - identifier.location = USB2; - std::unique_ptr<InputDevice> device2 = - std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, - identifier); - mFakeEventHub->addDevice(SECOND_EVENTHUB_ID, DEVICE_NAME, 0 /*classes*/); + std::shared_ptr<InputDevice> device2 = + newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, + Flags<InputDeviceClass>(0)); + mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0 /*flat*/, 0 /*fuzz*/); mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, @@ -6894,11 +7062,11 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Create displays. prepareDisplay(DISPLAY_ORIENTATION_0, hdmi1); - prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL, hdmi2); + prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2); // Default device will reconfigure above, need additional reconfiguration for another device. device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::CHANGE_DISPLAY_INFO); + InputReaderConfiguration::CHANGE_DISPLAY_INFO); // Two fingers down at default display. int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; @@ -7005,7 +7173,7 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { constexpr uint8_t hdmi2 = 1; const std::string secondaryUniqueId = "uniqueId2"; - constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL; + constexpr ViewportType type = ViewportType::EXTERNAL; mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2); @@ -7373,9 +7541,7 @@ TEST_F(MultiTouchInputMapperTest, Process_ShouldHandlePalmToolType_KeepFirstPoin class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest { protected: - virtual void SetUp() override { - InputMapperTest::SetUp(DEVICE_CLASSES | INPUT_DEVICE_CLASS_EXTERNAL); - } + void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); } }; /** @@ -7399,7 +7565,7 @@ TEST_F(MultiTouchInputMapperTest_ExternalDevice, Viewports_Fallback) { ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId); // Expect the event to be sent to the external viewport if it is present. - prepareSecondaryDisplay(ViewportType::VIEWPORT_EXTERNAL); + prepareSecondaryDisplay(ViewportType::EXTERNAL); processPosition(mapper, 100, 100); processSync(mapper); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); @@ -7413,7 +7579,7 @@ class MultiTouchInputMapperTest_SurfaceRange : public MultiTouchInputMapperTest protected: void halfDisplayToCenterHorizontal(int32_t orientation) { std::optional<DisplayViewport> internalViewport = - mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); // Half display to (width/4, 0, width * 3/4, height) to make display has offset. internalViewport->orientation = orientation; diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index b0d3e3bde0..b5e6ae943d 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -2,9 +2,15 @@ cc_library_shared { name: "libpowermanager", srcs: [ - "IPowerManager.cpp", - "Temperature.cpp", + "BatterySaverPolicyConfig.cpp", "CoolingDevice.cpp", + "ParcelDuration.cpp", + "PowerHalController.cpp", + "PowerHalLoader.cpp", + "PowerHalWrapper.cpp", + "PowerSaveState.cpp", + "Temperature.cpp", + "WorkSource.cpp", ":libpowermanager_aidl", ], @@ -17,9 +23,13 @@ cc_library_shared { }, shared_libs: [ - "libutils", "libbinder", - "liblog" + "libhidlbase", + "liblog", + "libutils", + "android.hardware.power@1.0", + "android.hardware.power@1.1", + "android.hardware.power-cpp", ], cflags: [ @@ -34,22 +44,3 @@ cc_library_shared { "include", ], } - -cc_test { - name: "thermalmanager-test", - srcs: ["IThermalManagerTest.cpp", - ], - cflags: [ - "-Wall", - "-Werror", - "-Wextra", - ], - shared_libs: [ - "libbase", - "libhidlbase", - "liblog", - "libpowermanager", - "libbinder", - "libutils", - ], -} diff --git a/services/powermanager/BatterySaverPolicyConfig.cpp b/services/powermanager/BatterySaverPolicyConfig.cpp new file mode 100644 index 0000000000..ee55b6b439 --- /dev/null +++ b/services/powermanager/BatterySaverPolicyConfig.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BatterySaverPolicyConfig" + +#include <android/BatterySaverPolicyConfig.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +status_t BatterySaverPolicyConfig::readDeviceSpecificSettings(const android::Parcel *parcel) { + int32_t num = 0; + status_t ret = parcel->readInt32(&num); + if (ret != OK) { + return ret; + } + for (int i = 0; i < num; i++) { + String16 key, val; + ret = parcel->readString16(&key) ?: + parcel->readString16(&val); + if (ret != OK) { + return ret; + } + mDeviceSpecificSettings.emplace_back(key, val); + } + return ret; +} + +status_t BatterySaverPolicyConfig::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->readFloat(&mAdjustBrightnessFactor) + ?: parcel->readBool(&mAdvertiseIsEnabled) + ?: parcel->readBool(&mDeferFullBackup) + ?: parcel->readBool(&mDeferKeyValueBackup) + ?: readDeviceSpecificSettings(parcel) + ?: parcel->readBool(&mDisableAnimation) + ?: parcel->readBool(&mDisableAod) + ?: parcel->readBool(&mDisableLaunchBoost) + ?: parcel->readBool(&mDisableOptionalSensors) + ?: parcel->readBool(&mDisableSoundTrigger) + ?: parcel->readBool(&mDisableVibration) + ?: parcel->readBool(&mEnableAdjustBrightness) + ?: parcel->readBool(&mEnableDataSaver) + ?: parcel->readBool(&mEnableFirewall) + ?: parcel->readBool(&mEnableNightMode) + ?: parcel->readBool(&mEnableQuickDoze) + ?: parcel->readBool(&mForceAllAppsStandby) + ?: parcel->readBool(&mForceBackgroundCheck) + ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode)); +} + +status_t BatterySaverPolicyConfig::writeDeviceSpecificSettings(android::Parcel *parcel) const { + status_t ret = parcel->writeInt32(mDeviceSpecificSettings.size()); + if (ret != OK) { + return ret; + } + for (auto& settings : mDeviceSpecificSettings) { + ret = parcel->writeString16(settings.first) ?: + parcel->writeString16(settings.second); + if (ret != OK) { + return ret; + } + } + return ret; +} + +status_t BatterySaverPolicyConfig::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->writeFloat(mAdjustBrightnessFactor) + ?: parcel->writeBool(mAdvertiseIsEnabled) + ?: parcel->writeBool(mDeferFullBackup) + ?: parcel->writeBool(mDeferKeyValueBackup) + ?: writeDeviceSpecificSettings(parcel) + ?: parcel->writeBool(mDisableAnimation) + ?: parcel->writeBool(mDisableAod) + ?: parcel->writeBool(mDisableLaunchBoost) + ?: parcel->writeBool(mDisableOptionalSensors) + ?: parcel->writeBool(mDisableSoundTrigger) + ?: parcel->writeBool(mDisableVibration) + ?: parcel->writeBool(mEnableAdjustBrightness) + ?: parcel->writeBool(mEnableDataSaver) + ?: parcel->writeBool(mEnableFirewall) + ?: parcel->writeBool(mEnableNightMode) + ?: parcel->writeBool(mEnableQuickDoze) + ?: parcel->writeBool(mForceAllAppsStandby) + ?: parcel->writeBool(mForceBackgroundCheck) + ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode)); +} + +} // namespace android::os diff --git a/services/powermanager/IPowerManager.cpp b/services/powermanager/IPowerManager.cpp deleted file mode 100644 index ea3a831c13..0000000000 --- a/services/powermanager/IPowerManager.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "IPowerManager" -//#define LOG_NDEBUG 0 -#include <utils/Log.h> - -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> - -#include <powermanager/IPowerManager.h> - -namespace android { - -class BpPowerManager : public BpInterface<IPowerManager> -{ -public: - explicit BpPowerManager(const sp<IBinder>& impl) - : BpInterface<IPowerManager>(impl) - { - } - - virtual status_t acquireWakeLock(int flags, const sp<IBinder>& lock, const String16& tag, - const String16& packageName, bool isOneWay) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - - data.writeStrongBinder(lock); - data.writeInt32(flags); - data.writeString16(tag); - data.writeString16(packageName); - data.writeInt32(0); // no WorkSource - data.writeString16(NULL, 0); // no history tag - return remote()->transact(ACQUIRE_WAKE_LOCK, data, &reply, - isOneWay ? IBinder::FLAG_ONEWAY : 0); - } - - virtual status_t acquireWakeLockWithUid(int flags, const sp<IBinder>& lock, const String16& tag, - const String16& packageName, int uid, bool isOneWay) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - - data.writeStrongBinder(lock); - data.writeInt32(flags); - data.writeString16(tag); - data.writeString16(packageName); - data.writeInt32(uid); // uid to blame for the work - return remote()->transact(ACQUIRE_WAKE_LOCK_UID, data, &reply, - isOneWay ? IBinder::FLAG_ONEWAY : 0); - } - - virtual status_t releaseWakeLock(const sp<IBinder>& lock, int flags, bool isOneWay) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeStrongBinder(lock); - data.writeInt32(flags); - return remote()->transact(RELEASE_WAKE_LOCK, data, &reply, - isOneWay ? IBinder::FLAG_ONEWAY : 0); - } - - virtual status_t updateWakeLockUids(const sp<IBinder>& lock, int len, const int *uids, - bool isOneWay) { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeStrongBinder(lock); - data.writeInt32Array(len, uids); - return remote()->transact(UPDATE_WAKE_LOCK_UIDS, data, &reply, - isOneWay ? IBinder::FLAG_ONEWAY : 0); - } - - virtual status_t powerHint(int hintId, int param) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeInt32(hintId); - data.writeInt32(param); - // This FLAG_ONEWAY is in the .aidl, so there is no way to disable it - return remote()->transact(POWER_HINT, data, &reply, IBinder::FLAG_ONEWAY); - } - - virtual status_t goToSleep(int64_t event_time_ms, int reason, int flags) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeInt64(event_time_ms); - data.writeInt32(reason); - data.writeInt32(flags); - return remote()->transact(GO_TO_SLEEP, data, &reply, 0); - } - - virtual status_t reboot(bool confirm, const String16& reason, bool wait) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeInt32(confirm); - data.writeString16(reason); - data.writeInt32(wait); - return remote()->transact(REBOOT, data, &reply, 0); - } - - virtual status_t shutdown(bool confirm, const String16& reason, bool wait) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeInt32(confirm); - data.writeString16(reason); - data.writeInt32(wait); - return remote()->transact(SHUTDOWN, data, &reply, 0); - } - - virtual status_t crash(const String16& message) - { - Parcel data, reply; - data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); - data.writeString16(message); - return remote()->transact(CRASH, data, &reply, 0); - } -}; - -IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager"); - -// ---------------------------------------------------------------------------- - -}; // namespace android diff --git a/services/powermanager/ParcelDuration.cpp b/services/powermanager/ParcelDuration.cpp new file mode 100644 index 0000000000..c0ab3809a6 --- /dev/null +++ b/services/powermanager/ParcelDuration.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ParcelDuration" + +#include <android/ParcelDuration.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +status_t ParcelDuration::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->readInt64(&mSeconds) ?: parcel->readInt32(&mNanos); +} + +status_t ParcelDuration::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->writeInt64(mSeconds) ?: parcel->writeInt32(mNanos); +} + +} // namespace android::os diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp new file mode 100644 index 0000000000..178f545b2e --- /dev/null +++ b/services/powermanager/PowerHalController.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalController" +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <powermanager/PowerHalController.h> +#include <powermanager/PowerHalLoader.h> +#include <utils/Log.h> + +using namespace android::hardware::power; + +namespace android { + +namespace power { + +// ------------------------------------------------------------------------------------------------- + +std::unique_ptr<HalWrapper> HalConnector::connect() { + sp<IPower> halAidl = PowerHalLoader::loadAidl(); + if (halAidl) { + return std::make_unique<AidlHalWrapper>(halAidl); + } + sp<V1_0::IPower> halHidlV1_0 = PowerHalLoader::loadHidlV1_0(); + sp<V1_1::IPower> halHidlV1_1 = PowerHalLoader::loadHidlV1_1(); + if (halHidlV1_1) { + return std::make_unique<HidlHalWrapperV1_1>(halHidlV1_0, halHidlV1_1); + } + if (halHidlV1_0) { + return std::make_unique<HidlHalWrapperV1_0>(halHidlV1_0); + } + return nullptr; +} + +void HalConnector::reset() { + PowerHalLoader::unloadAll(); +} + +// ------------------------------------------------------------------------------------------------- + +void PowerHalController::init() { + initHal(); +} + +// Check validity of current handle to the power HAL service, and create a new +// one if necessary. +std::shared_ptr<HalWrapper> PowerHalController::initHal() { + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + if (mConnectedHal == nullptr) { + mConnectedHal = mHalConnector->connect(); + if (mConnectedHal == nullptr) { + // Unable to connect to Power HAL service. Fallback to default. + return mDefaultHal; + } + } + return mConnectedHal; +} + +// Check if a call to Power HAL function failed; if so, log the failure and +// invalidate the current Power HAL handle. +HalResult PowerHalController::processHalResult(HalResult result, const char* fnName) { + if (result == HalResult::FAILED) { + ALOGE("%s() failed: power HAL service not available.", fnName); + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + // Drop Power HAL handle. This will force future api calls to reconnect. + mConnectedHal = nullptr; + mHalConnector->reset(); + } + return result; +} + +HalResult PowerHalController::setBoost(Boost boost, int32_t durationMs) { + std::shared_ptr<HalWrapper> handle = initHal(); + auto result = handle->setBoost(boost, durationMs); + return processHalResult(result, "setBoost"); +} + +HalResult PowerHalController::setMode(Mode mode, bool enabled) { + std::shared_ptr<HalWrapper> handle = initHal(); + auto result = handle->setMode(mode, enabled); + return processHalResult(result, "setMode"); +} + +} // namespace power + +} // namespace android diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp new file mode 100644 index 0000000000..1f1b43a607 --- /dev/null +++ b/services/powermanager/PowerHalLoader.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalLoader" + +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/IPower.h> +#include <binder/IServiceManager.h> +#include <hardware/power.h> +#include <hardware_legacy/power.h> +#include <powermanager/PowerHalLoader.h> + +using namespace android::hardware::power; + +namespace android { + +namespace power { + +// ------------------------------------------------------------------------------------------------- + +template <typename T, typename F> +sp<T> loadHal(bool& exists, sp<T>& hal, F& loadFn, const char* halName) { + if (!exists) { + return nullptr; + } + if (hal) { + return hal; + } + hal = loadFn(); + if (hal) { + ALOGV("Successfully connected to Power HAL %s service.", halName); + } else { + ALOGV("Power HAL %s service not available.", halName); + exists = false; + } + return hal; +} + +// ------------------------------------------------------------------------------------------------- + +std::mutex PowerHalLoader::gHalMutex; +sp<IPower> PowerHalLoader::gHalAidl = nullptr; +sp<V1_0::IPower> PowerHalLoader::gHalHidlV1_0 = nullptr; +sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr; + +void PowerHalLoader::unloadAll() { + std::lock_guard<std::mutex> lock(gHalMutex); + gHalAidl = nullptr; + gHalHidlV1_0 = nullptr; + gHalHidlV1_1 = nullptr; +} + +sp<IPower> PowerHalLoader::loadAidl() { + std::lock_guard<std::mutex> lock(gHalMutex); + static bool gHalExists = true; + static auto loadFn = []() { return waitForVintfService<IPower>(); }; + return loadHal<IPower>(gHalExists, gHalAidl, loadFn, "AIDL"); +} + +sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0() { + std::lock_guard<std::mutex> lock(gHalMutex); + return loadHidlV1_0Locked(); +} + +sp<V1_1::IPower> PowerHalLoader::loadHidlV1_1() { + std::lock_guard<std::mutex> lock(gHalMutex); + static bool gHalExists = true; + static auto loadFn = []() { return V1_1::IPower::castFrom(loadHidlV1_0Locked()); }; + return loadHal<V1_1::IPower>(gHalExists, gHalHidlV1_1, loadFn, "HIDL v1.1"); +} + +sp<V1_0::IPower> PowerHalLoader::loadHidlV1_0Locked() { + static bool gHalExists = true; + static auto loadFn = []() { return V1_0::IPower::getService(); }; + return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0"); +} + +// ------------------------------------------------------------------------------------------------- + +} // namespace power + +} // namespace android diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp new file mode 100644 index 0000000000..4a711ca80b --- /dev/null +++ b/services/powermanager/PowerHalWrapper.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "HalWrapper" +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/Mode.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +using namespace android::hardware::power; + +namespace android { + +namespace power { + +// ------------------------------------------------------------------------------------------------- + +inline HalResult toHalResult(const binder::Status& result) { + if (result.isOk()) { + return HalResult::SUCCESSFUL; + } + ALOGE("Power HAL request failed: %s", result.toString8().c_str()); + return HalResult::FAILED; +} + +template <typename T> +inline HalResult toHalResult(const hardware::Return<T>& result) { + if (result.isOk()) { + return HalResult::SUCCESSFUL; + } + ALOGE("Power HAL request failed: %s", result.description().c_str()); + return HalResult::FAILED; +} + +// ------------------------------------------------------------------------------------------------- + +HalResult EmptyHalWrapper::setBoost(Boost boost, int32_t durationMs) { + ALOGV("Skipped setBoost %s with duration %dms because Power HAL not available", + toString(boost).c_str(), durationMs); + return HalResult::UNSUPPORTED; +} + +HalResult EmptyHalWrapper::setMode(Mode mode, bool enabled) { + ALOGV("Skipped setMode %s to %s because Power HAL not available", toString(mode).c_str(), + enabled ? "true" : "false"); + return HalResult::UNSUPPORTED; +} + +// ------------------------------------------------------------------------------------------------- + +HalResult HidlHalWrapperV1_0::setBoost(Boost boost, int32_t durationMs) { + if (boost == Boost::INTERACTION) { + return sendPowerHint(V1_0::PowerHint::INTERACTION, durationMs); + } else { + ALOGV("Skipped setBoost %s because Power HAL AIDL not available", toString(boost).c_str()); + return HalResult::UNSUPPORTED; + } +} + +HalResult HidlHalWrapperV1_0::setMode(Mode mode, bool enabled) { + uint32_t data = enabled ? 1 : 0; + switch (mode) { + case Mode::LAUNCH: + return sendPowerHint(V1_0::PowerHint::LAUNCH, data); + case Mode::LOW_POWER: + return sendPowerHint(V1_0::PowerHint::LOW_POWER, data); + case Mode::SUSTAINED_PERFORMANCE: + return sendPowerHint(V1_0::PowerHint::SUSTAINED_PERFORMANCE, data); + case Mode::VR: + return sendPowerHint(V1_0::PowerHint::VR_MODE, data); + case Mode::INTERACTIVE: + return setInteractive(enabled); + case Mode::DOUBLE_TAP_TO_WAKE: + return setFeature(V1_0::Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, enabled); + default: + ALOGV("Skipped setMode %s because Power HAL AIDL not available", + toString(mode).c_str()); + return HalResult::UNSUPPORTED; + } +} + +HalResult HidlHalWrapperV1_0::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) { + return toHalResult(mHandleV1_0->powerHint(hintId, data)); +} + +HalResult HidlHalWrapperV1_0::setInteractive(bool enabled) { + return toHalResult(mHandleV1_0->setInteractive(enabled)); +} + +HalResult HidlHalWrapperV1_0::setFeature(V1_0::Feature feature, bool enabled) { + return toHalResult(mHandleV1_0->setFeature(feature, enabled)); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t data) { + return toHalResult(mHandleV1_1->powerHintAsync(hintId, data)); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) { + std::unique_lock<std::mutex> lock(mBoostMutex); + // Quick return if boost is not supported by HAL + if (boost > Boost::DISPLAY_UPDATE_IMMINENT || + mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) { + ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str()); + return HalResult::UNSUPPORTED; + } + + if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) { + bool isSupported = false; + auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported); + if (!isSupportedRet.isOk()) { + ALOGE("Skipped setBoost %s because check support failed with: %s", + toString(boost).c_str(), isSupportedRet.toString8().c_str()); + return HalResult::FAILED; + } + + mBoostSupportedArray[static_cast<int32_t>(boost)] = + isSupported ? HalSupport::ON : HalSupport::OFF; + if (!isSupported) { + ALOGV("Skipped setBoost %s because Power HAL doesn't support it", + toString(boost).c_str()); + return HalResult::UNSUPPORTED; + } + } + lock.unlock(); + + return toHalResult(mHandle->setBoost(boost, durationMs)); +} + +HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) { + std::unique_lock<std::mutex> lock(mModeMutex); + // Quick return if mode is not supported by HAL + if (mode > Mode::DISPLAY_INACTIVE || + mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) { + ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str()); + return HalResult::UNSUPPORTED; + } + + if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) { + bool isSupported = false; + auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported); + if (!isSupportedRet.isOk()) { + ALOGE("Skipped setMode %s because check support failed with: %s", + toString(mode).c_str(), isSupportedRet.toString8().c_str()); + return HalResult::FAILED; + } + + mModeSupportedArray[static_cast<int32_t>(mode)] = + isSupported ? HalSupport::ON : HalSupport::OFF; + if (!isSupported) { + ALOGV("Skipped setMode %s because Power HAL doesn't support it", + toString(mode).c_str()); + return HalResult::UNSUPPORTED; + } + } + lock.unlock(); + + return toHalResult(mHandle->setMode(mode, enabled)); +} + +// ------------------------------------------------------------------------------------------------- + +} // namespace power + +} // namespace android diff --git a/services/powermanager/PowerSaveState.cpp b/services/powermanager/PowerSaveState.cpp new file mode 100644 index 0000000000..6d1830a887 --- /dev/null +++ b/services/powermanager/PowerSaveState.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerSaveState" + +#include <android/PowerSaveState.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +status_t PowerSaveState::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->readBool(&mBatterySaverEnabled) + ?: parcel->readBool(&mGlobalBatterySaverEnabled) + ?: parcel->readInt32(reinterpret_cast<int32_t *>(&mLocationMode)) + ?: parcel->readFloat(&mBrightnessFactor); +} + +status_t PowerSaveState::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->writeBool(mBatterySaverEnabled) + ?: parcel->writeBool(mGlobalBatterySaverEnabled) + ?: parcel->writeInt32(static_cast<int32_t>(mLocationMode)) + ?: parcel->writeFloat(mBrightnessFactor); +} + +} // namespace android::os diff --git a/services/powermanager/TEST_MAPPING b/services/powermanager/TEST_MAPPING new file mode 100644 index 0000000000..caaec556e9 --- /dev/null +++ b/services/powermanager/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libpowermanager_test" + } + ] +} diff --git a/services/powermanager/WorkSource.cpp b/services/powermanager/WorkSource.cpp new file mode 100644 index 0000000000..1006a0666f --- /dev/null +++ b/services/powermanager/WorkSource.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "WorkSource" + +#include <android/WorkSource.h> +#include <binder/Parcel.h> +#include <utils/Log.h> + +namespace android::os { + +status_t WorkSource::readFromParcel(const android::Parcel *parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + int32_t num; + status_t ret = parcel->readInt32(&num) + ?: parcel->readInt32Vector(&mUids) + ?: parcel->readString16Vector(&mNames); + + return ret; +} + +status_t WorkSource::writeToParcel(android::Parcel *parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + return parcel->writeInt32(mUids.size()) + ?: parcel->writeInt32Vector(mUids) + ?: parcel->writeString16Vector(mNames); +} + +} // namespace android::os diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp new file mode 100644 index 0000000000..4c5d508c92 --- /dev/null +++ b/services/powermanager/benchmarks/Android.bp @@ -0,0 +1,42 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_benchmark { + name: "libpowermanager_benchmarks", + srcs: [ + "main.cpp", + "PowerHalAidlBenchmarks.cpp", + "PowerHalControllerBenchmarks.cpp", + "PowerHalHidlBenchmarks.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libpowermanager", + "libutils", + "android.hardware.power@1.0", + "android.hardware.power@1.1", + "android.hardware.power-cpp", + ], + static_libs: [ + "libtestUtil", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], +} diff --git a/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp new file mode 100644 index 0000000000..10048286b9 --- /dev/null +++ b/services/powermanager/benchmarks/PowerHalAidlBenchmarks.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalAidlBenchmarks" + +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <benchmark/benchmark.h> +#include <binder/IServiceManager.h> +#include <testUtil.h> +#include <chrono> + +using android::hardware::power::Boost; +using android::hardware::power::IPower; +using android::hardware::power::Mode; +using std::chrono::microseconds; + +using namespace android; +using namespace std::chrono_literals; + +// Values from Boost.aidl and Mode.aidl. +static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION); +static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT); +static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE); +static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH); + +// Delay between oneway method calls to avoid overflowing the binder buffers. +static constexpr microseconds ONEWAY_API_DELAY = 100us; + +template <class R, class... Args0, class... Args1> +static void runBenchmark(benchmark::State& state, microseconds delay, R (IPower::*fn)(Args0...), + Args1&&... args1) { + sp<IPower> hal = waitForVintfService<IPower>(); + + if (hal == nullptr) { + ALOGI("Power HAL not available, skipping test..."); + return; + } + + binder::Status ret = (*hal.*fn)(std::forward<Args1>(args1)...); + if (ret.exceptionCode() == binder::Status::Exception::EX_UNSUPPORTED_OPERATION) { + ALOGI("Power HAL does not support this operation, skipping test..."); + return; + } + + while (state.KeepRunning()) { + ret = (*hal.*fn)(std::forward<Args1>(args1)...); + state.PauseTiming(); + if (!ret.isOk()) state.SkipWithError(ret.toString8().c_str()); + if (delay > 0us) { + testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count()); + } + state.ResumeTiming(); + } +} + +static void BM_PowerHalAidlBenchmarks_isBoostSupported(benchmark::State& state) { + bool isSupported; + Boost boost = static_cast<Boost>(state.range(0)); + runBenchmark(state, 0us, &IPower::isBoostSupported, boost, &isSupported); +} + +static void BM_PowerHalAidlBenchmarks_isModeSupported(benchmark::State& state) { + bool isSupported; + Mode mode = static_cast<Mode>(state.range(0)); + runBenchmark(state, 0us, &IPower::isModeSupported, mode, &isSupported); +} + +static void BM_PowerHalAidlBenchmarks_setBoost(benchmark::State& state) { + Boost boost = static_cast<Boost>(state.range(0)); + runBenchmark(state, ONEWAY_API_DELAY, &IPower::setBoost, boost, 1); +} + +static void BM_PowerHalAidlBenchmarks_setMode(benchmark::State& state) { + Mode mode = static_cast<Mode>(state.range(0)); + runBenchmark(state, ONEWAY_API_DELAY, &IPower::setMode, mode, false); +} + +BENCHMARK(BM_PowerHalAidlBenchmarks_isBoostSupported)->DenseRange(FIRST_BOOST, LAST_BOOST, 1); +BENCHMARK(BM_PowerHalAidlBenchmarks_isModeSupported)->DenseRange(FIRST_MODE, LAST_MODE, 1); +BENCHMARK(BM_PowerHalAidlBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1); +BENCHMARK(BM_PowerHalAidlBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1); diff --git a/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp new file mode 100644 index 0000000000..598080b5bc --- /dev/null +++ b/services/powermanager/benchmarks/PowerHalControllerBenchmarks.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalControllerBenchmarks" + +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/Mode.h> +#include <benchmark/benchmark.h> +#include <powermanager/PowerHalController.h> +#include <testUtil.h> +#include <chrono> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::power::HalResult; +using android::power::PowerHalController; + +using namespace android; +using namespace std::chrono_literals; + +// Values from Boost.aidl and Mode.aidl. +static constexpr int64_t FIRST_BOOST = static_cast<int64_t>(Boost::INTERACTION); +static constexpr int64_t LAST_BOOST = static_cast<int64_t>(Boost::CAMERA_SHOT); +static constexpr int64_t FIRST_MODE = static_cast<int64_t>(Mode::DOUBLE_TAP_TO_WAKE); +static constexpr int64_t LAST_MODE = static_cast<int64_t>(Mode::CAMERA_STREAMING_HIGH); + +// Delay between oneway method calls to avoid overflowing the binder buffers. +static constexpr std::chrono::microseconds ONEWAY_API_DELAY = 100us; + +template <class... Args0, class... Args1> +static void runBenchmark(benchmark::State& state, HalResult (PowerHalController::*fn)(Args0...), + Args1&&... args1) { + while (state.KeepRunning()) { + PowerHalController controller; + HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...); + state.PauseTiming(); + if (ret == HalResult::FAILED) state.SkipWithError("Power HAL request failed"); + state.ResumeTiming(); + } +} + +template <class... Args0, class... Args1> +static void runCachedBenchmark(benchmark::State& state, + HalResult (PowerHalController::*fn)(Args0...), Args1&&... args1) { + PowerHalController controller; + // First call out of test, to cache HAL service and isSupported result. + (controller.*fn)(std::forward<Args1>(args1)...); + + while (state.KeepRunning()) { + HalResult ret = (controller.*fn)(std::forward<Args1>(args1)...); + state.PauseTiming(); + if (ret == HalResult::FAILED) { + state.SkipWithError("Power HAL request failed"); + } + testDelaySpin( + std::chrono::duration_cast<std::chrono::duration<float>>(ONEWAY_API_DELAY).count()); + state.ResumeTiming(); + } +} + +static void BM_PowerHalControllerBenchmarks_init(benchmark::State& state) { + while (state.KeepRunning()) { + PowerHalController controller; + controller.init(); + } +} + +static void BM_PowerHalControllerBenchmarks_initCached(benchmark::State& state) { + PowerHalController controller; + // First connection out of test. + controller.init(); + + while (state.KeepRunning()) { + controller.init(); + } +} + +static void BM_PowerHalControllerBenchmarks_setBoost(benchmark::State& state) { + Boost boost = static_cast<Boost>(state.range(0)); + runBenchmark(state, &PowerHalController::setBoost, boost, 0); +} + +static void BM_PowerHalControllerBenchmarks_setBoostCached(benchmark::State& state) { + Boost boost = static_cast<Boost>(state.range(0)); + runCachedBenchmark(state, &PowerHalController::setBoost, boost, 0); +} + +static void BM_PowerHalControllerBenchmarks_setMode(benchmark::State& state) { + Mode mode = static_cast<Mode>(state.range(0)); + runBenchmark(state, &PowerHalController::setMode, mode, false); +} + +static void BM_PowerHalControllerBenchmarks_setModeCached(benchmark::State& state) { + Mode mode = static_cast<Mode>(state.range(0)); + runCachedBenchmark(state, &PowerHalController::setMode, mode, false); +} + +BENCHMARK(BM_PowerHalControllerBenchmarks_init); +BENCHMARK(BM_PowerHalControllerBenchmarks_initCached); +BENCHMARK(BM_PowerHalControllerBenchmarks_setBoost)->DenseRange(FIRST_BOOST, LAST_BOOST, 1); +BENCHMARK(BM_PowerHalControllerBenchmarks_setBoostCached)->DenseRange(FIRST_BOOST, LAST_BOOST, 1); +BENCHMARK(BM_PowerHalControllerBenchmarks_setMode)->DenseRange(FIRST_MODE, LAST_MODE, 1); +BENCHMARK(BM_PowerHalControllerBenchmarks_setModeCached)->DenseRange(FIRST_MODE, LAST_MODE, 1); diff --git a/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp new file mode 100644 index 0000000000..97e026bd25 --- /dev/null +++ b/services/powermanager/benchmarks/PowerHalHidlBenchmarks.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalHidlBenchmarks" + +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <benchmark/benchmark.h> +#include <hardware/power.h> +#include <hardware_legacy/power.h> +#include <testUtil.h> +#include <chrono> + +using android::hardware::Return; +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using android::hardware::power::V1_0::PowerHint; +using std::chrono::microseconds; +using IPower1_0 = android::hardware::power::V1_0::IPower; +using IPower1_1 = android::hardware::power::V1_1::IPower; + +using namespace android; +using namespace std::chrono_literals; + +// Values from types.hal from versions 1.0 to 1.3. +static constexpr int64_t FIRST_POWER_HINT = static_cast<int64_t>(PowerHint::VSYNC); +static constexpr int64_t LAST_POWER_HINT = static_cast<int64_t>(PowerHint::LAUNCH); + +// Delay between oneway method calls to avoid overflowing the binder buffers. +static constexpr microseconds ONEWAY_API_DELAY = 100us; + +template <class R, class I, class... Args0, class... Args1> +static void runBenchmark(benchmark::State& state, microseconds delay, Return<R> (I::*fn)(Args0...), + Args1&&... args1) { + sp<I> hal = I::getService(); + + if (hal == nullptr) { + ALOGI("Power HAL HIDL not available, skipping test..."); + return; + } + + while (state.KeepRunning()) { + Return<R> ret = (*hal.*fn)(std::forward<Args1>(args1)...); + state.PauseTiming(); + if (!ret.isOk()) state.SkipWithError(ret.description().c_str()); + if (delay > 0us) { + testDelaySpin(std::chrono::duration_cast<std::chrono::duration<float>>(delay).count()); + } + state.ResumeTiming(); + } +} + +static void BM_PowerHalHidlBenchmarks_setFeature(benchmark::State& state) { + runBenchmark(state, 0us, &IPower1_0::setFeature, Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, + false); +} + +static void BM_PowerHalHidlBenchmarks_setInteractive(benchmark::State& state) { + runBenchmark(state, 0us, &IPower1_0::setInteractive, false); +} + +static void BM_PowerHalHidlBenchmarks_powerHint(benchmark::State& state) { + PowerHint powerHint = static_cast<PowerHint>(state.range(0)); + runBenchmark(state, 0us, &IPower1_0::powerHint, powerHint, 0); +} + +static void BM_PowerHalHidlBenchmarks_powerHintAsync(benchmark::State& state) { + PowerHint powerHint = static_cast<PowerHint>(state.range(0)); + runBenchmark(state, ONEWAY_API_DELAY, &IPower1_1::powerHintAsync, powerHint, 0); +} + +BENCHMARK(BM_PowerHalHidlBenchmarks_setFeature); +BENCHMARK(BM_PowerHalHidlBenchmarks_setInteractive); +BENCHMARK(BM_PowerHalHidlBenchmarks_powerHint)->DenseRange(FIRST_POWER_HINT, LAST_POWER_HINT, 1); +BENCHMARK(BM_PowerHalHidlBenchmarks_powerHintAsync) + ->DenseRange(FIRST_POWER_HINT, LAST_POWER_HINT, 1); diff --git a/services/powermanager/benchmarks/main.cpp b/services/powermanager/benchmarks/main.cpp new file mode 100644 index 0000000000..15c57bf1be --- /dev/null +++ b/services/powermanager/benchmarks/main.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <benchmark/benchmark.h> + +BENCHMARK_MAIN(); diff --git a/services/powermanager/include/android/BatterySaverPolicyConfig.h b/services/powermanager/include/android/BatterySaverPolicyConfig.h new file mode 100644 index 0000000000..728c8a02a0 --- /dev/null +++ b/services/powermanager/include/android/BatterySaverPolicyConfig.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_OS_BATTERY_SAVER_POLICY_CONFIG_H +#define ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H + +#include <math.h> +#include <binder/Parcelable.h> +#include <utils/RefBase.h> + +namespace android::os { + +enum class LocationMode : int32_t; +/** + * BatterySaverPolicyConfig is a structure of configs to set Battery Saver policy flags. + * This file needs to be kept in sync with + * frameworks/base/core/java/android/os/BatterySaverPolicyConfig.java + */ +struct BatterySaverPolicyConfig : public android::Parcelable { + + BatterySaverPolicyConfig(float adjustBrightnessFactor = 1.0f, + bool advertiseIsEnabled = false, + bool deferFullBackup = false, + bool deferKeyValueBackup = false, + std::vector<std::pair<String16, String16>> deviceSpecificSettings = {}, + bool disableAnimation = false, + bool disableAod = false, + bool disableLaunchBoost = false, + bool disableOptionalSensors = false, + bool disableSoundTrigger = false, + bool disableVibration = false, + bool enableAdjustBrightness = false, + bool enableDataSaver = false, + bool enableFirewall = false, + bool enableNightMode = false, + bool enableQuickDoze = false, + bool forceAllAppsStandby = false, + bool forceBackgroundCheck = false, + LocationMode locationMode = static_cast<LocationMode>(0)) + : mAdjustBrightnessFactor(adjustBrightnessFactor), + mAdvertiseIsEnabled(advertiseIsEnabled), + mDeferFullBackup(deferFullBackup), + mDeferKeyValueBackup(deferKeyValueBackup), + mDeviceSpecificSettings(deviceSpecificSettings), + mDisableAnimation(disableAnimation), + mDisableAod(disableAod), + mDisableLaunchBoost(disableLaunchBoost), + mDisableOptionalSensors(disableOptionalSensors), + mDisableSoundTrigger(disableSoundTrigger), + mDisableVibration(disableVibration), + mEnableAdjustBrightness(enableAdjustBrightness), + mEnableDataSaver(enableDataSaver), + mEnableFirewall(enableFirewall), + mEnableNightMode(enableNightMode), + mEnableQuickDoze(enableQuickDoze), + mForceAllAppsStandby(forceAllAppsStandby), + mForceBackgroundCheck(forceBackgroundCheck), + mLocationMode(locationMode) { + } + + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + bool operator == (const BatterySaverPolicyConfig &bsp) const { + return fabs(mAdjustBrightnessFactor - bsp.mAdjustBrightnessFactor) == 0.0f && + mAdvertiseIsEnabled == bsp.mAdvertiseIsEnabled && + mDeferFullBackup == bsp.mDeferFullBackup && + mDeferKeyValueBackup == bsp.mDeferKeyValueBackup && + mDeviceSpecificSettings == bsp.mDeviceSpecificSettings && + mDisableAnimation == bsp.mDisableAnimation && + mDisableAod == bsp.mDisableAod && + mDisableLaunchBoost == bsp.mDisableLaunchBoost && + mDisableOptionalSensors == bsp.mDisableOptionalSensors && + mDisableSoundTrigger == bsp.mDisableSoundTrigger && + mDisableVibration == bsp.mDisableVibration && + mEnableAdjustBrightness == bsp.mEnableAdjustBrightness && + mEnableDataSaver == bsp.mEnableDataSaver && + mEnableFirewall == bsp.mEnableFirewall && + mEnableNightMode == bsp.mEnableNightMode && + mEnableQuickDoze == bsp.mEnableQuickDoze && + mForceAllAppsStandby == bsp.mForceAllAppsStandby && + mForceBackgroundCheck == bsp.mForceBackgroundCheck && + mLocationMode == bsp.mLocationMode; + } + +private: + status_t readDeviceSpecificSettings(const android::Parcel *parcel); + status_t writeDeviceSpecificSettings(android::Parcel *parcel) const; + /** Adjust screen brightness factor */ + float mAdjustBrightnessFactor; + /** Is advertise enabled */ + bool mAdvertiseIsEnabled; + /** Defer full backup */ + bool mDeferFullBackup; + /** Defer key value backup */ + bool mDeferKeyValueBackup; + /** Device specific settings */ + std::vector<std::pair<String16, String16>> mDeviceSpecificSettings; + /** Disable animation */ + bool mDisableAnimation; + /** Disable Aod */ + bool mDisableAod; + /** Disable launch boost */ + bool mDisableLaunchBoost; + /** Disable optional sensors */ + bool mDisableOptionalSensors; + /** Disable sound trigger */ + bool mDisableSoundTrigger; + /** Disable vibration */ + bool mDisableVibration; + /** Enable adjust brightness */ + bool mEnableAdjustBrightness; + /** Enable data saver */ + bool mEnableDataSaver; + /** Enable firewall */ + bool mEnableFirewall; + /** Enable night mode */ + bool mEnableNightMode; + /** Enable quick doze */ + bool mEnableQuickDoze; + /** Force all Apps standby */ + bool mForceAllAppsStandby; + /** Force Background check */ + bool mForceBackgroundCheck; + /** Location mode */ + LocationMode mLocationMode; +}; + +} // namespace android::os + +#endif /* ANDROID_OS_BATTERY_SAVER_POLICY_CONFIG_H */ diff --git a/services/powermanager/include/android/LocationMode.h b/services/powermanager/include/android/LocationMode.h new file mode 100644 index 0000000000..42933d438b --- /dev/null +++ b/services/powermanager/include/android/LocationMode.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_OS_LOCATION_MODE_H +#define ANDROID_OS_LOCATION_MODE_H + +namespace android::os { + +enum class LocationMode : int32_t { + NO_CHANGE = IPowerManager::LOCATION_MODE_NO_CHANGE, + GPS_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF, + ALL_DISABLED_WHEN_SCREEN_OFF = IPowerManager::LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF, + FOREGROUND_ONLY = IPowerManager::LOCATION_MODE_FOREGROUND_ONLY, + THROTTLE_REQUESTS_WHEN_SCREEN_OFF = + IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF, + MIN = IPowerManager::LOCATION_MODE_NO_CHANGE, + MAX = IPowerManager::LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF, +}; + +} // namespace android::os + +#endif /* ANDROID_OS_LOCATION_MODE_H */ diff --git a/services/powermanager/include/android/ParcelDuration.h b/services/powermanager/include/android/ParcelDuration.h new file mode 100644 index 0000000000..117d1734e0 --- /dev/null +++ b/services/powermanager/include/android/ParcelDuration.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_OS_PARCELDURATION_H +#define ANDROID_OS_PARCELDURATION_H + +#include <binder/Parcelable.h> +#include <math.h> +#include <utils/RefBase.h> + +namespace android::os { + +/** + * Parcelable version of {@link java.time.Duration} that can be used in binder calls. + * This file needs to be kept in sync with + * frameworks/base/core/java/android/os/ParcelDuration.java + */ +struct ParcelDuration : public android::Parcelable { + ParcelDuration(int64_t seconds = 0, int32_t nanos = 0) : mSeconds(seconds), mNanos(nanos) {} + + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + bool operator==(const ParcelDuration& pd) const { + return mSeconds == pd.mSeconds && mNanos == pd.mNanos; + } + +private: + int64_t mSeconds; + int32_t mNanos; +}; + +} // namespace android::os + +#endif /* ANDROID_OS_PARCELDURATION_H */ diff --git a/services/powermanager/include/android/PowerSaveState.h b/services/powermanager/include/android/PowerSaveState.h new file mode 100644 index 0000000000..b421f6aaa8 --- /dev/null +++ b/services/powermanager/include/android/PowerSaveState.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_OS_POWER_SAVE_STATE_H +#define ANDROID_OS_POWER_SAVE_STATE_H + +#include <math.h> +#include <binder/Parcelable.h> +#include <utils/RefBase.h> + +namespace android::os { + +enum class LocationMode : int32_t; +/** + * PowerSaveState is a structure to encapsulate PowerSaveState status. + * This file needs to be kept in sync with frameworks/base/core/java/android/os/PowerSaveState.java + */ +struct PowerSaveState : public android::Parcelable { + + PowerSaveState(bool batterySaverEnabled = false, + bool globalBatterySaverEnabled = false, + LocationMode locationMode = static_cast<LocationMode>(0), + float brightnessFactor = 0.5f) + : mBatterySaverEnabled(batterySaverEnabled), + mGlobalBatterySaverEnabled(globalBatterySaverEnabled), + mLocationMode(locationMode), + mBrightnessFactor(brightnessFactor) { + } + + bool getBatterySaverEnabled() const { return mBatterySaverEnabled; } + bool getGlobalBatterySaverEnabled() const { return mGlobalBatterySaverEnabled; } + LocationMode getLocationMode() const { return mLocationMode; } + float getBrightnessFactor() const { return mBrightnessFactor; } + bool operator == (const PowerSaveState &ps) const { + return mBatterySaverEnabled == ps.mBatterySaverEnabled && + mGlobalBatterySaverEnabled == ps.mGlobalBatterySaverEnabled && + mLocationMode == ps.mLocationMode && + fabs(mBrightnessFactor - ps.mBrightnessFactor) == 0.0f; + } + + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + +private: + /** Whether we should enable battery saver for this service. */ + bool mBatterySaverEnabled; + /** Whether battery saver mode is enabled. */ + bool mGlobalBatterySaverEnabled; + /** Location mode */ + LocationMode mLocationMode; + /** Screen brightness factor. */ + float mBrightnessFactor; +}; + +} // namespace android::os + +#endif /* ANDROID_OS_POWER_SAVE_STATE_H */ diff --git a/services/powermanager/include/android/WorkSource.h b/services/powermanager/include/android/WorkSource.h new file mode 100644 index 0000000000..f12847d040 --- /dev/null +++ b/services/powermanager/include/android/WorkSource.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_OS_WORKSOURCE_H +#define ANDROID_OS_WORKSOURCE_H + +#include <optional> +#include <binder/Parcelable.h> +#include <utils/RefBase.h> + +namespace android::os { + +/** + * WorkSource is a structure to describes the source of some work that may be done by someone else. + * This file needs to be kept in sync with frameworks/base/core/java/android/os/WorkSource.java + */ +struct WorkSource : public android::Parcelable { + WorkSource( + std::vector<int32_t> uids = {}, + std::optional<std::vector<std::optional<String16>>> names = std::nullopt) + : mUids(uids), + mNames(names) { + } + std::vector<int32_t> getUids() const { return mUids; } + std::optional<std::vector<std::optional<String16>>> getNames() const { return mNames; } + bool operator == (const WorkSource &ws) const { + return mUids == ws.mUids && mNames == ws.mNames; + } + status_t readFromParcel(const android::Parcel* parcel) override; + status_t writeToParcel(android::Parcel* parcel) const override; + +private: + /** WorkSource UID array */ + std::vector<int32_t> mUids = {}; + /** WorkSource Tag array */ + std::optional<std::vector<std::optional<String16>>> mNames = {}; +}; + +} // namespace android::os + +#endif /* ANDROID_OS_WORKSOURCE_H */ diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp new file mode 100644 index 0000000000..49abc1152b --- /dev/null +++ b/services/powermanager/tests/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libpowermanager_test", + test_suites: ["device-tests"], + srcs: [ + "IThermalManagerTest.cpp", + "PowerHalControllerTest.cpp", + "PowerHalLoaderTest.cpp", + "PowerHalWrapperAidlTest.cpp", + "PowerHalWrapperHidlV1_0Test.cpp", + "PowerHalWrapperHidlV1_1Test.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + shared_libs: [ + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libpowermanager", + "libutils", + "android.hardware.power@1.0", + "android.hardware.power@1.1", + "android.hardware.power-cpp", + ], + static_libs: [ + "libgmock", + ], +} diff --git a/services/powermanager/IThermalManagerTest.cpp b/services/powermanager/tests/IThermalManagerTest.cpp index 575b9ee1c4..b62be5f5d4 100644 --- a/services/powermanager/IThermalManagerTest.cpp +++ b/services/powermanager/tests/IThermalManagerTest.cpp @@ -86,12 +86,14 @@ void IThermalServiceTest::SetUp() { EXPECT_NE(binder, nullptr); mThermalSvc = interface_cast<IThermalService>(binder); EXPECT_NE(mThermalSvc, nullptr); + // Lock mutex for operation, so listener will only be processed after wait_for is called + std::unique_lock<std::mutex> lock(mMutex); bool success = false; binder::Status ret = mThermalSvc->registerThermalStatusListener(this, &success); + // Check the result ASSERT_TRUE(success); ASSERT_TRUE(ret.isOk()); // Wait for listener called after registration, shouldn't timeout - std::unique_lock<std::mutex> lock(mMutex); EXPECT_NE(mCondition.wait_for(lock, 1s), std::cv_status::timeout); } @@ -111,6 +113,7 @@ class IThermalListenerTest : public IThermalServiceTest, public testing::WithPar TEST_P(IThermalListenerTest, TestListener) { int level = GetParam(); + // Lock mutex for operation, so listener will only be processed after wait_for is called std::unique_lock<std::mutex> lock(mMutex); // Set the override thermal status setThermalOverride(level); diff --git a/services/powermanager/tests/PowerHalControllerTest.cpp b/services/powermanager/tests/PowerHalControllerTest.cpp new file mode 100644 index 0000000000..141b2440b8 --- /dev/null +++ b/services/powermanager/tests/PowerHalControllerTest.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalControllerTest" + +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalController.h> +#include <utils/Log.h> + +#include <thread> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using android::hardware::power::V1_0::IPower; +using android::hardware::power::V1_0::PowerHint; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPowerV1_0 : public IPower { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); +}; + +class TestPowerHalConnector : public HalConnector { +public: + TestPowerHalConnector(sp<IPower> powerHal) : mHal(std::move(powerHal)) {} + virtual ~TestPowerHalConnector() = default; + + virtual std::unique_ptr<HalWrapper> connect() override { + mCountMutex.lock(); + ++mConnectedCount; + mCountMutex.unlock(); + return std::make_unique<HidlHalWrapperV1_0>(mHal); + } + + void reset() override { + mCountMutex.lock(); + ++mResetCount; + mCountMutex.unlock(); + } + + int getConnectCount() { return mConnectedCount; } + + int getResetCount() { return mResetCount; } + +private: + sp<IPower> mHal = nullptr; + std::mutex mCountMutex; + int mConnectedCount = 0; + int mResetCount = 0; +}; + +class AlwaysFailingTestPowerHalConnector : public TestPowerHalConnector { +public: + AlwaysFailingTestPowerHalConnector() : TestPowerHalConnector(nullptr) {} + + std::unique_ptr<HalWrapper> connect() override { + // Call parent to update counter, but ignore connected HalWrapper. + TestPowerHalConnector::connect(); + return nullptr; + } +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalControllerTest : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIPowerV1_0>(); + std::unique_ptr<TestPowerHalConnector> halConnector = + std::make_unique<TestPowerHalConnector>(mMockHal); + mHalConnector = halConnector.get(); + mHalController = std::make_unique<PowerHalController>(std::move(halConnector)); + } + +protected: + sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr; + TestPowerHalConnector* mHalConnector = nullptr; + std::unique_ptr<PowerHalController> mHalController = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalControllerTest, TestInitConnectsToPowerHalOnlyOnce) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + mHalController->init(); + mHalController->init(); + + // PowerHalConnector was called only once and never reset. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 1); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestUnableToConnectToPowerHalIgnoresAllApiCalls) { + std::unique_ptr<AlwaysFailingTestPowerHalConnector> halConnector = + std::make_unique<AlwaysFailingTestPowerHalConnector>(); + AlwaysFailingTestPowerHalConnector* failingHalConnector = halConnector.get(); + PowerHalController halController(std::move(halConnector)); + + int powerHalConnectCount = failingHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + // Still works with EmptyPowerHalWrapper as fallback ignoring every api call + // and logging. + auto result = halController.setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + result = halController.setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + + // PowerHalConnector was called every time to attempt to reconnect with + // underlying service. + powerHalConnectCount = failingHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 2); + // PowerHalConnector was never reset. + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestAllApiCallsDelegatedToConnectedPowerHal) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + { + InSequence seg; + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(100))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1)); + } + + auto result = mHalController->setBoost(Boost::INTERACTION, 100); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mHalController->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + + // PowerHalConnector was called only once and never reset. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 1); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestPowerHalRecoversFromFailureByRecreatingPowerHal) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _)) + .WillByDefault([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(4)); + + auto result = mHalController->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mHalController->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::FAILED, result); + result = mHalController->setMode(Mode::VR, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mHalController->setMode(Mode::LOW_POWER, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + + // PowerHalConnector was called only twice: on first api call and after failed + // call. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 2); + // PowerHalConnector was reset once after failed call. + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 1); +} + +TEST_F(PowerHalControllerTest, TestPowerHalDoesNotTryToRecoverFromFailureOnUnsupportedCalls) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + auto result = mHalController->setBoost(Boost::CAMERA_LAUNCH, 1000); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + result = mHalController->setMode(Mode::CAMERA_STREAMING_HIGH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + + // PowerHalConnector was called only once and never reset. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 1); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestMultiThreadConnectsOnlyOnce) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(10)); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mHalController->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + // PowerHalConnector was called only by the first thread to use the api and + // never reset. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 1); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_EQ(powerHalResetCount, 0); +} + +TEST_F(PowerHalControllerTest, TestMultiThreadWithFailureReconnectIsThreadSafe) { + int powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_EQ(powerHalConnectCount, 0); + + ON_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), _)) + .WillByDefault([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), powerHint(_, _)).Times(Exactly(40)); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mHalController->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + threads.push_back(std::thread([&]() { + auto result = mHalController->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::FAILED, result); + })); + threads.push_back(std::thread([&]() { + auto result = mHalController->setMode(Mode::LOW_POWER, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + threads.push_back(std::thread([&]() { + auto result = mHalController->setMode(Mode::VR, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + // PowerHalConnector was called at least once by the first thread. + // Reset and reconnect calls were made at most 10 times, once after each + // failure. + powerHalConnectCount = mHalConnector->getConnectCount(); + EXPECT_THAT(powerHalConnectCount, AllOf(Ge(1), Le(11))); + int powerHalResetCount = mHalConnector->getResetCount(); + EXPECT_THAT(powerHalResetCount, Le(10)); +} diff --git a/services/powermanager/tests/PowerHalLoaderTest.cpp b/services/powermanager/tests/PowerHalLoaderTest.cpp new file mode 100644 index 0000000000..058e1b5ca8 --- /dev/null +++ b/services/powermanager/tests/PowerHalLoaderTest.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalLoaderTest" + +#include <android-base/logging.h> +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/IPower.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalLoader.h> + +#include <future> + +using IPowerV1_0 = android::hardware::power::V1_0::IPower; +using IPowerV1_1 = android::hardware::power::V1_1::IPower; +using IPowerAidl = android::hardware::power::IPower; + +using namespace android; +using namespace android::power; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +template <typename T> +sp<T> loadHal(); + +template <> +sp<IPowerAidl> loadHal<IPowerAidl>() { + return PowerHalLoader::loadAidl(); +} + +template <> +sp<IPowerV1_0> loadHal<IPowerV1_0>() { + return PowerHalLoader::loadHidlV1_0(); +} + +template <> +sp<IPowerV1_1> loadHal<IPowerV1_1>() { + return PowerHalLoader::loadHidlV1_1(); +} + +// ------------------------------------------------------------------------------------------------- + +template <typename T> +class PowerHalLoaderTest : public Test { +public: + sp<T> load() { return ::loadHal<T>(); } + void unload() { PowerHalLoader::unloadAll(); } +}; + +// ------------------------------------------------------------------------------------------------- + +typedef ::testing::Types<IPowerAidl, IPowerV1_0, IPowerV1_1> PowerHalTypes; +TYPED_TEST_SUITE(PowerHalLoaderTest, PowerHalTypes); + +TYPED_TEST(PowerHalLoaderTest, TestLoadsOnlyOnce) { + sp<TypeParam> firstHal = this->load(); + if (firstHal == nullptr) { + ALOGE("Power HAL not available. Skipping test."); + return; + } + sp<TypeParam> secondHal = this->load(); + ASSERT_EQ(firstHal, secondHal); +} + +TYPED_TEST(PowerHalLoaderTest, TestUnload) { + sp<TypeParam> firstHal = this->load(); + if (firstHal == nullptr) { + ALOGE("Power HAL not available. Skipping test."); + return; + } + this->unload(); + sp<TypeParam> secondHal = this->load(); + ASSERT_NE(secondHal, nullptr); + ASSERT_NE(firstHal, secondHal); +} + +TYPED_TEST(PowerHalLoaderTest, TestLoadMultiThreadLoadsOnlyOnce) { + std::vector<std::future<sp<TypeParam>>> futures; + for (int i = 0; i < 10; i++) { + futures.push_back( + std::async(std::launch::async, &PowerHalLoaderTest<TypeParam>::load, this)); + } + + futures[0].wait(); + sp<TypeParam> firstHal = futures[0].get(); + if (firstHal == nullptr) { + ALOGE("Power HAL not available. Skipping test."); + return; + } + + for (int i = 1; i < 10; i++) { + futures[i].wait(); + sp<TypeParam> currentHal = futures[i].get(); + ASSERT_EQ(firstHal, currentHal); + } +} diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp new file mode 100644 index 0000000000..a7656598f0 --- /dev/null +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalWrapperAidlTest" + +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/Mode.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +#include <thread> + +using android::binder::Status; +using android::hardware::power::Boost; +using android::hardware::power::IPower; +using android::hardware::power::Mode; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPower : public IPower { +public: + MOCK_METHOD(Status, isBoostSupported, (Boost boost, bool* ret), (override)); + MOCK_METHOD(Status, setBoost, (Boost boost, int32_t durationMs), (override)); + MOCK_METHOD(Status, isModeSupported, (Mode mode, bool* ret), (override)); + MOCK_METHOD(Status, setMode, (Mode mode, bool enabled), (override)); + MOCK_METHOD(int32_t, getInterfaceVersion, (), (override)); + MOCK_METHOD(std::string, getInterfaceHash, (), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalWrapperAidlTest : public Test { +public: + void SetUp() override; + +protected: + std::unique_ptr<HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIPower>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +void PowerHalWrapperAidlTest::SetUp() { + mMockHal = new StrictMock<MockIPower>(); + mWrapper = std::make_unique<AidlHalWrapper>(mMockHal); + ASSERT_NE(mWrapper, nullptr); +} + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalWrapperAidlTest, TestSetBoostSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::DISPLAY_UPDATE_IMMINENT), Eq(100))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 100); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetBoostFailed) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::DISPLAY_UPDATE_IMMINENT), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + } + + auto result = mWrapper->setBoost(Boost::INTERACTION, 100); + ASSERT_EQ(HalResult::FAILED, result); + result = mWrapper->setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 1000); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) { + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status()))); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + result = mWrapper->setBoost(Boost::CAMERA_SHOT, 10); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetBoostMultiThreadCheckSupportedOnlyOnce) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setBoost(Eq(Boost::INTERACTION), Eq(100))).Times(Exactly(10)); + } + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->setBoost(Boost::INTERACTION, 100); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetModeSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::DISPLAY_INACTIVE), Eq(false))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetModeFailed) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(true))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::DISPLAY_INACTIVE), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(-1))); + } + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::FAILED, result); + result = mWrapper->setMode(Mode::DISPLAY_INACTIVE, false); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetModeUnsupported) { + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status()))); + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); + result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} + +TEST_F(PowerHalWrapperAidlTest, TestSetModeMultiThreadCheckSupportedOnlyOnce) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::LAUNCH), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(true), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), setMode(Eq(Mode::LAUNCH), Eq(false))).Times(Exactly(10)); + } + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->setMode(Mode::LAUNCH, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); +} diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp new file mode 100644 index 0000000000..6693d0b070 --- /dev/null +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_0Test.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalWrapperHidlV1_0Test" + +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using android::hardware::power::V1_0::IPower; +using android::hardware::power::V1_0::PowerHint; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPowerV1_0 : public IPower { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalWrapperHidlV1_0Test : public Test { +public: + void SetUp() override; + +protected: + std::unique_ptr<HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIPowerV1_0>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +void PowerHalWrapperHidlV1_0Test::SetUp() { + mMockHal = new StrictMock<MockIPowerV1_0>(); + mWrapper = std::make_unique<HidlHalWrapperV1_0>(mMockHal); + ASSERT_NE(mWrapper, nullptr); +} + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostSuccessful) { + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))).Times(Exactly(1)); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostFailed) { + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::INTERACTION), Eq(1000))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetBoostUnsupported) { + auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LOW_POWER), Eq(0))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::VR_MODE), Eq(0))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setInteractive(Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::LOW_POWER, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::VR, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::INTERACTIVE, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeFailed) { + EXPECT_CALL(*mMockHal.get(), powerHint(Eq(PowerHint::LAUNCH), Eq(1))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setMode(Mode::LAUNCH, 1); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperHidlV1_0Test, TestSetModeIgnored) { + auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} diff --git a/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp new file mode 100644 index 0000000000..55bbd6df79 --- /dev/null +++ b/services/powermanager/tests/PowerHalWrapperHidlV1_1Test.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalWrapperHidlV1_1Test" + +#include <android/hardware/power/1.1/IPower.h> +#include <android/hardware/power/Boost.h> +#include <android/hardware/power/IPower.h> +#include <android/hardware/power/Mode.h> +#include <binder/IServiceManager.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <powermanager/PowerHalWrapper.h> +#include <utils/Log.h> + +using android::hardware::power::Boost; +using android::hardware::power::Mode; +using android::hardware::power::V1_0::Feature; +using android::hardware::power::V1_0::PowerHint; +using IPowerV1_1 = android::hardware::power::V1_1::IPower; +using IPowerV1_0 = android::hardware::power::V1_0::IPower; + +using namespace android; +using namespace android::power; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIPowerV1_0 : public IPowerV1_0 { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); +}; + +class MockIPowerV1_1 : public IPowerV1_1 { +public: + MOCK_METHOD(hardware::Return<void>, setInteractive, (bool interactive), (override)); + MOCK_METHOD(hardware::Return<void>, powerHint, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, setFeature, (Feature feature, bool activate), (override)); + MOCK_METHOD(hardware::Return<void>, getPlatformLowPowerStats, + (getPlatformLowPowerStats_cb _hidl_cb), (override)); + MOCK_METHOD(hardware::Return<void>, powerHintAsync, (PowerHint hint, int32_t data), (override)); + MOCK_METHOD(hardware::Return<void>, getSubsystemLowPowerStats, + (getSubsystemLowPowerStats_cb _hidl_cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class PowerHalWrapperHidlV1_1Test : public Test { +public: + void SetUp() override; + +protected: + std::unique_ptr<HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIPowerV1_0>> mMockHalV1_0 = nullptr; + sp<StrictMock<MockIPowerV1_1>> mMockHalV1_1 = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +void PowerHalWrapperHidlV1_1Test::SetUp() { + mMockHalV1_0 = new StrictMock<MockIPowerV1_0>(); + mMockHalV1_1 = new StrictMock<MockIPowerV1_1>(); + mWrapper = std::make_unique<HidlHalWrapperV1_1>(mMockHalV1_0, mMockHalV1_1); + ASSERT_NE(mWrapper, nullptr); +} + +// ------------------------------------------------------------------------------------------------- + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostSuccessful) { + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000))) + .Times(Exactly(1)); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostFailed) { + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::INTERACTION), Eq(1000))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setBoost(Boost::INTERACTION, 1000); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetBoostUnsupported) { + auto result = mWrapper->setBoost(Boost::CAMERA_LAUNCH, 10); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetMode) { + { + InSequence seq; + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LOW_POWER), Eq(0))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_1.get(), + powerHintAsync(Eq(PowerHint::SUSTAINED_PERFORMANCE), Eq(1))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::VR_MODE), Eq(0))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_0.get(), setInteractive(Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHalV1_0.get(), + setFeature(Eq(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE), Eq(false))) + .Times(Exactly(1)); + } + + auto result = mWrapper->setMode(Mode::LAUNCH, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::LOW_POWER, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::SUSTAINED_PERFORMANCE, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::VR, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::INTERACTIVE, true); + ASSERT_EQ(HalResult::SUCCESSFUL, result); + result = mWrapper->setMode(Mode::DOUBLE_TAP_TO_WAKE, false); + ASSERT_EQ(HalResult::SUCCESSFUL, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeFailed) { + EXPECT_CALL(*mMockHalV1_1.get(), powerHintAsync(Eq(PowerHint::LAUNCH), Eq(1))) + .Times(Exactly(1)) + .WillRepeatedly([](PowerHint, int32_t) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + auto result = mWrapper->setMode(Mode::LAUNCH, 1); + ASSERT_EQ(HalResult::FAILED, result); +} + +TEST_F(PowerHalWrapperHidlV1_1Test, TestSetModeIgnored) { + auto result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true); + ASSERT_EQ(HalResult::UNSUPPORTED, result); +} diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index e355594176..2810bffdd3 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -145,13 +145,7 @@ void SensorDevice::initializeSensorList() { convertToSensor(convertToOldSensorInfo(list[i]), &sensor); if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) { - if(sensor.resolution == 0) { - // Don't crash here or the device will go into a crashloop. - ALOGW("%s must have a non-zero resolution", sensor.name); - // For simple algos, map their resolution to 1 if it's not specified - sensor.resolution = - SensorDeviceUtils::defaultResolutionForType(sensor.type); - } + sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor); // Some sensors don't have a default resolution and will be left at 0. // Don't crash in this case since CTS will verify that devices don't go to @@ -165,6 +159,9 @@ void SensorDevice::initializeSensorList() { SensorDeviceUtils::quantizeValue( &sensor.maxRange, promotedResolution); } + } else { + // Don't crash here or the device will go into a crashloop. + ALOGW("%s should have a non-zero resolution", sensor.name); } } diff --git a/services/sensorservice/SensorDeviceUtils.cpp b/services/sensorservice/SensorDeviceUtils.cpp index 52213cff7a..5aa283e240 100644 --- a/services/sensorservice/SensorDeviceUtils.cpp +++ b/services/sensorservice/SensorDeviceUtils.cpp @@ -31,7 +31,6 @@ namespace android { namespace SensorDeviceUtils { void quantizeSensorEventValues(sensors_event_t *event, float resolution) { - LOG_FATAL_IF(resolution == 0, "Resolution must be specified for all sensors!"); if (resolution == 0) { return; } @@ -79,8 +78,26 @@ void quantizeSensorEventValues(sensors_event_t *event, float resolution) { } } -float defaultResolutionForType(int type) { - switch ((SensorTypeV2_1)type) { +float resolutionForSensor(const sensor_t &sensor) { + switch ((SensorTypeV2_1)sensor.type) { + case SensorTypeV2_1::ACCELEROMETER: + case SensorTypeV2_1::MAGNETIC_FIELD: + case SensorTypeV2_1::GYROSCOPE: + case SensorTypeV2_1::MAGNETIC_FIELD_UNCALIBRATED: + case SensorTypeV2_1::GYROSCOPE_UNCALIBRATED: + case SensorTypeV2_1::ACCELEROMETER_UNCALIBRATED: { + if (sensor.maxRange == 0) { + ALOGE("No max range for sensor type %d, can't determine appropriate resolution", + sensor.type); + return sensor.resolution; + } + // Accel, gyro, and mag shouldn't have more than 24 bits of resolution on the most + // advanced devices. + double lowerBound = 2.0 * sensor.maxRange / std::pow(2, 24); + + // No need to check the upper bound as that's already enforced through CTS. + return std::max(sensor.resolution, static_cast<float>(lowerBound)); + } case SensorTypeV2_1::SIGNIFICANT_MOTION: case SensorTypeV2_1::STEP_DETECTOR: case SensorTypeV2_1::STEP_COUNTER: @@ -91,12 +108,14 @@ float defaultResolutionForType(int type) { case SensorTypeV2_1::WRIST_TILT_GESTURE: case SensorTypeV2_1::STATIONARY_DETECT: case SensorTypeV2_1::MOTION_DETECT: + // Ignore input resolution as all of these sensors are required to have a resolution of + // 1. return 1.0f; default: - // fall through and return 0 for all other types + // fall through and return the current resolution for all other types break; } - return 0.0f; + return sensor.resolution; } HidlServiceRegistrationWaiter::HidlServiceRegistrationWaiter() { diff --git a/services/sensorservice/SensorDeviceUtils.h b/services/sensorservice/SensorDeviceUtils.h index c232f0b408..1309971953 100644 --- a/services/sensorservice/SensorDeviceUtils.h +++ b/services/sensorservice/SensorDeviceUtils.h @@ -19,6 +19,7 @@ #include <android/hidl/manager/1.0/IServiceNotification.h> #include <hardware/sensors.h> +#include <utils/Log.h> #include <cmath> #include <condition_variable> @@ -33,6 +34,10 @@ namespace SensorDeviceUtils { // Quantizes a single value using a sensor's resolution. inline void quantizeValue(float *value, double resolution) { + if (resolution == 0) { + return; + } + // Increase the value of the sensor's nominal resolution to ensure that // sensor accuracy improvements, like runtime calibration, are not masked // during requantization. @@ -43,8 +48,8 @@ inline void quantizeValue(float *value, double resolution) { // Ensures a sensor event doesn't provide values finer grained than its sensor resolution allows. void quantizeSensorEventValues(sensors_event_t *event, float resolution); -// Provides a default resolution for simple sensor types if one wasn't provided by the HAL. -float defaultResolutionForType(int type); +// Returns the expected resolution value for the given sensor +float resolutionForSensor(const sensor_t &sensor); class HidlServiceRegistrationWaiter : public IServiceNotification { public: diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp index 3cccaf9329..6810c1b781 100644 --- a/services/sensorservice/SensorEventConnection.cpp +++ b/services/sensorservice/SensorEventConnection.cpp @@ -161,7 +161,7 @@ bool SensorService::SensorEventConnection::addSensor(int32_t handle) { Mutex::Autolock _l(mConnectionLock); sp<SensorInterface> si = mService->getSensorInterfaceFromHandle(handle); if (si == nullptr || - !canAccessSensor(si->getSensor(), "Tried adding", mOpPackageName) || + !canAccessSensor(si->getSensor(), "Add to SensorEventConnection: ", mOpPackageName) || mSensorInfo.count(handle) > 0) { return false; } @@ -460,8 +460,12 @@ bool SensorService::SensorEventConnection::noteOpIfRequired(const sensors_event_ mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) { success = true; } else { + int32_t sensorHandle = event.sensor; + String16 noteMsg("Sensor event ("); + noteMsg.append(String16(mService->getSensorStringType(sensorHandle))); + noteMsg.append(String16(")")); int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid, - mOpPackageName); + mOpPackageName, {}, noteMsg); success = (appOpMode == AppOpsManager::MODE_ALLOWED); } } diff --git a/services/sensorservice/SensorList.cpp b/services/sensorservice/SensorList.cpp index 0ce32cc06f..85ce0f0018 100644 --- a/services/sensorservice/SensorList.cpp +++ b/services/sensorservice/SensorList.cpp @@ -57,6 +57,12 @@ String8 SensorList::getName(int handle) const { mNonSensor.getName()); } +String8 SensorList::getStringType(int handle) const { + return getOne<String8>( + handle, [] (const Entry& e) -> String8 {return e.si->getSensor().getStringType();}, + mNonSensor.getStringType()); +} + sp<SensorInterface> SensorList::getInterface(int handle) const { return getOne<sp<SensorInterface>>( handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr); diff --git a/services/sensorservice/SensorList.h b/services/sensorservice/SensorList.h index 8424b22ed2..617ceefd0e 100644 --- a/services/sensorservice/SensorList.h +++ b/services/sensorservice/SensorList.h @@ -53,6 +53,8 @@ public: const Vector<Sensor> getVirtualSensors() const; String8 getName(int handle) const; + String8 getStringType(int handle) const; + sp<SensorInterface> getInterface(int handle) const; bool isNewHandle(int handle) const; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 3ca34bba1b..8f25bdba4f 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -701,6 +701,9 @@ status_t SensorService::shellCommand(int in, int out, int err, Vector<String16>& if (!checkCallingPermission(sManageSensorsPermission, nullptr, nullptr)) { return PERMISSION_DENIED; } + if (args.size() == 0) { + return BAD_INDEX; + } if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) { return BAD_VALUE; } @@ -1109,6 +1112,10 @@ String8 SensorService::getSensorName(int handle) const { return mSensors.getName(handle); } +String8 SensorService::getSensorStringType(int handle) const { + return mSensors.getStringType(handle); +} + bool SensorService::isVirtualSensor(int handle) const { sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle); return sensor != nullptr && sensor->isVirtual(); @@ -1804,9 +1811,6 @@ bool SensorService::canAccessSensor(const Sensor& sensor, const char* operation, } const int32_t opCode = sensor.getRequiredAppOp(); - const int32_t appOpMode = sAppOpsManager.checkOp(opCode, - IPCThreadState::self()->getCallingUid(), opPackageName); - bool appOpAllowed = appOpMode == AppOpsManager::MODE_ALLOWED; int targetSdkVersion = getTargetSdkVersion(opPackageName); bool canAccess = false; @@ -1819,14 +1823,16 @@ bool SensorService::canAccessSensor(const Sensor& sensor, const char* operation, canAccess = true; } else if (hasPermissionForSensor(sensor)) { // Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor - if (opCode < 0 || appOpAllowed) { + if (opCode >= 0) { + const int32_t appOpMode = sAppOpsManager.checkOp(opCode, + IPCThreadState::self()->getCallingUid(), opPackageName); + canAccess = (appOpMode == AppOpsManager::MODE_ALLOWED); + } else { canAccess = true; } } - if (canAccess) { - sAppOpsManager.noteOp(opCode, IPCThreadState::self()->getCallingUid(), opPackageName); - } else { + if (!canAccess) { ALOGE("%s %s a sensor (%s) without holding %s", String8(opPackageName).string(), operation, sensor.getName().string(), sensor.getRequiredPermission().string()); } diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 052cbfe290..50c7c2f383 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -295,6 +295,7 @@ private: virtual status_t dump(int fd, const Vector<String16>& args); status_t dumpProtoLocked(int fd, ConnectionSafeAutolock* connLock) const; String8 getSensorName(int handle) const; + String8 getSensorStringType(int handle) const; bool isVirtualSensor(int handle) const; sp<SensorInterface> getSensorInterfaceFromHandle(int handle) const; bool isWakeUpSensor(int type) const; diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index bdd04dbcb0..ed0d75b176 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -13,14 +13,16 @@ cc_defaults { cc_defaults { name: "libsurfaceflinger_defaults", - defaults: ["surfaceflinger_defaults"], + defaults: [ + "surfaceflinger_defaults", + "skia_deps", + ], cflags: [ "-DLOG_TAG=\"SurfaceFlinger\"", "-DGL_GLEXT_PROTOTYPES", "-DEGL_EGLEXT_PROTOTYPES", ], shared_libs: [ - "android.frameworks.vr.composer@2.0", "android.hardware.configstore-utils", "android.hardware.configstore@1.0", "android.hardware.configstore@1.1", @@ -36,7 +38,6 @@ cc_defaults { "android.hardware.power-cpp", "libbase", "libbinder", - "libbufferhubqueue", "libcutils", "libEGL", "libfmq", @@ -47,7 +48,6 @@ cc_defaults { "liblayers_proto", "liblog", "libnativewindow", - "libpdx_default_transport", "libprocessgroup", "libprotobuf-cpp-lite", "libstatslog", @@ -58,21 +58,13 @@ cc_defaults { "libutils", "libSurfaceFlingerProp", ], - // VrComposer is not used when building surfaceflinger for vendors - target: { - vendor: { - exclude_shared_libs: [ - "android.frameworks.vr.composer@2.0", - ], - }, - }, static_libs: [ "libcompositionengine", + "libframetimeline", "libperfetto_client_experimental", "librenderengine", "libserviceutils", "libtrace_proto", - "libvrflinger", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", @@ -145,6 +137,7 @@ filegroup { "DisplayHardware/HWComposer.cpp", "DisplayHardware/PowerAdvisor.cpp", "DisplayHardware/VirtualDisplaySurface.cpp", + "DisplayRenderArea.cpp", "Effects/Daltonizer.cpp", "EventLog/EventLog.cpp", "FrameTracer/FrameTracer.cpp", @@ -152,15 +145,14 @@ filegroup { "Layer.cpp", "LayerProtoHelper.cpp", "LayerRejecter.cpp", + "LayerRenderArea.cpp", "LayerVector.cpp", "MonitoredProducer.cpp", "NativeWindowSurface.cpp", "RefreshRateOverlay.cpp", "RegionSamplingThread.cpp", "RenderArea.cpp", - "Scheduler/DispSync.cpp", "Scheduler/DispSyncSource.cpp", - "Scheduler/EventControlThread.cpp", "Scheduler/EventThread.cpp", "Scheduler/OneShotTimer.cpp", "Scheduler/LayerHistory.cpp", @@ -168,15 +160,15 @@ filegroup { "Scheduler/LayerInfo.cpp", "Scheduler/LayerInfoV2.cpp", "Scheduler/MessageQueue.cpp", - "Scheduler/PhaseOffsets.cpp", "Scheduler/RefreshRateConfigs.cpp", "Scheduler/Scheduler.cpp", "Scheduler/SchedulerUtils.cpp", "Scheduler/Timer.cpp", "Scheduler/VSyncDispatchTimerQueue.cpp", "Scheduler/VSyncPredictor.cpp", - "Scheduler/VSyncModulator.cpp", + "Scheduler/VsyncModulator.cpp", "Scheduler/VSyncReactor.cpp", + "Scheduler/VsyncConfiguration.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", "SurfaceFlingerDefaultFactory.cpp", @@ -186,35 +178,12 @@ filegroup { ], } -cc_library_shared { - // Please use libsurfaceflinger_defaults to configure how the sources are - // built, so the same settings can be used elsewhere. - name: "libsurfaceflinger", - defaults: ["libsurfaceflinger_production_defaults"], - srcs: [ - ":libsurfaceflinger_sources", - - // Note: SurfaceFlingerFactory is not in the default sources so that it - // can be easily replaced. - "SurfaceFlingerFactory.cpp", - ], - cflags: [ - "-DUSE_VR_COMPOSER=1", - ], - // VrComposer is not used when building surfaceflinger for vendors - target: { - vendor: { - cflags: [ - "-DUSE_VR_COMPOSER=0", - ], - }, - }, - logtags: ["EventLog/EventLogTags.logtags"], -} - cc_defaults { name: "libsurfaceflinger_binary", - defaults: ["surfaceflinger_defaults"], + defaults: [ + "surfaceflinger_defaults", + "libsurfaceflinger_production_defaults", + ], cflags: [ "-DLOG_TAG=\"SurfaceFlinger\"", ], @@ -239,23 +208,31 @@ cc_defaults { "libserviceutils", "libtrace_proto", ], - ldflags: ["-Wl,--export-dynamic"], } filegroup { name: "surfaceflinger_binary_sources", - srcs: ["main_surfaceflinger.cpp"], + srcs: [ + ":libsurfaceflinger_sources", + "main_surfaceflinger.cpp", + ], } cc_binary { name: "surfaceflinger", defaults: ["libsurfaceflinger_binary"], init_rc: ["surfaceflinger.rc"], - srcs: [":surfaceflinger_binary_sources"], + srcs: [ + ":surfaceflinger_binary_sources", + // Note: SurfaceFlingerFactory is not in the filegroup so that it + // can be easily replaced. + "SurfaceFlingerFactory.cpp", + ], shared_libs: [ - "libsurfaceflinger", "libSurfaceFlingerProp", ], + + logtags: ["EventLog/EventLogTags.logtags"], } subdirs = [ diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index f0b0200bc5..fa75ffa403 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -204,8 +204,8 @@ std::optional<compositionengine::LayerFE::LayerSettings> BufferLayer::prepareCli layer.frameNumber = mCurrentFrameNumber; layer.bufferId = mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getId() : 0; - // TODO: we could be more subtle with isFixedSize() - const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize(); + const bool useFiltering = + targetSettings.needsFiltering || mNeedsFiltering || bufferNeedsFiltering(); // Query the texture matrix given our current filtering mode. float textureMatrix[16]; @@ -366,7 +366,7 @@ bool BufferLayer::onPostComposition(const DisplayDevice* display, mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); } else if (!display) { // Do nothing. - } else if (const auto displayId = display->getId(); + } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId()); displayId && mFlinger->getHwComposer().isConnected(*displayId)) { // The HWC doesn't support present fences, so use the refresh // timestamp instead. @@ -393,6 +393,15 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, nsecs_t expectedPresentTime) { ATRACE_CALL(); + // If this is not a valid vsync for the layer's uid, return and try again later + const bool isVsyncValidForUid = + mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid); + if (!isVsyncValidForUid) { + ATRACE_NAME("!isVsyncValidForUid"); + mFlinger->setTransactionFlags(eTraversalNeeded); + return false; + } + bool refreshRequired = latchSidebandStream(recomputeVisibleRegions); if (refreshRequired) { @@ -527,10 +536,6 @@ bool BufferLayer::hasReadyFrame() const { } uint32_t BufferLayer::getEffectiveScalingMode() const { - if (mOverrideScalingMode >= 0) { - return mOverrideScalingMode; - } - return mBufferInfo.mScaleMode; } @@ -539,20 +544,6 @@ bool BufferLayer::isProtected() const { return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); } -bool BufferLayer::latchUnsignaledBuffers() { - static bool propertyLoaded = false; - static bool latch = false; - static std::mutex mutex; - std::lock_guard<std::mutex> lock(mutex); - if (!propertyLoaded) { - char value[PROPERTY_VALUE_MAX] = {}; - property_get("debug.sf.latch_unsignaled", value, "0"); - latch = atoi(value); - propertyLoaded = true; - } - return latch; -} - // h/w composer set-up bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) { const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime); @@ -840,6 +831,10 @@ void BufferLayer::setTransformHint(ui::Transform::RotationFlags displayTransform } } +bool BufferLayer::bufferNeedsFiltering() const { + return isFixedSize(); +} + } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 26bfb4931b..deaf8461e8 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -37,6 +37,7 @@ #include "BufferLayerConsumer.h" #include "Client.h" #include "DisplayHardware/HWComposer.h" +#include "FrameTimeline.h" #include "FrameTracker.h" #include "Layer.h" #include "LayerVector.h" @@ -50,10 +51,7 @@ public: explicit BufferLayer(const LayerCreationArgs& args); virtual ~BufferLayer() override; - // ----------------------------------------------------------------------- - // Overriden from Layer - // ----------------------------------------------------------------------- -public: + // Implements Layer. sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const override; compositionengine::LayerFECompositionState* editCompositionState() override; @@ -96,8 +94,7 @@ public: bool hasReadyFrame() const override; - // Returns the current scaling mode, unless mOverrideScalingMode - // is set, in which case, it returns mOverrideScalingMode + // Returns the current scaling mode uint32_t getEffectiveScalingMode() const override; // Calls latchBuffer if the buffer has a frame queued and then releases the buffer. @@ -118,40 +115,9 @@ public: ui::Transform::RotationFlags getTransformHint() const override { return mTransformHint; } - // ----------------------------------------------------------------------- - // Functions that must be implemented by derived classes - // ----------------------------------------------------------------------- -private: - virtual bool fenceHasSignaled() const = 0; - virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0; - - PixelFormat getPixelFormat() const; - - // Computes the transform matrix using the setFilteringEnabled to determine whether the - // transform matrix should be computed for use with bilinear filtering. - void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]); - - virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0; - - virtual bool getAutoRefresh() const = 0; - virtual bool getSidebandStreamChanged() const = 0; - - // Latch sideband stream and returns true if the dirty region should be updated. - virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0; - - virtual bool hasFrameUpdate() const = 0; - - virtual status_t bindTextureImage() = 0; - virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - nsecs_t expectedPresentTime) = 0; - - virtual status_t updateActiveBuffer() = 0; - virtual status_t updateFrameNumber(nsecs_t latchTime) = 0; - - // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they - // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion - // detection. - bool needsInputInfo() const override { return !mPotentialCursor; } + // Returns true if the transformed buffer size does not match the layer size and we need + // to apply filtering. + virtual bool bufferNeedsFiltering() const; protected: struct BufferInfo { @@ -187,9 +153,6 @@ protected: bool onPreComposition(nsecs_t) override; void preparePerFrameCompositionState() override; - // Loads the corresponding system property once per process - static bool latchUnsignaledBuffers(); - // Check all of the local sync points to ensure that all transactions // which need to have been applied prior to the frame which is about to // be latched have signaled @@ -207,7 +170,7 @@ protected: void updateCloneBufferInfo() override; uint64_t mPreviousFrameNumber = 0; - virtual uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const; + uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const override; void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; @@ -216,6 +179,29 @@ protected: ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0; private: + virtual bool fenceHasSignaled() const = 0; + virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0; + virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0; + + virtual bool getAutoRefresh() const = 0; + virtual bool getSidebandStreamChanged() const = 0; + + // Latch sideband stream and returns true if the dirty region should be updated. + virtual bool latchSidebandStream(bool& recomputeVisibleRegions) = 0; + + virtual bool hasFrameUpdate() const = 0; + + virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + nsecs_t expectedPresentTime) = 0; + + virtual status_t updateActiveBuffer() = 0; + virtual status_t updateFrameNumber(nsecs_t latchTime) = 0; + + // We generate InputWindowHandles for all non-cursor buffered layers regardless of whether they + // have an InputChannel. This is to enable the InputDispatcher to do PID based occlusion + // detection. + bool needsInputInfo() const override { return !mPotentialCursor; } + // Returns true if this layer requires filtering bool needsFiltering(const DisplayDevice*) const override; bool needsFilteringForScreenshots(const DisplayDevice*, @@ -225,6 +211,12 @@ private: // and its parent layer is not bounded Rect getBufferSize(const State& s) const override; + PixelFormat getPixelFormat() const; + + // Computes the transform matrix using the setFilteringEnabled to determine whether the + // transform matrix should be computed for use with bilinear filtering. + void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]); + std::unique_ptr<compositionengine::LayerFECompositionState> mCompositionState; FloatRect computeSourceBounds(const FloatRect& parentBounds) const override; diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp index 8722952bba..69d2d11a4d 100644 --- a/services/surfaceflinger/BufferLayerConsumer.cpp +++ b/services/surfaceflinger/BufferLayerConsumer.cpp @@ -25,7 +25,7 @@ #include "BufferLayerConsumer.h" #include "Layer.h" -#include "Scheduler/DispSync.h" +#include "Scheduler/VsyncController.h" #include <inttypes.h> @@ -153,25 +153,9 @@ status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t e if (err != NO_ERROR) { return err; } - - if (!mRE.useNativeFenceSync()) { - // Bind the new buffer to the GL texture. - // - // Older devices require the "implicit" synchronization provided - // by glEGLImageTargetTexture2DOES, which this method calls. Newer - // devices will either call this in Layer::onDraw, or (if it's not - // a GL-composited layer) not at all. - err = bindTextureImageLocked(); - } - return err; } -status_t BufferLayerConsumer::bindTextureImage() { - Mutex::Autolock lock(mMutex); - return bindTextureImageLocked(); -} - void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) { if (!fence->isValid()) { return; @@ -292,17 +276,6 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, return err; } -status_t BufferLayerConsumer::bindTextureImageLocked() { - ATRACE_CALL(); - - if (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) { - return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer->graphicBuffer(), - mCurrentFence); - } - - return NO_INIT; -} - void BufferLayerConsumer::getTransformMatrix(float mtx[16]) { Mutex::Autolock lock(mMutex); memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); @@ -530,6 +503,9 @@ void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const BufferLayerConsumer::Image::Image(const sp<GraphicBuffer>& graphicBuffer, renderengine::RenderEngine& engine) : mGraphicBuffer(graphicBuffer), mRE(engine) { + if (graphicBuffer != nullptr && (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED)) { + return; + } mRE.cacheExternalTextureBuffer(mGraphicBuffer); } diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h index 5e3044fd98..dd39214aff 100644 --- a/services/surfaceflinger/BufferLayerConsumer.h +++ b/services/surfaceflinger/BufferLayerConsumer.h @@ -34,7 +34,6 @@ namespace android { // ---------------------------------------------------------------------------- -class DispSync; class Layer; class String8; @@ -95,9 +94,6 @@ public: status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime, bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber); - // See BufferLayerConsumer::bindTextureImageLocked(). - status_t bindTextureImage(); - // setReleaseFence stores a fence that will signal when the current buffer // is no longer being read. This fence will be returned to the producer // when the current buffer is released by updateTexImage(). Multiple @@ -215,10 +211,6 @@ protected: PendingRelease* pendingRelease = nullptr) EXCLUDES(mImagesMutex); - // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target. - // If the bind succeeds, this calls doFenceWait. - status_t bindTextureImageLocked(); - private: // Utility class for managing GraphicBuffer references into renderengine class Image { diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 6e4235e409..71b05fd2bf 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -35,6 +35,7 @@ #include "TimeStats/TimeStats.h" namespace android { +using PresentState = frametimeline::SurfaceFrame::PresentState; BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {} @@ -109,7 +110,7 @@ bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const { Mutex::Autolock lock(mQueueItemLock); - const int64_t addedTime = mQueueItems[0].mTimestamp; + const int64_t addedTime = mQueueItems[0].item.mTimestamp; // Ignore timestamps more than a second in the future const bool isPlausible = addedTime < (expectedPresentTime + s2ns(1)); @@ -131,16 +132,12 @@ bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const { // ----------------------------------------------------------------------- bool BufferQueueLayer::fenceHasSignaled() const { - if (latchUnsignaledBuffers()) { - return true; - } - if (!hasFrameUpdate()) { return true; } Mutex::Autolock lock(mQueueItemLock); - if (mQueueItems[0].mIsDroppable) { + if (mQueueItems[0].item.mIsDroppable) { // Even though this buffer's fence may not have signaled yet, it could // be replaced by another buffer before it has a chance to, which means // that it's possible to get into a situation where a buffer is never @@ -148,7 +145,7 @@ bool BufferQueueLayer::fenceHasSignaled() const { return true; } const bool fenceSignaled = - mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; + mQueueItems[0].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; if (!fenceSignaled) { mFlinger->mTimeStats->incrementLatchSkipped(getSequence(), TimeStats::LatchSkipReason::LateAcquire); @@ -163,12 +160,12 @@ bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) co } Mutex::Autolock lock(mQueueItemLock); - return mQueueItems[0].mTimestamp <= expectedPresentTime; + return mQueueItems[0].item.mTimestamp <= expectedPresentTime; } uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const { Mutex::Autolock lock(mQueueItemLock); - uint64_t frameNumber = mQueueItems[0].mFrameNumber; + uint64_t frameNumber = mQueueItems[0].item.mFrameNumber; // The head of the queue will be dropped if there are signaled and timely frames behind it if (isRemovedFromCurrentState()) { @@ -177,23 +174,23 @@ uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const { for (int i = 1; i < mQueueItems.size(); i++) { const bool fenceSignaled = - mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; + mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; if (!fenceSignaled) { break; } // We don't drop frames without explicit timestamps - if (mQueueItems[i].mIsAutoTimestamp) { + if (mQueueItems[i].item.mIsAutoTimestamp) { break; } - const nsecs_t desiredPresent = mQueueItems[i].mTimestamp; + const nsecs_t desiredPresent = mQueueItems[i].item.mTimestamp; if (desiredPresent < expectedPresentTime - BufferQueueConsumer::MAX_REASONABLE_NSEC || desiredPresent > expectedPresentTime) { break; } - frameNumber = mQueueItems[i].mFrameNumber; + frameNumber = mQueueItems[i].item.mFrameNumber; } return frameNumber; @@ -208,8 +205,12 @@ bool BufferQueueLayer::getSidebandStreamChanged() const { } bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { + // We need to update the sideband stream if the layer has both a buffer and a sideband stream. + const bool updateSidebandStream = hasFrameUpdate() && mSidebandStream.get(); + bool sidebandStreamChanged = true; - if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) { + if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false) || + updateSidebandStream) { // mSidebandStreamChanged was changed to false mSidebandStream = mConsumer->getSidebandStream(); auto* layerCompositionState = editCompositionState(); @@ -229,10 +230,6 @@ bool BufferQueueLayer::hasFrameUpdate() const { return mQueuedFrames > 0; } -status_t BufferQueueLayer::bindTextureImage() { - return mConsumer->bindTextureImage(); -} - status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, nsecs_t expectedPresentTime) { // This boolean is used to make sure that SurfaceFlinger's shadow copy @@ -242,7 +239,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t bool queuedBuffer = false; const int32_t layerId = getSequence(); LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions, - getProducerStickyTransform() != 0, mName, mOverrideScalingMode, + getProducerStickyTransform() != 0, mName, getTransformToDisplayInverse()); if (isRemovedFromCurrentState()) { @@ -258,11 +255,11 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t Mutex::Autolock lock(mQueueItemLock); for (int i = 0; i < mQueueItems.size(); i++) { bool fenceSignaled = - mQueueItems[i].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; + mQueueItems[i].item.mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; if (!fenceSignaled) { break; } - lastSignaledFrameNumber = mQueueItems[i].mFrameNumber; + lastSignaledFrameNumber = mQueueItems[i].item.mFrameNumber; } } const uint64_t maxFrameNumberToAcquire = @@ -280,9 +277,13 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t // and return early if (queuedBuffer) { Mutex::Autolock lock(mQueueItemLock); - mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage); - mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber); - mQueueItems.removeAt(0); + mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage); + mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber); + if (mQueueItems[0].surfaceFrame) { + mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame), + PresentState::Dropped); + } + mQueueItems.erase(mQueueItems.begin()); mQueuedFrames--; } return BAD_VALUE; @@ -293,6 +294,12 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t // early. if (queuedBuffer) { Mutex::Autolock lock(mQueueItemLock); + for (auto& [item, surfaceFrame] : mQueueItems) { + if (surfaceFrame) { + mFlinger->mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), + PresentState::Dropped); + } + } mQueueItems.clear(); mQueuedFrames = 0; mFlinger->mTimeStats->onDestroy(layerId); @@ -316,19 +323,29 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t // Remove any stale buffers that have been dropped during // updateTexImage - while (mQueueItems[0].mFrameNumber != currentFrameNumber) { - mConsumer->mergeSurfaceDamage(mQueueItems[0].mSurfaceDamage); - mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].mFrameNumber); - mQueueItems.removeAt(0); + while (mQueueItems[0].item.mFrameNumber != currentFrameNumber) { + mConsumer->mergeSurfaceDamage(mQueueItems[0].item.mSurfaceDamage); + mFlinger->mTimeStats->removeTimeRecord(layerId, mQueueItems[0].item.mFrameNumber); + if (mQueueItems[0].surfaceFrame) { + mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame), + PresentState::Dropped); + } + mQueueItems.erase(mQueueItems.begin()); mQueuedFrames--; } - uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId(); + uint64_t bufferID = mQueueItems[0].item.mGraphicBuffer->getId(); mFlinger->mTimeStats->setLatchTime(layerId, currentFrameNumber, latchTime); mFlinger->mFrameTracer->traceTimestamp(layerId, bufferID, currentFrameNumber, latchTime, FrameTracer::FrameEvent::LATCH); - mQueueItems.removeAt(0); + if (mQueueItems[0].surfaceFrame) { + mQueueItems[0].surfaceFrame->setAcquireFenceTime( + mQueueItems[0].item.mFenceTime->getSignalTime()); + mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mQueueItems[0].surfaceFrame), + PresentState::Presented); + } + mQueueItems.erase(mQueueItems.begin()); } // Decrement the queued-frames count. Signal another event if we @@ -364,6 +381,10 @@ status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) { return NO_ERROR; } +void BufferQueueLayer::setFrameTimelineVsyncForBuffer(int64_t frameTimelineVsyncId) { + mFrameTimelineVsyncId = frameTimelineVsyncId; +} + // ----------------------------------------------------------------------- // Interface implementation for BufferLayerConsumer::ContentsChangedListener // ----------------------------------------------------------------------- @@ -420,7 +441,12 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { } } - mQueueItems.push_back(item); + auto surfaceFrame = + mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName, + mName, mFrameTimelineVsyncId); + surfaceFrame->setActualQueueTime(systemTime()); + + mQueueItems.push_back({item, std::move(surfaceFrame)}); mQueuedFrames++; // Wake up any pending callbacks @@ -453,7 +479,13 @@ void BufferQueueLayer::onFrameReplaced(const BufferItem& item) { ALOGE("Can't replace a frame on an empty queue"); return; } - mQueueItems.editItemAt(mQueueItems.size() - 1) = item; + + auto surfaceFrame = + mFlinger->mFrameTimeline->createSurfaceFrameForToken(mOwnerPid, mOwnerUid, mName, + mName, mFrameTimelineVsyncId); + surfaceFrame->setActualQueueTime(systemTime()); + mQueueItems[mQueueItems.size() - 1].item = item; + mQueueItems[mQueueItems.size() - 1].surfaceFrame = std::move(surfaceFrame); // Wake up any pending callbacks mLastFrameNumberReceived = item.mFrameNumber; diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h index 5ebc22d2af..fb8a0c22ee 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -22,6 +22,10 @@ namespace android { +namespace frametimeline { +class SurfaceFrame; +} + /* * A new BufferQueue and a new BufferLayerConsumer are created when the * BufferLayer is first referenced. @@ -35,10 +39,7 @@ public: explicit BufferQueueLayer(const LayerCreationArgs&); ~BufferQueueLayer() override; - // ----------------------------------------------------------------------- - // Interface implementation for Layer - // ----------------------------------------------------------------------- -public: + // Implements Layer. const char* getType() const override { return "BufferQueueLayer"; } void onLayerDisplayed(const sp<Fence>& releaseFence) override; @@ -54,41 +55,12 @@ public: bool shouldPresentNow(nsecs_t expectedPresentTime) const override; - // ----------------------------------------------------------------------- - - // ----------------------------------------------------------------------- - // Interface implementation for BufferLayer - // ----------------------------------------------------------------------- -public: + // Implements BufferLayer. bool fenceHasSignaled() const override; bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override; -private: - uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override; - - bool getAutoRefresh() const override; - bool getSidebandStreamChanged() const override; - - bool latchSidebandStream(bool& recomputeVisibleRegions) override; - void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; - - bool hasFrameUpdate() const override; - - status_t bindTextureImage() override; - status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - nsecs_t expectedPresentTime) override; - - status_t updateActiveBuffer() override; - status_t updateFrameNumber(nsecs_t latchTime) override; - - sp<Layer> createClone() override; - - void onFrameAvailable(const BufferItem& item); - void onFrameReplaced(const BufferItem& item); - void onSidebandStreamChanged(); - void onFrameDequeued(const uint64_t bufferId); - void onFrameDetached(const uint64_t bufferId); - void onFrameCancelled(const uint64_t bufferId); + status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format); + sp<IGraphicBufferProducer> getProducer() const; protected: void gatherBufferInfo() override; @@ -114,19 +86,39 @@ protected: BufferQueueLayer* mBufferQueueLayer = nullptr; Mutex mMutex; }; - // ----------------------------------------------------------------------- -public: - status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format); +private: + uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override; - sp<IGraphicBufferProducer> getProducer() const; + bool getAutoRefresh() const override; + bool getSidebandStreamChanged() const override; -private: - // Temporary - Used only for LEGACY camera mode. - uint32_t getProducerStickyTransform() const; + bool latchSidebandStream(bool& recomputeVisibleRegions) override; + void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; + + bool hasFrameUpdate() const override; + + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + nsecs_t expectedPresentTime) override; + + status_t updateActiveBuffer() override; + status_t updateFrameNumber(nsecs_t latchTime) override; + void setFrameTimelineVsyncForBuffer(int64_t frameTimelineVsyncId) override; + + sp<Layer> createClone() override; void onFirstRef() override; + void onFrameAvailable(const BufferItem& item); + void onFrameReplaced(const BufferItem& item); + void onSidebandStreamChanged(); + void onFrameDequeued(const uint64_t bufferId); + void onFrameDetached(const uint64_t bufferId); + void onFrameCancelled(const uint64_t bufferId); + + // Temporary - Used only for LEGACY camera mode. + uint32_t getProducerStickyTransform() const; + sp<BufferLayerConsumer> mConsumer; sp<IGraphicBufferProducer> mProducer; @@ -138,7 +130,14 @@ private: // Local copy of the queued contents of the incoming BufferQueue mutable Mutex mQueueItemLock; Condition mQueueItemCondition; - Vector<BufferItem> mQueueItems; + + struct BufferData { + BufferData(BufferItem item, std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame) + : item(item), surfaceFrame(std::move(surfaceFrame)) {} + BufferItem item; + std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame; + }; + std::vector<BufferData> mQueueItems; std::atomic<uint64_t> mLastFrameNumberReceived{0}; bool mAutoRefresh{false}; @@ -148,6 +147,11 @@ private: std::atomic<bool> mSidebandStreamChanged{false}; sp<ContentsChangedListener> mContentsChangedListener; + + // The last vsync id received on this layer. This will be used when we get + // a buffer to correlate the buffer with the vsync id. Can only be accessed + // with the SF state lock held. + std::optional<int64_t> mFrameTimelineVsyncId; }; } // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 790f2ece77..a64b2434d3 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -48,7 +48,6 @@ const std::array<float, 16> BufferStateLayer::IDENTITY_MATRIX{ BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) { - mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; mCurrentState.dataspace = ui::Dataspace::V0_SRGB; } @@ -161,6 +160,10 @@ 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; @@ -258,12 +261,14 @@ 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, - const client_cache_t& clientCacheId) { + const client_cache_t& clientCacheId, uint64_t frameNumber) { + ATRACE_CALL(); + if (mCurrentState.buffer) { mReleasePreviousBuffer = true; } - mCurrentState.frameNumber++; + mCurrentState.frameNumber = frameNumber; mCurrentState.buffer = buffer; mCurrentState.clientCacheId = clientCacheId; @@ -272,7 +277,7 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence const int32_t layerId = getSequence(); mFlinger->mTimeStats->setPostTime(layerId, mCurrentState.frameNumber, getName().c_str(), - postTime); + mOwnerUid, postTime); desiredPresentTime = desiredPresentTime <= 0 ? 0 : desiredPresentTime; mCurrentState.desiredPresentTime = desiredPresentTime; @@ -422,10 +427,6 @@ FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) c // Interface implementation for BufferLayer // ----------------------------------------------------------------------- bool BufferStateLayer::fenceHasSignaled() const { - if (latchUnsignaledBuffers()) { - return true; - } - const bool fenceSignaled = getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled; if (!fenceSignaled) { @@ -512,13 +513,6 @@ bool BufferStateLayer::hasFrameUpdate() const { return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr); } -status_t BufferStateLayer::bindTextureImage() { - const State& s(getDrawingState()); - auto& engine(mFlinger->getRenderEngine()); - - return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence); -} - status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime, nsecs_t /*expectedPresentTime*/) { const State& s(getDrawingState()); @@ -563,20 +557,6 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse handle->frameNumber = mDrawingState.frameNumber; } - if (!SyncFeatures::getInstance().useNativeFenceSync()) { - // Bind the new buffer to the GL texture. - // - // Older devices require the "implicit" synchronization provided - // by glEGLImageTargetTexture2DOES, which this method calls. Newer - // devices will either call this in Layer::onDraw, or (if it's not - // a GL-composited layer) not at all. - status_t err = bindTextureImage(); - if (err != NO_ERROR) { - mFlinger->mTimeStats->onDestroy(layerId); - return BAD_VALUE; - } - } - mFlinger->mTimeStats->setAcquireFence(layerId, mDrawingState.frameNumber, std::make_shared<FenceTime>(mDrawingState.acquireFence)); mFlinger->mTimeStats->setLatchTime(layerId, mDrawingState.frameNumber, latchTime); @@ -702,6 +682,10 @@ void BufferStateLayer::gatherBufferInfo() { mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId); } +uint32_t BufferStateLayer::getEffectiveScalingMode() const { + return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; +} + Rect BufferStateLayer::computeCrop(const State& s) { if (s.crop.isEmpty() && s.buffer) { return s.buffer->getBounds(); @@ -760,6 +744,31 @@ Layer::RoundedCornerState BufferStateLayer::getRoundedCornerState() const { static_cast<float>(s.active.transform.ty() + s.active.h)), radius); } + +bool BufferStateLayer::bufferNeedsFiltering() const { + const State& s(getDrawingState()); + if (!s.buffer) { + return false; + } + + uint32_t bufferWidth = s.buffer->width; + uint32_t bufferHeight = s.buffer->height; + + // Undo any transformations on the buffer and return the result. + if (s.transform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + + if (s.transformToDisplayInverse) { + uint32_t invTransform = DisplayDevice::getPrimaryDisplayRotationFlags(); + if (invTransform & ui::Transform::ROT_90) { + std::swap(bufferWidth, bufferHeight); + } + } + + const Rect layerSize{getBounds()}; + return layerSize.width() != bufferWidth || layerSize.height() != bufferHeight; +} } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 00fa7f7a2d..104a13be71 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -36,9 +36,7 @@ public: ~BufferStateLayer() override; - // ----------------------------------------------------------------------- - // Interface implementation for Layer - // ----------------------------------------------------------------------- + // Implements Layer. const char* getType() const override { return "BufferStateLayer"; } void onLayerDisplayed(const sp<Fence>& releaseFence) override; @@ -72,7 +70,8 @@ public: bool setCrop(const Rect& crop) override; bool setFrame(const Rect& frame) override; bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime, - nsecs_t desiredPresentTime, const client_cache_t& clientCacheId) override; + nsecs_t desiredPresentTime, const client_cache_t& clientCacheId, + uint64_t frameNumber) override; bool setAcquireFence(const sp<Fence>& fence) override; bool setDataspace(ui::Dataspace dataspace) override; bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; @@ -93,7 +92,6 @@ public: return false; } bool setCrop_legacy(const Rect& /*crop*/) override { return false; } - bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; } void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/, uint64_t /*frameNumber*/) override {} void deferTransactionUntil_legacy(const sp<Layer>& /*barrierLayer*/, @@ -111,12 +109,15 @@ public: bool fenceHasSignaled() const override; bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override; bool onPreComposition(nsecs_t refreshStartTime) override; + uint32_t getEffectiveScalingMode() const override; protected: void gatherBufferInfo() override; uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const; private: + friend class SlotGenerationTest; + bool updateFrameEventHistory(const sp<Fence>& acquireFence, nsecs_t postedTime, nsecs_t requestedPresentTime); @@ -129,7 +130,6 @@ private: bool hasFrameUpdate() const override; - status_t bindTextureImage() override; status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, nsecs_t expectedPresentTime) override; @@ -141,10 +141,10 @@ private: // Crop that applies to the buffer Rect computeCrop(const State& s); -private: - friend class SlotGenerationTest; bool willPresentCurrentTransaction() const; + bool bufferNeedsFiltering() const override; + static const std::array<float, 16> IDENTITY_MATRIX; std::unique_ptr<renderengine::Image> mTextureImage; diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 78bbcba4b4..aac6c913cf 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -79,17 +79,18 @@ sp<Layer> Client::getLayerUser(const sp<IBinder>& handle) const status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp<IBinder>& parentHandle, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, uint32_t* outTransformHint) { + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, + uint32_t* outTransformHint) { // We rely on createLayer to check permissions. return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp, - parentHandle, nullptr, outTransformHint); + parentHandle, outLayerId, nullptr, outTransformHint); } status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp<IGraphicBufferProducer>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, uint32_t* outTransformHint) { if (mFlinger->authenticateSurfaceTexture(parent) == false) { ALOGE("failed to authenticate surface texture"); @@ -103,11 +104,12 @@ status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32 } return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp, - nullptr, layer, outTransformHint); + nullptr, outLayerId, layer, outTransformHint); } -status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) { - return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle); +status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle, + int32_t* outLayerId) { + return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outLayerId); } status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const { diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h index e9063e5bb6..15cd763822 100644 --- a/services/surfaceflinger/Client.h +++ b/services/surfaceflinger/Client.h @@ -28,13 +28,9 @@ namespace android { -// --------------------------------------------------------------------------- - class Layer; class SurfaceFlinger; -// --------------------------------------------------------------------------- - class Client : public BnSurfaceComposerClient { public: @@ -54,17 +50,18 @@ private: virtual status_t createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp<IBinder>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, uint32_t* outTransformHint = nullptr); virtual status_t createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, const sp<IGraphicBufferProducer>& parent, LayerMetadata metadata, sp<IBinder>* handle, - sp<IGraphicBufferProducer>* gbp, + sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId, uint32_t* outTransformHint = nullptr); - status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle); + status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle, + int32_t* outLayerId); virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const; @@ -80,7 +77,6 @@ private: mutable Mutex mLock; }; -// --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_SF_CLIENT_H diff --git a/services/surfaceflinger/Colorizer.h b/services/surfaceflinger/Colorizer.h index b7d61ce08e..b0cc0305fd 100644 --- a/services/surfaceflinger/Colorizer.h +++ b/services/surfaceflinger/Colorizer.h @@ -21,8 +21,6 @@ namespace android { -// --------------------------------------------------------------------------- - class Colorizer { bool mEnabled; public: @@ -59,9 +57,6 @@ public: } }; -// --------------------------------------------------------------------------- - -}; // namespace android - +} // namespace android #endif /* ANDROID_SURFACE_FLINGER_COLORIZER_H */ diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index b37ca33d34..57dc60bbac 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -6,7 +6,6 @@ cc_defaults { "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", ], shared_libs: [ - "android.frameworks.vr.composer@2.0", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", @@ -96,8 +95,9 @@ cc_test { "tests/MockHWC2.cpp", "tests/MockHWComposer.cpp", "tests/MockPowerAdvisor.cpp", - "tests/OutputTest.cpp", "tests/OutputLayerTest.cpp", + "tests/OutputTest.cpp", + "tests/ProjectionSpaceTest.cpp", "tests/RenderSurfaceTest.cpp", ], static_libs: [ diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h index a38d1f3c99..01dd5343b2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -34,8 +34,8 @@ struct DisplayColorProfileCreationArgs; */ class Display : public virtual Output { public: - // Gets the HWC DisplayId for the display if there is one - virtual const std::optional<DisplayId>& getId() const = 0; + // Gets the DisplayId for the display + virtual DisplayId getId() const = 0; // True if the display is secure virtual bool isSecure() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h index 6bc677d432..95ba9f0429 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h @@ -20,12 +20,14 @@ #include <optional> #include <string> +#include <ui/DisplayId.h> #include <ui/DisplayInfo.h> #include <ui/PixelFormat.h> #include <ui/Size.h> #include "DisplayHardware/DisplayIdentification.h" #include "DisplayHardware/PowerAdvisor.h" +#include "DisplayIdGenerator.h" namespace android::compositionengine { @@ -65,6 +67,9 @@ struct DisplayCreationArgs { // Debugging. Human readable name for the display. std::string name; + + // Generator for IDs of virtual displays, which are backed by the GPU. + DisplayIdGenerator<GpuVirtualDisplayId>* gpuVirtualDisplayIdGenerator; }; /** @@ -95,6 +100,12 @@ public: return *this; } + DisplayCreationArgsBuilder& setGpuVirtualDisplayIdGenerator( + DisplayIdGenerator<GpuVirtualDisplayId>& generator) { + mArgs.gpuVirtualDisplayIdGenerator = &generator; + return *this; + } + DisplayCreationArgsBuilder& setIsSecure(bool isSecure) { mArgs.isSecure = isSecure; return *this; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index 6cc90cb3ee..5c7f12dd3d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -80,10 +80,6 @@ public: // The clip region, or visible region that is being rendered to const Region& clip; - // If true, the layer should use an identity transform for its position - // transform. Used only by the captureScreen API call. - const bool useIdentityTransform; - // If set to true, the layer should enable filtering when rendering. const bool needsFiltering; @@ -148,7 +144,6 @@ using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>; static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs, const LayerFE::ClientCompositionTargetSettings& rhs) { return lhs.clip.hasSameRects(rhs.clip) && - lhs.useIdentityTransform == rhs.useIdentityTransform && lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure && lhs.supportsProtectedContent == rhs.supportsProtectedContent && lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.viewport == rhs.viewport && @@ -170,7 +165,6 @@ static inline void PrintTo(const LayerFE::ClientCompositionTargetSettings& setti *os << "ClientCompositionTargetSettings{"; *os << "\n .clip = \n"; PrintTo(settings.clip, os); - *os << "\n .useIdentityTransform = " << settings.useIdentityTransform; *os << "\n .needsFiltering = " << settings.needsFiltering; *os << "\n .isSecure = " << settings.isSecure; *os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index b4ed92f965..5a3b9ac817 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -20,6 +20,7 @@ #include <gui/HdrMetadata.h> #include <math/mat4.h> +#include <ui/BlurRegion.h> #include <ui/FloatRect.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -118,6 +119,9 @@ struct LayerFECompositionState { // length of the shadow in screen space float shadowRadius{0.f}; + // List of regions that require blur + std::vector<BlurRegion> blurRegions; + /* * Geometry state */ @@ -130,16 +134,6 @@ struct LayerFECompositionState { Rect geomContentCrop; Rect geomCrop; - /* - * Extra metadata - */ - - // The type for this layer - int type{0}; - - // The appId for this layer - int appId{0}; - GenericLayerMetadataMap metadata; /* diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index baf52588e6..3be1cc4265 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -163,11 +163,15 @@ public: virtual void setCompositionEnabled(bool) = 0; // Sets the projection state to use - virtual void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame, - const Rect& viewport, const Rect& sourceClip, - const Rect& destinationClip, bool needsFiltering) = 0; + virtual void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, + const Rect& orientedDisplaySpaceRect) = 0; // Sets the bounds to use - virtual void setBounds(const ui::Size&) = 0; + virtual void setDisplaySize(const ui::Size&) = 0; + // Gets the transform hint used in layers that belong to this output. Used to guide + // composition orientation so that HW overlay can be used when display isn't in its natural + // orientation on some devices. Therefore usually we only use transform hint from display + // output. + virtual ui::Transform::RotationFlags getTransformHint() const = 0; // Sets the layer stack filtering settings for this output. See // belongsInOutput for full details. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h new file mode 100644 index 0000000000..7ca91d86eb --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/ProjectionSpace.h @@ -0,0 +1,115 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <ostream> + +#include <android-base/stringprintf.h> +#include <ui/Rect.h> +#include <ui/Rotation.h> +#include <ui/Transform.h> + +namespace android { +namespace compositionengine { + +// Geometrical space to which content is projected. +// For example, this can be the layer space or the physical display space. +struct ProjectionSpace { + ProjectionSpace() = default; + ProjectionSpace(ui::Size size, Rect content) + : bounds(std::move(size)), content(std::move(content)) {} + + // Bounds of this space. Always starts at (0,0). + Rect bounds; + + // Rect onto which content is projected. + Rect content; + + // The orientation of this space. This value is meaningful only in relation to the rotation + // of another projection space and it's used to determine the rotating transformation when + // mapping between the two. + // As a convention when using this struct orientation = 0 for the "oriented*" projection + // spaces. For example when the display is rotated 90 degress counterclockwise, the orientation + // of the display space will become 90, while the orientation of the layer stack space will + // remain the same. + ui::Rotation orientation = ui::ROTATION_0; + + // Returns a transform which maps this.content into destination.content + // and also rotates according to this.orientation and destination.orientation + ui::Transform getTransform(const ProjectionSpace& destination) const { + ui::Rotation rotation = destination.orientation - orientation; + + // Compute a transformation which rotates the destination in a way it has the same + // orientation as us. + const uint32_t inverseRotationFlags = ui::Transform::toRotationFlags(-rotation); + ui::Transform inverseRotatingTransform; + inverseRotatingTransform.set(inverseRotationFlags, destination.bounds.width(), + destination.bounds.height()); + // The destination content rotated so it has the same orientation as us. + Rect orientedDestContent = inverseRotatingTransform.transform(destination.content); + + // Compute translation from the source content to (0, 0). + const float sourceX = content.left; + const float sourceY = content.top; + ui::Transform sourceTranslation; + sourceTranslation.set(-sourceX, -sourceY); + + // Compute scaling transform which maps source content to destination content, assuming + // they are both at (0, 0). + ui::Transform scale; + const float scaleX = static_cast<float>(orientedDestContent.width()) / content.width(); + const float scaleY = static_cast<float>(orientedDestContent.height()) / content.height(); + scale.set(scaleX, 0, 0, scaleY); + + // Compute translation from (0, 0) to the orientated destination content. + const float destX = orientedDestContent.left; + const float destY = orientedDestContent.top; + ui::Transform destTranslation; + destTranslation.set(destX, destY); + + // Compute rotation transform. + const uint32_t orientationFlags = ui::Transform::toRotationFlags(rotation); + auto orientedDestWidth = destination.bounds.width(); + auto orientedDestHeight = destination.bounds.height(); + if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) { + std::swap(orientedDestWidth, orientedDestHeight); + } + ui::Transform rotationTransform; + rotationTransform.set(orientationFlags, orientedDestWidth, orientedDestHeight); + + // The layerStackSpaceRect and orientedDisplaySpaceRect are both in the logical orientation. + // Apply the logical translation, scale to physical size, apply the + // physical translation and finally rotate to the physical orientation. + return rotationTransform * destTranslation * scale * sourceTranslation; + } +}; + +} // namespace compositionengine + +inline std::string to_string(const android::compositionengine::ProjectionSpace& space) { + return android::base:: + StringPrintf("ProjectionSpace(bounds = %s, content = %s, orientation = %s)", + to_string(space.bounds).c_str(), to_string(space.content).c_str(), + toCString(space.orientation)); +} + +// Defining PrintTo helps with Google Tests. +inline void PrintTo(const android::compositionengine::ProjectionSpace& space, ::std::ostream* os) { + *os << to_string(space); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index 7a4f7383d0..54e91ae6be 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -57,7 +57,7 @@ public: void finishFrame(const CompositionRefreshArgs&) override; // compositionengine::Display overrides - const std::optional<DisplayId>& getId() const override; + DisplayId getId() const override; bool isSecure() const override; bool isVirtual() const override; void disconnect() override; @@ -85,12 +85,14 @@ public: std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const; // Testing - void setDisplayIdForTesting(std::optional<DisplayId> displayId); + void setDisplayIdForTesting(DisplayId displayId); private: bool mIsVirtual = false; - std::optional<DisplayId> mId; + bool mIsDisconnected = false; + DisplayId mId; Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; + DisplayIdGenerator<GpuVirtualDisplayId>* mGpuVirtualDisplayIdGenerator; }; // This template factory function standardizes the implementation details of the diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 6f25e6391b..651230c475 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -38,11 +38,11 @@ public: bool isValid() const override; std::optional<DisplayId> getDisplayId() const override; void setCompositionEnabled(bool) override; - void setProjection(const ui::Transform&, uint32_t orientation, const Rect& frame, - const Rect& viewport, const Rect& sourceClip, const Rect& destinationClip, - bool needsFiltering) override; - void setBounds(const ui::Size&) override; + void setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, + const Rect& orientedDisplaySpaceRect) override; + void setDisplaySize(const ui::Size&) override; void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override; + ui::Transform::RotationFlags getTransformHint() const override; void setColorTransform(const compositionengine::CompositionRefreshArgs&) override; void setColorProfile(const ColorProfile&) override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index 66ed2b6d25..06e6a6f46d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -29,6 +29,7 @@ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" +#include <compositionengine/ProjectionSpace.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Transform.h> @@ -38,7 +39,7 @@ namespace android { namespace compositionengine::impl { struct OutputCompositionState { - // If false, composition will not per performed for this display + // If false, composition will not be performed for this display bool isEnabled{false}; // If false, this output is not considered secure @@ -50,8 +51,7 @@ struct OutputCompositionState { // If true, the current frame on this output uses device composition bool usesDeviceComposition{false}; - // If true, the client target should be flipped when performing client - // composition + // If true, the client target should be flipped when performing client composition bool flipClientTarget{false}; // If true, the current frame reused the buffer from a previous client composition @@ -63,27 +63,25 @@ struct OutputCompositionState { // The layer stack to display on this display uint32_t layerStackId{~0u}; - // The physical space screen bounds - Rect bounds; + // The common space for all layers in the layer stack. layerStackSpace.content is the Rect + // which gets projected on the display. The orientation of this space is always ROTATION_0. + ProjectionSpace layerStackSpace; - // The logical to physical transformation to use - ui::Transform transform; - - // The physical orientation of the display, expressed as ui::Transform - // orientation flags. - uint32_t orientation{0}; + // Oriented physical display space. It will have the same size as displaySpace oriented to + // match the orientation of layerStackSpace. The orientation of this space is always ROTATION_0. + ProjectionSpace orientedDisplaySpace; - // The logical space user visible bounds - Rect frame; + // The space of the framebuffer. Its bounds match the size of the framebuffer and its + // orientation matches the orientation of the display. Typically the framebuffer space will + // be identical to the physical display space. + ProjectionSpace framebufferSpace; - // The logical space user viewport rectangle - Rect viewport; + // The space of the physical display. It is as big as the currently active display mode. The + // content in this space can be rotated. + ProjectionSpace displaySpace; - // The physical space source clip rectangle - Rect sourceClip; - - // The physical space destination clip rectangle - Rect destinationClip; + // Transformation from layerStackSpace to displaySpace + ui::Transform transform; // If true, RenderEngine filtering should be enabled bool needsFiltering{false}; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h index 3a4c70fd64..08a8b84306 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -32,7 +32,7 @@ public: Display(); virtual ~Display(); - MOCK_CONST_METHOD0(getId, const std::optional<DisplayId>&()); + MOCK_CONST_METHOD0(getId, DisplayId()); MOCK_CONST_METHOD0(isSecure, bool()); MOCK_CONST_METHOD0(isVirtual, bool()); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index 4661c5d616..95db4da5b3 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -36,11 +36,10 @@ public: MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>()); MOCK_METHOD1(setCompositionEnabled, void(bool)); - MOCK_METHOD7(setProjection, - void(const ui::Transform&, uint32_t, const Rect&, const Rect&, const Rect&, - const Rect&, bool)); - MOCK_METHOD1(setBounds, void(const ui::Size&)); + MOCK_METHOD3(setProjection, void(ui::Rotation, const Rect&, const Rect&)); + MOCK_METHOD1(setDisplaySize, void(const ui::Size&)); MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool)); + MOCK_CONST_METHOD0(getTransformHint, ui::Transform::RotationFlags()); MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&)); MOCK_METHOD1(setColorProfile, void(const ColorProfile&)); diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index d201104810..0b0b8d5e9c 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -51,18 +51,26 @@ Display::~Display() = default; void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) { mIsVirtual = !args.physical; - mId = args.physical ? std::make_optional(args.physical->id) : std::nullopt; mPowerAdvisor = args.powerAdvisor; - editState().isSecure = args.isSecure; - + editState().displaySpace.bounds = Rect(args.pixels); setLayerStackFilter(args.layerStackId, - args.physical ? args.physical->type == DisplayConnectionType::Internal - : false); + args.physical && args.physical->type == DisplayConnectionType::Internal); setName(args.name); + mGpuVirtualDisplayIdGenerator = args.gpuVirtualDisplayIdGenerator; - if (!args.physical && args.useHwcVirtualDisplays) { - mId = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat); + if (args.physical) { + mId = args.physical->id; + } else { + std::optional<DisplayId> id; + if (args.useHwcVirtualDisplays) { + id = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat); + } + if (!id) { + id = mGpuVirtualDisplayIdGenerator->nextId(); + } + LOG_ALWAYS_FATAL_IF(!id, "Failed to generate display ID"); + mId = *id; } } @@ -77,7 +85,7 @@ bool Display::isValid() const { return Output::isValid() && mPowerAdvisor; } -const std::optional<DisplayId>& Display::getId() const { +DisplayId Display::getId() const { return mId; } @@ -93,31 +101,36 @@ std::optional<DisplayId> Display::getDisplayId() const { return mId; } -void Display::setDisplayIdForTesting(std::optional<DisplayId> displayId) { +void Display::setDisplayIdForTesting(DisplayId displayId) { mId = displayId; } void Display::disconnect() { - if (!mId) { + if (mIsDisconnected) { return; } - auto& hwc = getCompositionEngine().getHwComposer(); - hwc.disconnectDisplay(*mId); - mId.reset(); + mIsDisconnected = true; + if (const auto id = GpuVirtualDisplayId::tryCast(mId)) { + mGpuVirtualDisplayIdGenerator->markUnused(*id); + return; + } + const auto halDisplayId = HalDisplayId::tryCast(mId); + LOG_FATAL_IF(!halDisplayId); + getCompositionEngine().getHwComposer().disconnectDisplay(*halDisplayId); } void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) { Output::setColorTransform(args); - - if (!mId || CC_LIKELY(!args.colorTransformMatrix)) { + const auto halDisplayId = HalDisplayId::tryCast(mId); + if (mIsDisconnected || !halDisplayId || CC_LIKELY(!args.colorTransformMatrix)) { return; } auto& hwc = getCompositionEngine().getHwComposer(); - status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix); + status_t result = hwc.setColorTransform(*halDisplayId, *args.colorTransformMatrix); ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d", - mId ? to_string(*mId).c_str() : "", result); + to_string(mId).c_str(), result); } void Display::setColorProfile(const ColorProfile& colorProfile) { @@ -139,8 +152,10 @@ void Display::setColorProfile(const ColorProfile& colorProfile) { Output::setColorProfile(colorProfile); - auto& hwc = getCompositionEngine().getHwComposer(); - hwc.setActiveColorMode(*mId, colorProfile.mode, colorProfile.renderIntent); + const auto physicalId = PhysicalDisplayId::tryCast(mId); + LOG_FATAL_IF(!physicalId); + getCompositionEngine().getHwComposer().setActiveColorMode(*physicalId, colorProfile.mode, + colorProfile.renderIntent); } void Display::dump(std::string& out) const { @@ -149,14 +164,8 @@ void Display::dump(std::string& out) const { StringAppendF(&out, " Composition Display State: [\"%s\"]", getName().c_str()); out.append("\n "); - dumpVal(out, "isVirtual", mIsVirtual); - if (mId) { - dumpVal(out, "hwcId", to_string(*mId)); - } else { - StringAppendF(&out, "no hwcId, "); - } - + dumpVal(out, "DisplayId", to_string(mId)); out.append("\n"); Output::dumpBase(out); @@ -177,31 +186,33 @@ void Display::createClientCompositionCache(uint32_t cacheSize) { std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer( const sp<compositionengine::LayerFE>& layerFE) const { - auto result = impl::createOutputLayer(*this, layerFE); + auto outputLayer = impl::createOutputLayer(*this, layerFE); - if (result && mId) { + if (const auto halDisplayId = HalDisplayId::tryCast(mId); + outputLayer && !mIsDisconnected && halDisplayId) { auto& hwc = getCompositionEngine().getHwComposer(); - auto displayId = *mId; // Note: For the moment we ensure it is safe to take a reference to the // HWComposer implementation by destroying all the OutputLayers (and // hence the HWC2::Layers they own) before setting a new HWComposer. See // for example SurfaceFlinger::updateVrFlinger(). // TODO(b/121291683): Make this safer. - auto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId), - [&hwc, displayId](HWC2::Layer* layer) { - hwc.destroyLayer(displayId, layer); - }); + auto hwcLayer = + std::shared_ptr<HWC2::Layer>(hwc.createLayer(*halDisplayId), + [&hwc, id = *halDisplayId](HWC2::Layer* layer) { + hwc.destroyLayer(id, layer); + }); ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s", getName().c_str()); - result->setHwcLayer(std::move(hwcLayer)); + outputLayer->setHwcLayer(std::move(hwcLayer)); } - return result; + return outputLayer; } void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) { Output::setReleasedLayers(refreshArgs); - if (!mId || refreshArgs.layersWithQueuedFrames.empty()) { + if (mIsDisconnected || GpuVirtualDisplayId::tryCast(mId) || + refreshArgs.layersWithQueuedFrames.empty()) { return; } @@ -237,19 +248,25 @@ void Display::chooseCompositionStrategy() { ATRACE_CALL(); ALOGV(__FUNCTION__); + if (mIsDisconnected) { + return; + } + // Default to the base settings -- client composition only. Output::chooseCompositionStrategy(); - // If we don't have a HWC display, then we are done - if (!mId) { + // If we don't have a HWC display, then we are done. + const auto halDisplayId = HalDisplayId::tryCast(mId); + if (!halDisplayId) { return; } // Get any composition changes requested by the HWC device, and apply them. std::optional<android::HWComposer::DeviceRequestedChanges> changes; auto& hwc = getCompositionEngine().getHwComposer(); - if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(), - &changes); + if (status_t result = + hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(), + &changes); result != NO_ERROR) { ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result, strerror(-result)); @@ -270,8 +287,12 @@ void Display::chooseCompositionStrategy() { bool Display::getSkipColorTransform() const { const auto& hwc = getCompositionEngine().getHwComposer(); - return mId ? hwc.hasDisplayCapability(*mId, hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM) - : hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM); + if (const auto halDisplayId = HalDisplayId::tryCast(mId)) { + return hwc.hasDisplayCapability(*halDisplayId, + hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); + } + + return hwc.hasCapability(hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM); } bool Display::anyLayersRequireClientComposition() const { @@ -338,16 +359,17 @@ void Display::applyClientTargetRequests(const ClientTargetProperty& clientTarget } compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { - auto result = impl::Output::presentAndGetFrameFences(); + auto fences = impl::Output::presentAndGetFrameFences(); - if (!mId) { - return result; + const auto halDisplayIdOpt = HalDisplayId::tryCast(mId); + if (mIsDisconnected || !halDisplayIdOpt) { + return fences; } auto& hwc = getCompositionEngine().getHwComposer(); - hwc.presentAndGetReleaseFences(*mId); + hwc.presentAndGetReleaseFences(*halDisplayIdOpt); - result.presentFence = hwc.getPresentFence(*mId); + fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt); // TODO(b/121291683): Change HWComposer call to return entire map for (const auto* layer : getOutputLayersOrderedByZ()) { @@ -356,19 +378,19 @@ compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { continue; } - result.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*mId, hwcLayer)); + fences.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*halDisplayIdOpt, hwcLayer)); } - hwc.clearReleaseFences(*mId); + hwc.clearReleaseFences(*halDisplayIdOpt); - return result; + return fences; } void Display::setExpensiveRenderingExpected(bool enabled) { Output::setExpensiveRenderingExpected(enabled); - if (mPowerAdvisor && mId) { - mPowerAdvisor->setExpensiveRenderingExpected(*mId, enabled); + if (mPowerAdvisor && !GpuVirtualDisplayId::tryCast(mId)) { + mPowerAdvisor->setExpensiveRenderingExpected(mId, enabled); } } @@ -377,11 +399,10 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre // 1) It is being handled by hardware composer, which may need this to // keep its virtual display state machine in sync, or // 2) There is work to be done (the dirty region isn't empty) - if (!mId) { - if (getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) { - ALOGV("Skipping display composition"); - return; - } + if (GpuVirtualDisplayId::tryCast(mId) && + getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) { + ALOGV("Skipping display composition"); + return; } impl::Output::finishFrame(refreshArgs); diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp index 959843050c..9d1bb02e7c 100644 --- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp @@ -77,6 +77,7 @@ void dumpVal(std::string& out, const char* name, const Region& region) { void dumpVal(std::string& out, const char* name, const ui::Transform& transform) { transform.dump(out, name); + out.append(" "); } void dumpVal(std::string& out, const char* name, const ui::Size& size) { diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp index 02e3a45acd..1338538861 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp @@ -75,10 +75,6 @@ void LayerFECompositionState::dump(std::string& out) const { dumpVal(out, "alpha", alpha); dumpVal(out, "backgroundBlurRadius", backgroundBlurRadius); - out.append("\n "); - dumpVal(out, "type", type); - dumpVal(out, "appId", appId); - if (!metadata.empty()) { out.append("\n metadata {"); for (const auto& [key, entry] : metadata) { diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 34dc53648c..3852f45e35 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -69,6 +69,19 @@ Reversed<T> reversed(const T& c) { return Reversed<T>(c); } +struct ScaleVector { + float x; + float y; +}; + +// Returns a ScaleVector (x, y) such that from.scale(x, y) = to', +// where to' will have the same size as "to". In the case where "from" and "to" +// start at the origin to'=to. +ScaleVector getScale(const Rect& from, const Rect& to) { + return {.x = static_cast<float>(to.width()) / from.width(), + .y = static_cast<float>(to.height()) / from.height()}; +} + } // namespace std::shared_ptr<Output> createOutput( @@ -105,30 +118,84 @@ void Output::setCompositionEnabled(bool enabled) { dirtyEntireOutput(); } -void Output::setProjection(const ui::Transform& transform, uint32_t orientation, const Rect& frame, - const Rect& viewport, const Rect& sourceClip, - const Rect& destinationClip, bool needsFiltering) { +void Output::setProjection(ui::Rotation orientation, const Rect& layerStackSpaceRect, + const Rect& orientedDisplaySpaceRect) { auto& outputState = editState(); - outputState.transform = transform; - outputState.orientation = orientation; - outputState.sourceClip = sourceClip; - outputState.destinationClip = destinationClip; - outputState.frame = frame; - outputState.viewport = viewport; - outputState.needsFiltering = needsFiltering; + outputState.displaySpace.orientation = orientation; + LOG_FATAL_IF(outputState.displaySpace.bounds == Rect::INVALID_RECT, + "The display bounds are unknown."); + + // Compute orientedDisplaySpace + ui::Size orientedSize = outputState.displaySpace.bounds.getSize(); + if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) { + std::swap(orientedSize.width, orientedSize.height); + } + outputState.orientedDisplaySpace.bounds = Rect(orientedSize); + outputState.orientedDisplaySpace.content = orientedDisplaySpaceRect; + + // Compute displaySpace.content + const uint32_t transformOrientationFlags = ui::Transform::toRotationFlags(orientation); + ui::Transform rotation; + if (transformOrientationFlags != ui::Transform::ROT_INVALID) { + const auto displaySize = outputState.displaySpace.bounds; + rotation.set(transformOrientationFlags, displaySize.width(), displaySize.height()); + } + outputState.displaySpace.content = rotation.transform(orientedDisplaySpaceRect); + + // Compute framebufferSpace + outputState.framebufferSpace.orientation = orientation; + LOG_FATAL_IF(outputState.framebufferSpace.bounds == Rect::INVALID_RECT, + "The framebuffer bounds are unknown."); + const auto scale = + getScale(outputState.displaySpace.bounds, outputState.framebufferSpace.bounds); + outputState.framebufferSpace.content = outputState.displaySpace.content.scale(scale.x, scale.y); + + // Compute layerStackSpace + outputState.layerStackSpace.content = layerStackSpaceRect; + outputState.layerStackSpace.bounds = layerStackSpaceRect; + + outputState.transform = outputState.layerStackSpace.getTransform(outputState.displaySpace); + outputState.needsFiltering = outputState.transform.needsBilinearFiltering(); dirtyEntireOutput(); } -// TODO(b/121291683): Rename setSize() once more is moved. -void Output::setBounds(const ui::Size& size) { +void Output::setDisplaySize(const ui::Size& size) { mRenderSurface->setDisplaySize(size); - // TODO(b/121291683): Rename outputState.size once more is moved. - editState().bounds = Rect(mRenderSurface->getSize()); + + auto& state = editState(); + + // Update framebuffer space + const Rect newBounds(size); + ScaleVector scale; + scale = getScale(state.framebufferSpace.bounds, newBounds); + state.framebufferSpace.bounds = newBounds; + state.framebufferSpace.content.scaleSelf(scale.x, scale.y); + + // Update display space + scale = getScale(state.displaySpace.bounds, newBounds); + state.displaySpace.bounds = newBounds; + state.displaySpace.content.scaleSelf(scale.x, scale.y); + state.transform = state.layerStackSpace.getTransform(state.displaySpace); + + // Update oriented display space + const auto orientation = state.displaySpace.orientation; + ui::Size orientedSize = size; + if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) { + std::swap(orientedSize.width, orientedSize.height); + } + const Rect newOrientedBounds(orientedSize); + scale = getScale(state.orientedDisplaySpace.bounds, newOrientedBounds); + state.orientedDisplaySpace.bounds = newOrientedBounds; + state.orientedDisplaySpace.content.scaleSelf(scale.x, scale.y); dirtyEntireOutput(); } +ui::Transform::RotationFlags Output::getTransformHint() const { + return static_cast<ui::Transform::RotationFlags>(getState().transform.getOrientation()); +} + void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) { auto& outputState = editState(); outputState.layerStackId = layerStackId; @@ -232,8 +299,7 @@ compositionengine::RenderSurface* Output::getRenderSurface() const { void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) { mRenderSurface = std::move(surface); - editState().bounds = Rect(mRenderSurface->getSize()); - + editState().framebufferSpace.bounds = Rect(mRenderSurface->getSize()); dirtyEntireOutput(); } @@ -251,7 +317,7 @@ void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSu Region Output::getDirtyRegion(bool repaintEverything) const { const auto& outputState = getState(); - Region dirty(outputState.viewport); + Region dirty(outputState.layerStackSpace.content); if (!repaintEverything) { dirty.andSelf(outputState.dirtyRegion); } @@ -336,7 +402,7 @@ void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& // Compute the resulting coverage for this output, and store it for later const ui::Transform& tr = outputState.transform; - Region undefinedRegion{outputState.bounds}; + Region undefinedRegion{outputState.displaySpace.bounds}; undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers)); outputState.undefinedRegion = undefinedRegion; @@ -539,7 +605,7 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below) const auto& outputState = getState(); Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion)); - drawRegion.andSelf(outputState.bounds); + drawRegion.andSelf(outputState.displaySpace.bounds); if (drawRegion.isEmpty()) { return; } @@ -556,8 +622,8 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, outputLayerState.visibleRegion = visibleRegion; outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion; outputLayerState.coveredRegion = coveredRegion; - outputLayerState.outputSpaceVisibleRegion = - outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport)); + outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform( + visibleNonShadowRegion.intersect(outputState.layerStackSpace.content)); outputLayerState.shadowRegion = shadowRegion; } @@ -603,7 +669,8 @@ void Output::updateAndWriteCompositionState( compositionengine::OutputLayer* Output::findLayerRequestingBackgroundComposition() const { compositionengine::OutputLayer* layerRequestingBgComposition = nullptr; for (auto* layer : getOutputLayersOrderedByZ()) { - if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0) { + if (layer->getLayerFE().getCompositionState()->backgroundBlurRadius > 0 || + layer->getLayerFE().getCompositionState()->blurRegions.size() > 0) { layerRequestingBgComposition = layer; } } @@ -835,6 +902,8 @@ std::optional<base::unique_fd> Output::composeSurfaces( needsProtected == renderEngine.isProtected()) { mRenderSurface->setProtected(needsProtected); } + } else if (!outputState.isSecure && renderEngine.isProtected()) { + renderEngine.useProtectedContext(false); } base::unique_fd fd; @@ -862,9 +931,10 @@ std::optional<base::unique_fd> Output::composeSurfaces( ALOGV("hasClientComposition"); renderengine::DisplaySettings clientCompositionDisplay; - clientCompositionDisplay.physicalDisplay = outputState.destinationClip; - clientCompositionDisplay.clip = outputState.sourceClip; - clientCompositionDisplay.orientation = outputState.orientation; + clientCompositionDisplay.physicalDisplay = outputState.framebufferSpace.content; + clientCompositionDisplay.clip = outputState.layerStackSpace.content; + clientCompositionDisplay.orientation = + ui::Transform::toRotationFlags(outputState.displaySpace.orientation); clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut() ? outputState.dataspace : ui::Dataspace::UNKNOWN; @@ -921,9 +991,8 @@ std::optional<base::unique_fd> Output::composeSurfaces( const nsecs_t renderEngineStart = systemTime(); status_t status = - renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, - buf->getNativeBuffer(), /*useFramebufferCache=*/true, - std::move(fd), &readyFence); + renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buf, + /*useFramebufferCache=*/true, std::move(fd), &readyFence); if (status != NO_ERROR && mClientCompositionRequestCache) { // If rendering was not successful, remove the request from the cache. @@ -948,8 +1017,7 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( ALOGV("Rendering client layers"); const auto& outputState = getState(); - const Region viewportRegion(outputState.viewport); - const bool useIdentityTransform = false; + const Region viewportRegion(outputState.layerStackSpace.content); bool firstLayer = true; // Used when a layer clears part of the buffer. Region stubRegion; @@ -985,18 +1053,17 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( !layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty(); if (clientComposition || clearClientComposition) { - compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{ - clip, - useIdentityTransform, - layer->needsFiltering() || outputState.needsFiltering, - outputState.isSecure, - supportsProtectedContent, - clientComposition ? clearRegion : stubRegion, - outputState.viewport, - outputDataspace, - realContentIsVisible, - !clientComposition, /* clearContent */ - }; + compositionengine::LayerFE::ClientCompositionTargetSettings + targetSettings{.clip = clip, + .needsFiltering = + layer->needsFiltering() || outputState.needsFiltering, + .isSecure = outputState.isSecure, + .supportsProtectedContent = supportsProtectedContent, + .clearRegion = clientComposition ? clearRegion : stubRegion, + .viewport = outputState.layerStackSpace.content, + .dataspace = outputDataspace, + .realContentIsVisible = realContentIsVisible, + .clearContent = !clientComposition}; std::vector<LayerFE::LayerSettings> results = layerFE.prepareClientCompositionList(targetSettings); if (realContentIsVisible && !results.empty()) { @@ -1093,7 +1160,7 @@ void Output::postFramebuffer() { void Output::dirtyEntireOutput() { auto& outputState = editState(); - outputState.dirtyRegion.set(outputState.bounds); + outputState.dirtyRegion.set(outputState.displaySpace.bounds); } void Output::chooseCompositionStrategy() { diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 4835aefa8f..ee30ad8583 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -37,12 +37,14 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "transform", transform); out.append("\n "); - - dumpVal(out, "bounds", bounds); - dumpVal(out, "frame", frame); - dumpVal(out, "viewport", viewport); - dumpVal(out, "sourceClip", sourceClip); - dumpVal(out, "destinationClip", destinationClip); + dumpVal(out, "layerStackSpace", to_string(layerStackSpace)); + out.append("\n "); + dumpVal(out, "framebufferSpace", to_string(framebufferSpace)); + out.append("\n "); + dumpVal(out, "orientedDisplaySpace", to_string(orientedDisplaySpace)); + out.append("\n "); + dumpVal(out, "displaySpace", to_string(displaySpace)); + out.append("\n "); dumpVal(out, "needsFiltering", needsFiltering); out.append("\n "); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 1faf775ed3..0faab6fbf4 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -77,7 +77,7 @@ Rect OutputLayer::calculateInitialCrop() const { FloatRect activeCropFloat = reduce(layerState.geomLayerBounds, layerState.transparentRegionHint); - const Rect& viewport = getOutput().getState().viewport; + const Rect& viewport = getOutput().getState().layerStackSpace.content; const ui::Transform& layerTransform = layerState.geomLayerTransform; const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; // Transform to screen space. @@ -133,7 +133,8 @@ FloatRect OutputLayer::calculateOutputSourceCrop() const { * the code below applies the primary display's inverse transform to the * buffer */ - uint32_t invTransformOrient = outputState.orientation; + uint32_t invTransformOrient = + ui::Transform::toRotationFlags(outputState.displaySpace.orientation); // calculate the inverse transform if (invTransformOrient & HAL_TRANSFORM_ROT_90) { invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; @@ -189,7 +190,7 @@ Rect OutputLayer::calculateOutputDisplayFrame() const { Rect activeCrop = layerState.geomCrop; if (!activeCrop.isEmpty() && bufferSize.isValid()) { activeCrop = layerTransform.transform(activeCrop); - if (!activeCrop.intersect(outputState.viewport, &activeCrop)) { + if (!activeCrop.intersect(outputState.layerStackSpace.content, &activeCrop)) { activeCrop.clear(); } activeCrop = inverseLayerTransform.transform(activeCrop, true); @@ -215,7 +216,7 @@ Rect OutputLayer::calculateOutputDisplayFrame() const { // transformation. We then round upon constructing 'frame'. Rect frame{ layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))}; - if (!frame.intersect(outputState.viewport, &frame)) { + if (!frame.intersect(outputState.layerStackSpace.content, &frame)) { frame.clear(); } const ui::Transform displayTransform{outputState.transform}; @@ -402,13 +403,6 @@ void OutputLayer::writeOutputIndependentGeometryStateToHWC( outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error)); } - if (auto error = hwcLayer->setInfo(static_cast<uint32_t>(outputIndependentState.type), - static_cast<uint32_t>(outputIndependentState.appId)); - error != hal::Error::NONE) { - ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(), - to_string(error).c_str(), static_cast<int32_t>(error)); - } - for (const auto& [name, entry] : outputIndependentState.metadata) { if (auto error = hwcLayer->setLayerGenericMetadata(name, entry.mandatory, entry.value); error != hal::Error::NONE) { @@ -568,7 +562,7 @@ void OutputLayer::writeCursorPositionToHWC() const { const auto& outputState = getOutput().getState(); Rect frame = layerFEState->cursorFrame; - frame.intersect(outputState.viewport, &frame); + frame.intersect(outputState.layerStackSpace.content, &frame); Rect position = outputState.transform.transform(frame); if (auto error = hwcLayer->setCursorPosition(position.left, position.top); diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp index 2773fd3a16..b47f7fd8a9 100644 --- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp @@ -48,6 +48,8 @@ RenderSurface::~RenderSurface() = default; namespace impl { +constexpr auto DEFAULT_USAGE = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; + std::unique_ptr<compositionengine::RenderSurface> createRenderSurface( const compositionengine::CompositionEngine& compositionEngine, compositionengine::Display& display, @@ -81,7 +83,7 @@ void RenderSurface::initialize() { ALOGE_IF(status != NO_ERROR, "Unable to connect BQ producer: %d", status); status = native_window_set_buffers_format(window, HAL_PIXEL_FORMAT_RGBA_8888); ALOGE_IF(status != NO_ERROR, "Unable to set BQ format to RGBA888: %d", status); - status = native_window_set_usage(window, GRALLOC_USAGE_HW_RENDER); + status = native_window_set_usage(window, DEFAULT_USAGE); ALOGE_IF(status != NO_ERROR, "Unable to set BQ usage bits for GPU rendering: %d", status); } @@ -109,7 +111,7 @@ void RenderSurface::setBufferPixelFormat(ui::PixelFormat pixelFormat) { } void RenderSurface::setProtected(bool useProtected) { - uint64_t usageFlags = GRALLOC_USAGE_HW_RENDER; + uint64_t usageFlags = DEFAULT_USAGE; if (useProtected) { usageFlags |= GRALLOC_USAGE_PROTECTED; } diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 09f37fba18..1befbf8306 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -56,8 +56,9 @@ using testing::Sequence; using testing::SetArgPointee; using testing::StrictMock; -constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42}; -constexpr DisplayId VIRTUAL_DISPLAY_ID = DisplayId{43}; +constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId{42}; +// TODO(b/160679868) Use VirtualDisplayId +constexpr PhysicalDisplayId VIRTUAL_DISPLAY_ID = PhysicalDisplayId{43}; constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920; constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080; constexpr int32_t DEFAULT_LAYER_STACK = 123; @@ -159,6 +160,7 @@ struct DisplayTestCommon : public testing::Test { EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); } DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() { @@ -175,6 +177,7 @@ struct DisplayTestCommon : public testing::Test { DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() { return DisplayCreationArgsBuilder() .setUseHwcVirtualDisplays(false) + .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator) .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888)) .setIsSecure(false) @@ -188,6 +191,7 @@ struct DisplayTestCommon : public testing::Test { StrictMock<renderengine::mock::RenderEngine> mRenderEngine; StrictMock<mock::CompositionEngine> mCompositionEngine; sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>(); + RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuDisplayIdGenerator; }; struct PartialMockDisplayTestCommon : public DisplayTestCommon { @@ -244,7 +248,7 @@ TEST_F(DisplayCreationTest, createNonHwcVirtualDisplay) { getDisplayCreationArgsForNonHWCVirtualDisplay()); EXPECT_FALSE(display->isSecure()); EXPECT_TRUE(display->isVirtual()); - EXPECT_EQ(std::nullopt, display->getId()); + EXPECT_TRUE(GpuVirtualDisplayId::tryCast(display->getId())); } /* @@ -331,6 +335,7 @@ TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfHwcAll mDisplay->setConfiguration( DisplayCreationArgsBuilder() .setUseHwcVirtualDisplays(true) + .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator) .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH)) .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888)) .setIsSecure(false) @@ -339,7 +344,7 @@ TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfHwcAll .setName(getDisplayNameFromCurrentTest()) .build()); - EXPECT_EQ(std::nullopt, mDisplay->getId()); + EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId())); EXPECT_FALSE(mDisplay->isSecure()); EXPECT_TRUE(mDisplay->isVirtual()); EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId); @@ -351,6 +356,7 @@ TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfShould mDisplay->setConfiguration( DisplayCreationArgsBuilder() .setUseHwcVirtualDisplays(false) + .setGpuVirtualDisplayIdGenerator(mGpuDisplayIdGenerator) .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH)) .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888)) .setIsSecure(false) @@ -359,7 +365,7 @@ TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfShould .setName(getDisplayNameFromCurrentTest()) .build()); - EXPECT_EQ(std::nullopt, mDisplay->getId()); + EXPECT_TRUE(GpuVirtualDisplayId::tryCast(mDisplay->getId())); EXPECT_FALSE(mDisplay->isSecure()); EXPECT_TRUE(mDisplay->isVirtual()); EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId); @@ -374,16 +380,13 @@ TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfShould using DisplayDisconnectTest = PartialMockDisplayTestCommon; TEST_F(DisplayDisconnectTest, disconnectsDisplay) { - // The first call to disconnect will disconnect the display with the HWC and - // set mHwcId to -1. - EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1); + // The first call to disconnect will disconnect the display with the HWC. + EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1); mDisplay->disconnect(); - EXPECT_FALSE(mDisplay->getId()); // Subsequent calls will do nothing, - EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0); + EXPECT_CALL(mHwComposer, disconnectDisplay(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(0); mDisplay->disconnect(); - EXPECT_FALSE(mDisplay->getId()); } /* @@ -401,7 +404,8 @@ TEST_F(DisplaySetColorTransformTest, setsTransform) { // Identity matrix sets an identity state value const mat4 kIdentity; - EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1); + EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kIdentity)) + .Times(1); refreshArgs.colorTransformMatrix = kIdentity; mDisplay->setColorTransform(refreshArgs); @@ -409,7 +413,8 @@ TEST_F(DisplaySetColorTransformTest, setsTransform) { // Non-identity matrix sets a non-identity state value const mat4 kNonIdentity = mat4() * 2; - EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1); + EXPECT_CALL(mHwComposer, setColorTransform(HalDisplayId(DEFAULT_DISPLAY_ID), kNonIdentity)) + .Times(1); refreshArgs.colorTransformMatrix = kNonIdentity; mDisplay->setColorTransform(refreshArgs); @@ -526,13 +531,14 @@ TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) { sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>(); StrictMock<HWC2::mock::Layer> hwcLayer; - EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer)); + EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID))) + .WillOnce(Return(&hwcLayer)); auto outputLayer = mDisplay->createOutputLayer(layerFE); EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer()); - EXPECT_CALL(mHwComposer, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer)); + EXPECT_CALL(mHwComposer, destroyLayer(HalDisplayId(DEFAULT_DISPLAY_ID), &hwcLayer)); outputLayer.reset(); } @@ -605,7 +611,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) { auto args = getDisplayCreationArgsForNonHWCVirtualDisplay(); std::shared_ptr<Display> nonHwcDisplay = createPartialMockDisplay<Display>(mCompositionEngine, args); - EXPECT_FALSE(nonHwcDisplay->getId()); + EXPECT_TRUE(GpuVirtualDisplayId::tryCast(nonHwcDisplay->getId())); nonHwcDisplay->chooseCompositionStrategy(); @@ -616,7 +622,8 @@ TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) { TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) { EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _)) + EXPECT_CALL(mHwComposer, + getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _)) .WillOnce(Return(INVALID_OPERATION)); mDisplay->chooseCompositionStrategy(); @@ -638,7 +645,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) { .InSequence(s) .WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _)) + EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _)) .WillOnce(Return(NO_ERROR)); EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); @@ -668,7 +675,7 @@ TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) { .InSequence(s) .WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _)) + EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _)) .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR))); EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1); EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1); @@ -698,7 +705,7 @@ TEST_F(DisplayGetSkipColorTransformTest, checksCapabilityIfNonHwcDisplay) { TEST_F(DisplayGetSkipColorTransformTest, checksDisplayCapability) { EXPECT_CALL(mHwComposer, - hasDisplayCapability(DEFAULT_DISPLAY_ID, + hasDisplayCapability(HalDisplayId(DEFAULT_DISPLAY_ID), hal::DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM)) .WillOnce(Return(true)); EXPECT_TRUE(mDisplay->getSkipColorTransform()); @@ -856,13 +863,16 @@ TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) { sp<Fence> layer1Fence = new Fence(); sp<Fence> layer2Fence = new Fence(); - EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1); - EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence)); - EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer1.hwc2Layer)) + EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1); + EXPECT_CALL(mHwComposer, getPresentFence(HalDisplayId(DEFAULT_DISPLAY_ID))) + .WillOnce(Return(presentFence)); + EXPECT_CALL(mHwComposer, + getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer1.hwc2Layer)) .WillOnce(Return(layer1Fence)); - EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mLayer2.hwc2Layer)) + EXPECT_CALL(mHwComposer, + getLayerReleaseFence(HalDisplayId(DEFAULT_DISPLAY_ID), &mLayer2.hwc2Layer)) .WillOnce(Return(layer2Fence)); - EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1); + EXPECT_CALL(mHwComposer, clearReleaseFences(HalDisplayId(DEFAULT_DISPLAY_ID))).Times(1); auto result = mDisplay->presentAndGetFrameFences(); @@ -907,7 +917,7 @@ TEST_F(DisplayFinishFrameTest, doesNotSkipCompositionIfNotDirtyOnHwcDisplay) { mDisplay->editState().isEnabled = true; mDisplay->editState().usesClientComposition = false; - mDisplay->editState().viewport = Rect(0, 0, 1, 1); + mDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1); mDisplay->editState().dirtyRegion = Region::INVALID_REGION; CompositionRefreshArgs refreshArgs; @@ -928,7 +938,7 @@ TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) { nonHwcDisplay->editState().isEnabled = true; nonHwcDisplay->editState().usesClientComposition = false; - nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1); + nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1); nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION; CompositionRefreshArgs refreshArgs; @@ -949,7 +959,7 @@ TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) { nonHwcDisplay->editState().isEnabled = true; nonHwcDisplay->editState().usesClientComposition = false; - nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1); + nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1); nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1)); CompositionRefreshArgs refreshArgs; @@ -970,7 +980,7 @@ TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) { nonHwcDisplay->editState().isEnabled = true; nonHwcDisplay->editState().usesClientComposition = false; - nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1); + nonHwcDisplay->editState().layerStackSpace.content = Rect(0, 0, 1, 1); nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION; CompositionRefreshArgs refreshArgs; diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h index d21b97e05e..87911ccacf 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h @@ -66,7 +66,6 @@ public: MOCK_METHOD1(setTransform, Error(hal::Transform)); MOCK_METHOD1(setVisibleRegion, Error(const android::Region&)); MOCK_METHOD1(setZOrder, Error(uint32_t)); - MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t)); MOCK_METHOD1(setColorTransform, Error(const android::mat4&)); MOCK_METHOD3(setLayerGenericMetadata, diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 75a4fec8fb..84c027bfd3 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -42,63 +42,69 @@ public: MOCK_CONST_METHOD3(getDisplayIdentificationData, bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*)); MOCK_CONST_METHOD1(hasCapability, bool(hal::Capability)); - MOCK_CONST_METHOD2(hasDisplayCapability, bool(DisplayId, hal::DisplayCapability)); + MOCK_CONST_METHOD2(hasDisplayCapability, bool(HalDisplayId, hal::DisplayCapability)); MOCK_METHOD3(allocateVirtualDisplay, std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*)); - MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, DisplayId)); - MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId)); - MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*)); + MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId)); + MOCK_METHOD1(createLayer, HWC2::Layer*(HalDisplayId)); + MOCK_METHOD2(destroyLayer, void(HalDisplayId, HWC2::Layer*)); MOCK_METHOD3(getDeviceCompositionChanges, - status_t(DisplayId, bool, + status_t(HalDisplayId, bool, std::optional<android::HWComposer::DeviceRequestedChanges>*)); MOCK_METHOD5(setClientTarget, - status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, + status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace)); - MOCK_METHOD1(presentAndGetReleaseFences, status_t(DisplayId)); - MOCK_METHOD2(setPowerMode, status_t(DisplayId, hal::PowerMode)); - MOCK_METHOD2(setActiveConfig, status_t(DisplayId, size_t)); - MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&)); - MOCK_METHOD1(disconnectDisplay, void(DisplayId)); + MOCK_METHOD1(presentAndGetReleaseFences, status_t(HalDisplayId)); + MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode)); + MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t)); + MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&)); + MOCK_METHOD1(disconnectDisplay, void(HalDisplayId)); MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&)); - MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId)); - MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*)); - MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&)); - MOCK_METHOD1(clearReleaseFences, void(DisplayId)); - MOCK_METHOD2(getHdrCapabilities, status_t(DisplayId, HdrCapabilities*)); - MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(DisplayId)); - MOCK_CONST_METHOD2(getRenderIntents, std::vector<ui::RenderIntent>(DisplayId, ui::ColorMode)); - MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(DisplayId, ui::Dataspace)); + MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId)); + MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*)); + MOCK_METHOD3(setOutputBuffer, + status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&)); + MOCK_METHOD1(clearReleaseFences, void(HalDisplayId)); + MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*)); + MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId)); + MOCK_CONST_METHOD2(getRenderIntents, + std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode)); + MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace)); MOCK_METHOD4(getDisplayedContentSamplingAttributes, - status_t(DisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*)); - MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t)); + status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*)); + MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t)); MOCK_METHOD4(getDisplayedContentSample, - status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*)); - MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(DisplayId, float)); - MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*)); + status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*)); + MOCK_METHOD2(setDisplayBrightness, std::future<status_t>(PhysicalDisplayId, float)); + MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*)); MOCK_METHOD2(onHotplug, std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection)); + MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool()); MOCK_METHOD2(onVsync, bool(hal::HWDisplayId, int64_t)); - MOCK_METHOD2(setVsyncEnabled, void(DisplayId, hal::Vsync)); - MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(DisplayId)); - MOCK_CONST_METHOD1(isConnected, bool(DisplayId)); - MOCK_CONST_METHOD1(getConfigs, - std::vector<std::shared_ptr<const HWC2::Display::Config>>(DisplayId)); - MOCK_CONST_METHOD1(getActiveConfig, std::shared_ptr<const HWC2::Display::Config>(DisplayId)); - MOCK_CONST_METHOD1(getActiveConfigIndex, int(DisplayId)); - MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId)); - MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent)); + MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync)); + MOCK_CONST_METHOD1(getRefreshTimestamp, nsecs_t(PhysicalDisplayId)); + MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId)); + MOCK_CONST_METHOD1( + getConfigs, + std::vector<std::shared_ptr<const HWC2::Display::Config>>(PhysicalDisplayId)); + MOCK_CONST_METHOD1(getActiveConfig, + std::shared_ptr<const HWC2::Display::Config>(PhysicalDisplayId)); + MOCK_CONST_METHOD1(getActiveConfigIndex, int(PhysicalDisplayId)); + 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(DisplayId)); - MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId)); - MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId)); + MOCK_CONST_METHOD1(getDisplayConnectionType, DisplayConnectionType(PhysicalDisplayId)); + MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId)); + MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(PhysicalDisplayId)); MOCK_METHOD4(setActiveConfigWithConstraints, - status_t(DisplayId, size_t, const hal::VsyncPeriodChangeConstraints&, + status_t(PhysicalDisplayId, size_t, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline*)); - MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool)); - MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<hal::ContentType>*)); - MOCK_METHOD2(setContentType, status_t(DisplayId, hal::ContentType)); + MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool)); + MOCK_METHOD2(getSupportedContentTypes, + status_t(PhysicalDisplayId, std::vector<hal::ContentType>*)); + MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType)); MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata, const std::unordered_map<std::string, bool>&()); @@ -107,8 +113,8 @@ public: MOCK_CONST_METHOD1(getHwcDisplayId, std::optional<hal::HWDisplayId>(int32_t)); MOCK_CONST_METHOD0(getInternalHwcDisplayId, std::optional<hal::HWDisplayId>()); MOCK_CONST_METHOD0(getExternalHwcDisplayId, std::optional<hal::HWDisplayId>()); - MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<DisplayId>(hal::HWDisplayId)); - MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(DisplayId)); + MOCK_CONST_METHOD1(toPhysicalDisplayId, std::optional<PhysicalDisplayId>(hal::HWDisplayId)); + MOCK_CONST_METHOD1(fromPhysicalDisplayId, std::optional<hal::HWDisplayId>(PhysicalDisplayId)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 020f93a607..dcfc162922 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -21,6 +21,7 @@ #include <compositionengine/mock/LayerFE.h> #include <compositionengine/mock/Output.h> #include <gtest/gtest.h> +#include <log/log.h> #include "MockHWC2.h" #include "MockHWComposer.h" @@ -55,6 +56,22 @@ MATCHER_P(ColorEq, expected, "") { return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a; } +ui::Rotation toRotation(uint32_t rotationFlag) { + switch (rotationFlag) { + case ui::Transform::RotationFlags::ROT_0: + return ui::ROTATION_0; + case ui::Transform::RotationFlags::ROT_90: + return ui::ROTATION_90; + case ui::Transform::RotationFlags::ROT_180: + return ui::ROTATION_180; + case ui::Transform::RotationFlags::ROT_270: + return ui::ROTATION_270; + default: + LOG_FATAL("Unexpected rotation flag %d", rotationFlag); + return ui::Rotation(-1); + } +} + struct OutputLayerTest : public testing::Test { struct OutputLayer final : public impl::OutputLayer { OutputLayer(const compositionengine::Output& output, sp<compositionengine::LayerFE> layerFE) @@ -138,7 +155,7 @@ struct OutputLayerSourceCropTest : public OutputLayerTest { mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080}; mLayerFEState.geomBufferTransform = TR_IDENT; - mOutputState.viewport = Rect{0, 0, 1920, 1080}; + mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080}; } FloatRect calculateOutputSourceCrop() { @@ -209,7 +226,7 @@ TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformed mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay; mLayerFEState.geomBufferTransform = entry.buffer; - mOutputState.orientation = entry.display; + mOutputState.displaySpace.orientation = toRotation(entry.display); EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i; } @@ -223,7 +240,7 @@ TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) { } TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) { - mOutputState.viewport = Rect{0, 0, 960, 540}; + mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540}; const FloatRect expected{0.f, 0.f, 960.f, 540.f}; EXPECT_THAT(calculateOutputSourceCrop(), expected); @@ -245,7 +262,7 @@ struct OutputLayerDisplayFrameTest : public OutputLayerTest { mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080}; mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; - mOutputState.viewport = Rect{0, 0, 1920, 1080}; + mOutputState.layerStackSpace.content = Rect{0, 0, 1920, 1080}; mOutputState.transform = ui::Transform{TR_IDENT}; } @@ -293,7 +310,7 @@ TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) { } TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) { - mOutputState.viewport = Rect{0, 0, 960, 540}; + mOutputState.layerStackSpace.content = Rect{0, 0, 960, 540}; const Rect expected{0, 0, 960, 540}; EXPECT_THAT(calculateOutputDisplayFrame(), expected); } @@ -358,7 +375,7 @@ TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) { mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080); mLayerFEState.geomBufferTransform = entry.buffer; - mOutputState.orientation = entry.display; + mOutputState.displaySpace.orientation = toRotation(entry.display); mOutputState.transform = ui::Transform{entry.display}; const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.display); @@ -470,7 +487,7 @@ TEST_F(OutputLayerTest, mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080); mLayerFEState.geomBufferTransform = entry.buffer; - mOutputState.orientation = entry.display; + mOutputState.displaySpace.orientation = toRotation(entry.display); mOutputState.transform = ui::Transform{entry.display}; const auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(entry.internal); @@ -676,8 +693,6 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { static constexpr Hwc2::IComposerClient::BlendMode kBlendMode = static_cast<Hwc2::IComposerClient::BlendMode>(41); static constexpr float kAlpha = 51.f; - static constexpr uint32_t kType = 61u; - static constexpr uint32_t kAppId = 62u; static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71); static constexpr int kSupportedPerFrameMetadata = 101; static constexpr int kExpectedHwcSlot = 0; @@ -711,8 +726,6 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { mLayerFEState.blendMode = kBlendMode; mLayerFEState.alpha = kAlpha; - mLayerFEState.type = kType; - mLayerFEState.appId = kAppId; mLayerFEState.colorTransform = kColorTransform; mLayerFEState.color = kColor; mLayerFEState.surfaceDamage = kSurfaceDamage; @@ -746,7 +759,6 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { EXPECT_CALL(*mHwcLayer, setBlendMode(kBlendMode)).WillOnce(Return(kError)); EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError)); - EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError)); } void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) { @@ -858,7 +870,7 @@ TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) { // This test simulates a scenario where displayInstallOrientation is set to // ROT_90. This only has an effect on the transform; orientation stays 0 (see // DisplayDevice::setProjection). - mOutputState.orientation = TR_IDENT; + mOutputState.displaySpace.orientation = ui::ROTATION_0; mOutputState.transform = ui::Transform{TR_ROT_90}; // Buffers are pre-rotated based on the transform hint (ROT_90); their // geomBufferTransform is set to the inverse transform. @@ -988,7 +1000,7 @@ struct OutputLayerWriteCursorPositionToHWCTest : public OutputLayerTest { mLayerFEState.cursorFrame = kDefaultCursorFrame; - mOutputState.viewport = kDefaultDisplayViewport; + mOutputState.layerStackSpace.content = kDefaultDisplayViewport; mOutputState.transform = ui::Transform{kDefaultTransform}; } diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 7a06400d62..9badb99f96 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -136,7 +136,7 @@ struct OutputTest : public testing::Test { std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); - mOutput->editState().bounds = kDefaultDisplaySize; + mOutput->editState().displaySpace.bounds = kDefaultDisplaySize; } void injectOutputLayer(InjectedLayer& layer) { @@ -235,42 +235,123 @@ TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) { * Output::setProjection() */ -TEST_F(OutputTest, setProjectionTriviallyWorks) { - const ui::Transform transform{ui::Transform::ROT_180}; - const int32_t orientation = 123; - const Rect frame{1, 2, 3, 4}; - const Rect viewport{5, 6, 7, 8}; - const Rect sourceClip{9, 10, 11, 12}; - const Rect destinationClip{13, 14, 15, 16}; - const bool needsFiltering = true; +TEST_F(OutputTest, setProjectionWorks) { + const Rect displayRect{0, 0, 1000, 2000}; + mOutput->editState().displaySpace.bounds = displayRect; + mOutput->editState().framebufferSpace.bounds = displayRect; - mOutput->setProjection(transform, orientation, frame, viewport, sourceClip, destinationClip, - needsFiltering); + const ui::Rotation orientation = ui::ROTATION_90; + const Rect frame{50, 60, 100, 100}; + const Rect viewport{10, 20, 30, 40}; - EXPECT_THAT(mOutput->getState().transform, transform); - EXPECT_EQ(orientation, mOutput->getState().orientation); - EXPECT_EQ(frame, mOutput->getState().frame); - EXPECT_EQ(viewport, mOutput->getState().viewport); - EXPECT_EQ(sourceClip, mOutput->getState().sourceClip); - EXPECT_EQ(destinationClip, mOutput->getState().destinationClip); - EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering); + mOutput->setProjection(orientation, viewport, frame); + + EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation); + EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content); + EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content); + + const auto state = mOutput->getState(); + EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); + EXPECT_EQ(viewport, state.layerStackSpace.content); + EXPECT_EQ(viewport, state.layerStackSpace.bounds); + + EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); + EXPECT_EQ(frame, state.orientedDisplaySpace.content); + EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds); + + EXPECT_EQ(displayRect, state.displaySpace.bounds); + EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content); + EXPECT_EQ(orientation, state.displaySpace.orientation); + + EXPECT_EQ(displayRect, state.framebufferSpace.bounds); + EXPECT_EQ(Rect(900, 50, 940, 100), state.framebufferSpace.content); + EXPECT_EQ(orientation, state.framebufferSpace.orientation); + + EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); + + EXPECT_EQ(ui::Transform::ROT_90, mOutput->getTransformHint()); +} + +TEST_F(OutputTest, setProjectionWithSmallFramebufferWorks) { + const Rect displayRect{0, 0, 1000, 2000}; + const Rect framebufferRect{0, 0, 500, 1000}; + mOutput->editState().displaySpace.bounds = displayRect; + mOutput->editState().framebufferSpace.bounds = framebufferRect; + + const ui::Rotation orientation = ui::ROTATION_90; + const Rect frame{50, 60, 100, 100}; + const Rect viewport{10, 20, 30, 40}; + + mOutput->setProjection(orientation, viewport, frame); + + EXPECT_EQ(orientation, mOutput->getState().displaySpace.orientation); + EXPECT_EQ(frame, mOutput->getState().orientedDisplaySpace.content); + EXPECT_EQ(viewport, mOutput->getState().layerStackSpace.content); + + const auto state = mOutput->getState(); + EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); + EXPECT_EQ(viewport, state.layerStackSpace.content); + EXPECT_EQ(viewport, state.layerStackSpace.bounds); + + EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); + EXPECT_EQ(frame, state.orientedDisplaySpace.content); + EXPECT_EQ(Rect(0, 0, 2000, 1000), state.orientedDisplaySpace.bounds); + + EXPECT_EQ(displayRect, state.displaySpace.bounds); + EXPECT_EQ(Rect(900, 50, 940, 100), state.displaySpace.content); + EXPECT_EQ(orientation, state.displaySpace.orientation); + + EXPECT_EQ(framebufferRect, state.framebufferSpace.bounds); + EXPECT_EQ(Rect(450, 25, 470, 50), state.framebufferSpace.content); + EXPECT_EQ(orientation, state.framebufferSpace.orientation); + + EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); } /* - * Output::setBounds() + * Output::setDisplaySize() */ -TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) { - const ui::Size displaySize{200, 400}; +TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) { + mOutput->editState().layerStackSpace.content = Rect(0, 0, 2000, 1000); + mOutput->editState().layerStackSpace.bounds = Rect(0, 0, 2000, 1000); + mOutput->editState().orientedDisplaySpace.content = Rect(0, 0, 1800, 900); + mOutput->editState().orientedDisplaySpace.bounds = Rect(0, 0, 2000, 1000); + mOutput->editState().framebufferSpace.content = Rect(0, 0, 900, 1800); + mOutput->editState().framebufferSpace.bounds = Rect(0, 0, 1000, 2000); + mOutput->editState().framebufferSpace.orientation = ui::ROTATION_90; + mOutput->editState().displaySpace.content = Rect(0, 0, 900, 1800); + mOutput->editState().displaySpace.bounds = Rect(0, 0, 1000, 2000); + mOutput->editState().displaySpace.orientation = ui::ROTATION_90; + + const ui::Size newDisplaySize{500, 1000}; + + EXPECT_CALL(*mRenderSurface, setDisplaySize(newDisplaySize)).Times(1); - EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1); - EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize)); + mOutput->setDisplaySize(newDisplaySize); - mOutput->setBounds(displaySize); + const auto state = mOutput->getState(); - EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds); + const Rect displayRect(newDisplaySize); + EXPECT_EQ(ui::ROTATION_0, state.layerStackSpace.orientation); + EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.content); + EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds); - EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize)))); + EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); + EXPECT_EQ(Rect(0, 0, 900, 450), state.orientedDisplaySpace.content); + EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds); + + EXPECT_EQ(displayRect, state.displaySpace.bounds); + EXPECT_EQ(Rect(0, 0, 450, 900), state.displaySpace.content); + EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation); + + EXPECT_EQ(displayRect, state.framebufferSpace.bounds); + EXPECT_EQ(Rect(0, 0, 450, 900), state.framebufferSpace.content); + EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation); + + EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); + + EXPECT_THAT(state.dirtyRegion, RegionEq(Region(displayRect))); } /* @@ -431,7 +512,7 @@ TEST_F(OutputTest, setRenderSurfaceResetsBounds) { mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface)); - EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds); + EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().framebufferSpace.bounds); } /* @@ -440,7 +521,7 @@ TEST_F(OutputTest, setRenderSurfaceResetsBounds) { TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) { const Rect viewport{100, 200}; - mOutput->editState().viewport = viewport; + mOutput->editState().layerStackSpace.content = viewport; mOutput->editState().dirtyRegion.set(50, 300); { @@ -452,7 +533,7 @@ TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) { TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) { const Rect viewport{100, 200}; - mOutput->editState().viewport = viewport; + mOutput->editState().layerStackSpace.content = viewport; mOutput->editState().dirtyRegion.set(50, 300); { @@ -860,7 +941,7 @@ struct OutputRebuildLayerStacksTest : public testing::Test { OutputRebuildLayerStacksTest() { mOutput.mState.isEnabled = true; mOutput.mState.transform = kIdentityTransform; - mOutput.mState.bounds = kOutputBounds; + mOutput.mState.displaySpace.bounds = kOutputBounds; mRefreshArgs.updatingOutputGeometryThisFrame = true; @@ -1067,8 +1148,8 @@ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test { EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) .WillRepeatedly(Return(&mLayer.outputLayer)); - mOutput.mState.bounds = Rect(0, 0, 200, 300); - mOutput.mState.viewport = Rect(0, 0, 200, 300); + mOutput.mState.displaySpace.bounds = Rect(0, 0, 200, 300); + mOutput.mState.layerStackSpace.content = Rect(0, 0, 200, 300); mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300); mLayer.layerFEState.isVisible = true; @@ -1148,7 +1229,7 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisible } TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) { - mOutput.mState.bounds = Rect(0, 0, 0, 0); + mOutput.mState.displaySpace.bounds = Rect(0, 0, 0, 0); ensureOutputLayerIfVisible(); } @@ -1345,7 +1426,7 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); - mOutput.mState.viewport = Rect(0, 0, 300, 200); + mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200); mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300); EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); @@ -1371,7 +1452,7 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, mLayer.layerFEState.contentDirty = true; mLayer.layerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200); - mOutput.mState.viewport = Rect(0, 0, 300, 200); + mOutput.mState.layerStackSpace.content = Rect(0, 0, 300, 200); mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300); EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer.layerFE))) @@ -2785,12 +2866,12 @@ struct OutputComposeSurfacesTest : public testing::Test { mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); mOutput.cacheClientCompositionRequests(MAX_CLIENT_COMPOSITION_CACHE_SIZE); - mOutput.mState.frame = kDefaultOutputFrame; - mOutput.mState.viewport = kDefaultOutputViewport; - mOutput.mState.sourceClip = kDefaultOutputSourceClip; - mOutput.mState.destinationClip = kDefaultOutputDestinationClip; - mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation}; - mOutput.mState.orientation = kDefaultOutputOrientation; + mOutput.mState.orientedDisplaySpace.content = kDefaultOutputFrame; + mOutput.mState.layerStackSpace.content = kDefaultOutputViewport; + mOutput.mState.framebufferSpace.content = kDefaultOutputDestinationClip; + mOutput.mState.displaySpace.content = kDefaultOutputDestinationClip; + mOutput.mState.displaySpace.orientation = kDefaultOutputOrientation; + mOutput.mState.transform = ui::Transform{kDefaultOutputOrientationFlags}; mOutput.mState.dataspace = kDefaultOutputDataspace; mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat; mOutput.mState.isSecure = false; @@ -2825,7 +2906,9 @@ struct OutputComposeSurfacesTest : public testing::Test { // Call this member function to start using the mini-DSL defined above. [[nodiscard]] auto verify() { return ExecuteState::make(this); } - static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT; + static constexpr ui::Rotation kDefaultOutputOrientation = ui::ROTATION_0; + static constexpr uint32_t kDefaultOutputOrientationFlags = + ui::Transform::toRotationFlags(kDefaultOutputOrientation); static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN; static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3; static constexpr float kDefaultMaxLuminance = 0.9f; @@ -2834,7 +2917,6 @@ struct OutputComposeSurfacesTest : public testing::Test { static const Rect kDefaultOutputFrame; static const Rect kDefaultOutputViewport; - static const Rect kDefaultOutputSourceClip; static const Rect kDefaultOutputDestinationClip; static const mat4 kDefaultColorTransformMat; @@ -2856,7 +2938,6 @@ struct OutputComposeSurfacesTest : public testing::Test { const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004}; const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008}; -const Rect OutputComposeSurfacesTest::kDefaultOutputSourceClip{1009, 1010, 1011, 1012}; const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016}; const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f}; const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs; @@ -2871,6 +2952,7 @@ TEST_F(OutputComposeSurfacesTest, doesNothingButSignalNoExpensiveRenderingIfNoCl mOutput.mState.usesClientComposition = false; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)); @@ -2883,6 +2965,7 @@ TEST_F(OutputComposeSurfacesTest, mOutput.mState.flipClientTarget = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer)); EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)); @@ -2892,6 +2975,7 @@ TEST_F(OutputComposeSurfacesTest, TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFailsForClientComposition) { EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr)); @@ -2904,6 +2988,7 @@ TEST_F(OutputComposeSurfacesTest, mOutput.mState.flipClientTarget = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr)); @@ -2914,6 +2999,7 @@ TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) { EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) @@ -2936,6 +3022,7 @@ TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) { EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) @@ -2963,6 +3050,7 @@ TEST_F(OutputComposeSurfacesTest, renderDuplicateClientCompositionRequestsWithou EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) @@ -2991,6 +3079,7 @@ TEST_F(OutputComposeSurfacesTest, skipDuplicateClientCompositionRequests) { EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) @@ -3019,6 +3108,7 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) { EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) @@ -3050,6 +3140,7 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfRequestChanges) { EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r2})) .WillOnce(Return(std::vector<LayerFE::LayerSettings>{r1, r3})); @@ -3072,6 +3163,7 @@ TEST_F(OutputComposeSurfacesTest, clientCompositionIfRequestChanges) { struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest { OutputComposeSurfacesTest_UsesExpectedDisplaySettings() { EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace)) .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{})); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) @@ -3122,9 +3214,9 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposi verify().ifMixedCompositionIs(true) .andIfUsesHdr(true) .andIfSkipColorTransform(false) - .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip, + .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientation}) + Region::INVALID_REGION, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3133,9 +3225,9 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComp verify().ifMixedCompositionIs(true) .andIfUsesHdr(false) .andIfSkipColorTransform(false) - .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip, + .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientation}) + Region::INVALID_REGION, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3144,10 +3236,10 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientCo verify().ifMixedCompositionIs(false) .andIfUsesHdr(true) .andIfSkipColorTransform(false) - .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip, + .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, kDefaultColorTransformMat, Region::INVALID_REGION, - kDefaultOutputOrientation}) + kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3156,10 +3248,10 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClien verify().ifMixedCompositionIs(false) .andIfUsesHdr(false) .andIfSkipColorTransform(false) - .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip, + .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, kDefaultColorTransformMat, Region::INVALID_REGION, - kDefaultOutputOrientation}) + kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3169,9 +3261,9 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, verify().ifMixedCompositionIs(false) .andIfUsesHdr(true) .andIfSkipColorTransform(true) - .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputSourceClip, + .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(), - Region::INVALID_REGION, kDefaultOutputOrientation}) + Region::INVALID_REGION, kDefaultOutputOrientationFlags}) .execute() .expectAFenceWasReturned(); } @@ -3219,6 +3311,8 @@ TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) mOutput.mState.isSecure = false; mLayer2.mLayerFEState.hasProtectedContent = true; EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true)); + EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)); + EXPECT_CALL(mRenderEngine, useProtectedContext(false)).WillOnce(Return(true)); mOutput.composeSurfaces(kDebugRegion, kDefaultRefreshArgs); } @@ -3311,6 +3405,7 @@ struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSu EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false)); EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); + EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)) .WillRepeatedly(Return()); EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer)); @@ -3414,12 +3509,12 @@ struct GenerateClientCompositionRequestsTest : public testing::Test { struct GenerateClientCompositionRequestsTest_ThreeLayers : public GenerateClientCompositionRequestsTest { GenerateClientCompositionRequestsTest_ThreeLayers() { - mOutput.mState.frame = kDisplayFrame; - mOutput.mState.viewport = kDisplayViewport; - mOutput.mState.sourceClip = kDisplaySourceClip; - mOutput.mState.destinationClip = kDisplayDestinationClip; - mOutput.mState.transform = ui::Transform{kDisplayOrientation}; - mOutput.mState.orientation = kDisplayOrientation; + mOutput.mState.orientedDisplaySpace.content = kDisplayFrame; + mOutput.mState.layerStackSpace.content = kDisplayViewport; + mOutput.mState.displaySpace.content = kDisplayDestinationClip; + mOutput.mState.transform = + ui::Transform{ui::Transform::toRotationFlags(kDisplayOrientation)}; + mOutput.mState.displaySpace.orientation = kDisplayOrientation; mOutput.mState.needsFiltering = false; mOutput.mState.isSecure = false; @@ -3443,12 +3538,11 @@ struct GenerateClientCompositionRequestsTest_ThreeLayers EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size())); } - static constexpr uint32_t kDisplayOrientation = TR_IDENT; + static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0; static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN; static const Rect kDisplayFrame; static const Rect kDisplayViewport; - static const Rect kDisplaySourceClip; static const Rect kDisplayDestinationClip; std::array<Layer, 3> mLayers; @@ -3456,7 +3550,6 @@ struct GenerateClientCompositionRequestsTest_ThreeLayers const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200); const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201); -const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplaySourceClip(0, 0, 102, 202); const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayDestinationClip(0, 0, 103, 203); @@ -3587,7 +3680,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3599,7 +3691,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3643,7 +3734,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(Rect(10, 10, 20, 20)), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3655,7 +3745,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(Rect(0, 0, 30, 30)), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3667,7 +3756,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(Rect(0, 0, 40, 201)), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3699,7 +3787,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3711,7 +3798,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3723,7 +3809,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3755,7 +3840,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3768,7 +3852,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3780,7 +3863,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ true, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -3811,7 +3893,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ @@ -3823,7 +3904,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ @@ -3835,7 +3915,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ true, /* secure */ false, /* supports protected content */ @@ -3864,7 +3943,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ @@ -3876,7 +3954,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ @@ -3888,7 +3965,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, }; compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ true, /* supports protected content */ @@ -3938,6 +4014,34 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) mOutput->updateAndWriteCompositionState(args); } +TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) { + InjectedLayer layer1; + InjectedLayer layer2; + InjectedLayer layer3; + + // Layer requesting blur, or below, should request client composition. + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(false)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(false)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(false)); + + BlurRegion region; + layer2.layerFEState.blurRegions.push_back(region); + + injectOutputLayer(layer1); + injectOutputLayer(layer2); + injectOutputLayer(layer3); + + mOutput->editState().isEnabled = true; + + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = false; + args.devOptForceClientComposition = false; + mOutput->updateAndWriteCompositionState(args); +} + TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) { // In split-screen landscape mode, the screen is rotated 90 degrees, with // one layer on the left covering the left side of the output, and one layer @@ -3945,17 +4049,15 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq const Rect kPortraitFrame(0, 0, 1000, 2000); const Rect kPortraitViewport(0, 0, 2000, 1000); - const Rect kPortraitSourceClip(0, 0, 1000, 2000); const Rect kPortraitDestinationClip(0, 0, 1000, 2000); - const uint32_t kPortraitOrientation = TR_ROT_90; + const ui::Rotation kPortraitOrientation = ui::ROTATION_90; constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3; - mOutput.mState.frame = kPortraitFrame; - mOutput.mState.viewport = kPortraitViewport; - mOutput.mState.sourceClip = kPortraitSourceClip; - mOutput.mState.destinationClip = kPortraitDestinationClip; - mOutput.mState.transform = ui::Transform{kPortraitOrientation}; - mOutput.mState.orientation = kPortraitOrientation; + mOutput.mState.orientedDisplaySpace.content = kPortraitFrame; + mOutput.mState.layerStackSpace.content = kPortraitViewport; + mOutput.mState.displaySpace.content = kPortraitDestinationClip; + mOutput.mState.transform = ui::Transform{ui::Transform::toRotationFlags(kPortraitOrientation)}; + mOutput.mState.displaySpace.orientation = kPortraitOrientation; mOutput.mState.needsFiltering = false; mOutput.mState.isSecure = true; @@ -3982,7 +4084,6 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{ Region(Rect(0, 0, 1000, 1000)), - false, /* identity transform */ false, /* needs filtering */ true, /* secure */ true, /* supports protected content */ @@ -4000,7 +4101,6 @@ TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenReq compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{ Region(Rect(1000, 0, 2000, 1000)), - false, /* identity transform */ false, /* needs filtering */ true, /* secure */ true, /* supports protected content */ @@ -4034,7 +4134,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{ Region(Rect(60, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */ - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ @@ -4080,7 +4179,6 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, Region accumClearRegion(Rect(10, 11, 12, 13)); compositionengine::LayerFE::ClientCompositionTargetSettings layer2Settings{ Region(Rect(50, 40, 70, 80)).merge(Rect(40, 80, 70, 90)), /* visible region */ - false, /* identity transform */ false, /* needs filtering */ false, /* secure */ false, /* supports protected content */ diff --git a/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp new file mode 100644 index 0000000000..704f5a8c07 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/ProjectionSpaceTest.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <compositionengine/ProjectionSpace.h> +#include <gtest/gtest.h> + +namespace android::compositionengine { +namespace { + +// Returns a rectangular strip along the side of the given rect pointed by +// rotation. E.g. if rotation is ROTATION_0, the srip will be along the top +// side, if it is ROTATION_90 the stip will be along the right wall. +// One of the dimensions of the strip will be 0 and the other one will match +// the length of the corresponding side. +// The strip will be contained inside the given rect. +Rect getSideStrip(const Rect& rect, ui::Rotation rotation) { + int width, height; + if (rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270) { + width = 0; + height = rect.height(); + } else { + width = rect.width(); + height = 0; + } + + if (rotation == ui::ROTATION_0 || rotation == ui::ROTATION_270) { + return Rect(rect.left, rect.top, rect.left + width, rect.top + height); + } + + if (rotation == ui::ROTATION_90) { + return Rect(rect.right, rect.top, rect.right + width, rect.top + height); + } + + if (rotation == ui::ROTATION_180) { + return Rect(rect.left, rect.bottom, rect.left + width, rect.bottom + height); + } + + return Rect::INVALID_RECT; +} +} // namespace + +TEST(ProjectionSpaceTest, getTransformToSelfIsIdentity) { + ProjectionSpace space; + space.content = Rect(100, 200); + space.bounds = Rect(100, 200); + + const ui::Transform identity; + for (int rotation = 0; rotation <= 3; rotation++) { + space.orientation = ui::Rotation(rotation); + EXPECT_EQ(space.getTransform(space), identity); + } +} + +TEST(ProjectionSpaceTest, getTransformWhenTranslationIsNeeded) { + ProjectionSpace source; + source.content = Rect(10, 10, 20, 20); + source.bounds = Rect(100, 200); + + ProjectionSpace dest; + dest.content = Rect(10, 20, 30, 20); + dest.bounds = source.bounds; + + const auto transform = source.getTransform(dest); + EXPECT_EQ(transform.transform(source.content), dest.content); +} + +TEST(ProjectionSpaceTest, getTransformWhenScaleIsNeeded) { + ProjectionSpace source; + source.content = Rect(0, 0, 20, 20); + source.bounds = Rect(100, 200); + + ProjectionSpace dest; + dest.content = Rect(0, 0, 40, 30); + dest.bounds = source.bounds; + + const auto transform = source.getTransform(dest); + EXPECT_EQ(transform.transform(source.content), dest.content); +} + +TEST(ProjectionSpaceTest, getSideStripTest) { + const Rect rect(10, 20, 40, 100); + EXPECT_EQ(getSideStrip(rect, ui::ROTATION_0), Rect(10, 20, 40, 20)); + EXPECT_EQ(getSideStrip(rect, ui::ROTATION_90), Rect(40, 20, 40, 100)); + EXPECT_EQ(getSideStrip(rect, ui::ROTATION_180), Rect(10, 100, 40, 100)); + EXPECT_EQ(getSideStrip(rect, ui::ROTATION_270), Rect(10, 20, 10, 100)); +} + +void testTransform(const ProjectionSpace& source, const ProjectionSpace& dest) { + const auto transform = source.getTransform(dest); + EXPECT_EQ(transform.transform(source.content), dest.content) + << "Source content doesn't map to dest content when projecting " << to_string(source) + << " onto " << to_string(dest); + + // We take a strip at the top (according to the orientation) of each + // content rect and verify that transform maps between them. This way we + // verify that the transform is rotating properly. + // In the following example the strip is marked with asterisks: + // + // ******* +-------* + // | | | * + // | | | * + // +-----+ +-------* + // source(ROTATION_0) dest (ROTATION_90) + const auto sourceStrip = getSideStrip(source.content, source.orientation); + const auto destStrip = getSideStrip(dest.content, dest.orientation); + ASSERT_NE(sourceStrip, Rect::INVALID_RECT); + ASSERT_NE(destStrip, Rect::INVALID_RECT); + const auto mappedStrip = transform.transform(sourceStrip); + EXPECT_EQ(mappedStrip, destStrip) + << to_string(sourceStrip) << " maps to " << to_string(mappedStrip) << " instead of " + << to_string(destStrip) << " when projecting " << to_string(source) << " onto " + << to_string(dest); +} + +TEST(ProjectionSpaceTest, getTransformWithOrienations) { + ProjectionSpace source; + source.bounds = Rect(12, 13, 678, 789); + source.content = Rect(40, 50, 234, 343); + ProjectionSpace dest; + dest.bounds = Rect(17, 18, 879, 564); + dest.content = Rect(43, 52, 432, 213); + + for (int sourceRot = 0; sourceRot <= 3; sourceRot++) { + source.orientation = ui::Rotation(sourceRot); + for (int destRot = 0; destRot <= 3; destRot++) { + dest.orientation = ui::Rotation(destRot); + testTransform(source, dest); + } + } +} + +} // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp index fd47e453c8..6ce8a6b2ea 100644 --- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp @@ -33,7 +33,7 @@ namespace { constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920; constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080; -constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u}); +constexpr DisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId(123u); const std::string DEFAULT_DISPLAY_NAME = "Mock Display"; using testing::_; @@ -48,7 +48,7 @@ using testing::StrictMock; class RenderSurfaceTest : public testing::Test { public: RenderSurfaceTest() { - EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID)); + EXPECT_CALL(mDisplay, getId()).WillRepeatedly(Return(DEFAULT_DISPLAY_ID)); EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME)); EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine)); EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)) @@ -82,7 +82,8 @@ TEST_F(RenderSurfaceTest, initializeConfiguresNativeWindow) { EXPECT_CALL(*mNativeWindow, connect(NATIVE_WINDOW_API_EGL)).WillOnce(Return(NO_ERROR)); EXPECT_CALL(*mNativeWindow, setBuffersFormat(HAL_PIXEL_FORMAT_RGBA_8888)) .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE)) + .WillOnce(Return(NO_ERROR)); mSurface.initialize(); } @@ -136,7 +137,9 @@ TEST_F(RenderSurfaceTest, setBufferDataspaceAppliesChange) { TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) { EXPECT_FALSE(mSurface.isProtected()); - EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED)) + EXPECT_CALL(*mNativeWindow, + setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE | + GRALLOC_USAGE_PROTECTED)) .WillOnce(Return(NO_ERROR)); mSurface.setProtected(true); @@ -145,7 +148,8 @@ TEST_F(RenderSurfaceTest, setProtectedTrueEnablesProtection) { TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) { EXPECT_FALSE(mSurface.isProtected()); - EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE)) + .WillOnce(Return(NO_ERROR)); mSurface.setProtected(false); EXPECT_FALSE(mSurface.isProtected()); @@ -153,9 +157,12 @@ TEST_F(RenderSurfaceTest, setProtectedFalseDisablesProtection) { TEST_F(RenderSurfaceTest, setProtectedEnableAndDisable) { EXPECT_FALSE(mSurface.isProtected()); - EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED)) + EXPECT_CALL(*mNativeWindow, + setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE | + GRALLOC_USAGE_PROTECTED)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE)) .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER)).WillOnce(Return(NO_ERROR)); mSurface.setProtected(true); EXPECT_TRUE(mSurface.isProtected()); @@ -165,7 +172,9 @@ TEST_F(RenderSurfaceTest, setProtectedEnableAndDisable) { TEST_F(RenderSurfaceTest, setProtectedEnableWithError) { EXPECT_FALSE(mSurface.isProtected()); - EXPECT_CALL(*mNativeWindow, setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_PROTECTED)) + EXPECT_CALL(*mNativeWindow, + setUsage(GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE | + GRALLOC_USAGE_PROTECTED)) .WillOnce(Return(INVALID_OPERATION)); mSurface.setProtected(true); EXPECT_FALSE(mSurface.isProtected()); diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 730f29744a..cbc201fabc 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -29,6 +29,7 @@ #include <compositionengine/DisplayColorProfileCreationArgs.h> #include <compositionengine/DisplayCreationArgs.h> #include <compositionengine/DisplaySurface.h> +#include <compositionengine/ProjectionSpace.h> #include <compositionengine/RenderSurface.h> #include <compositionengine/RenderSurfaceCreationArgs.h> #include <compositionengine/impl/OutputCompositionState.h> @@ -101,11 +102,11 @@ void DisplayDevice::disconnect() { } int DisplayDevice::getWidth() const { - return mCompositionDisplay->getState().bounds.getWidth(); + return mCompositionDisplay->getState().displaySpace.bounds.getWidth(); } int DisplayDevice::getHeight() const { - return mCompositionDisplay->getState().bounds.getHeight(); + return mCompositionDisplay->getState().displaySpace.bounds.getHeight(); } void DisplayDevice::setDisplayName(const std::string& displayName) { @@ -116,6 +117,10 @@ void DisplayDevice::setDisplayName(const std::string& displayName) { } } +void DisplayDevice::setDeviceProductInfo(std::optional<DeviceProductInfo> info) { + mDeviceProductInfo = std::move(info); +} + uint32_t DisplayDevice::getPageFlipCount() const { return mCompositionDisplay->getRenderSurface()->getPageFlipCount(); } @@ -151,96 +156,38 @@ void DisplayDevice::setLayerStack(ui::LayerStack stack) { } void DisplayDevice::setDisplaySize(int width, int height) { - mCompositionDisplay->setBounds(ui::Size(width, height)); + LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays."); + mCompositionDisplay->setDisplaySize(ui::Size(width, height)); } -void DisplayDevice::setProjection(ui::Rotation orientation, Rect viewport, Rect frame) { +void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect, + Rect orientedDisplaySpaceRect) { mOrientation = orientation; - const Rect& displayBounds = getCompositionDisplay()->getState().bounds; - const int displayWidth = displayBounds.width(); - const int displayHeight = displayBounds.height(); - - ui::Transform rotation; - if (const auto flags = ui::Transform::toRotationFlags(orientation); - flags != ui::Transform::ROT_INVALID) { - rotation.set(flags, displayWidth, displayHeight); - } - - if (!frame.isValid()) { - // the destination frame can be invalid if it has never been set, - // in that case we assume the whole display frame. - frame = Rect(displayWidth, displayHeight); - } - - if (viewport.isEmpty()) { - // viewport can be invalid if it has never been set, in that case - // we assume the whole display size. - // it's also invalid to have an empty viewport, so we handle that - // case in the same way. - viewport = Rect(displayWidth, displayHeight); - if (rotation.getOrientation() & ui::Transform::ROT_90) { - // viewport is always specified in the logical orientation - // of the display (ie: post-rotation). - std::swap(viewport.right, viewport.bottom); - } - } - - ui::Transform logicalTranslation, physicalTranslation, scale; - const float sourceWidth = viewport.width(); - const float sourceHeight = viewport.height(); - const float destWidth = frame.width(); - const float destHeight = frame.height(); - if (sourceWidth != destWidth || sourceHeight != destHeight) { - const float scaleX = destWidth / sourceWidth; - const float scaleY = destHeight / sourceHeight; - scale.set(scaleX, 0, 0, scaleY); - } - - const float sourceX = viewport.left; - const float sourceY = viewport.top; - const float destX = frame.left; - const float destY = frame.top; - logicalTranslation.set(-sourceX, -sourceY); - physicalTranslation.set(destX, destY); - - // need to take care of primary display rotation for globalTransform - // for case if the panel is not installed aligned with device orientation if (isPrimary()) { - if (const auto flags = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation); - flags != ui::Transform::ROT_INVALID) { - rotation.set(flags, displayWidth, displayHeight); - } + sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation); } - // The viewport and frame are both in the logical orientation. - // Apply the logical translation, scale to physical size, apply the - // physical translation and finally rotate to the physical orientation. - ui::Transform globalTransform = rotation * physicalTranslation * scale * logicalTranslation; - - const uint8_t type = globalTransform.getType(); - const bool needsFiltering = - (!globalTransform.preserveRects() || (type >= ui::Transform::SCALE)); - - const Rect& sourceClip = viewport; - Rect destinationClip = globalTransform.transform(viewport); - if (destinationClip.isEmpty()) { - destinationClip = displayBounds; + if (!orientedDisplaySpaceRect.isValid()) { + // The destination frame can be invalid if it has never been set, + // in that case we assume the whole display size. + orientedDisplaySpaceRect = getCompositionDisplay()->getState().displaySpace.bounds; } - // Make sure the destination clip is contained in the display bounds - destinationClip.intersect(displayBounds, &destinationClip); - - uint32_t transformOrientation; - if (isPrimary()) { - sPrimaryDisplayRotationFlags = ui::Transform::toRotationFlags(orientation); - transformOrientation = ui::Transform::toRotationFlags(orientation + mPhysicalOrientation); - } else { - transformOrientation = ui::Transform::toRotationFlags(orientation); + if (layerStackSpaceRect.isEmpty()) { + // The layerStackSpaceRect can be invalid if it has never been set, in that case + // we assume the whole framebuffer size. + layerStackSpaceRect = getCompositionDisplay()->getState().framebufferSpace.bounds; + if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) { + std::swap(layerStackSpaceRect.right, layerStackSpaceRect.bottom); + } } - getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport, - sourceClip, destinationClip, needsFiltering); + // We need to take care of display rotation for globalTransform for case if the panel is not + // installed aligned with device orientation. + const auto transformOrientation = orientation + mPhysicalOrientation; + getCompositionDisplay()->setProjection(transformOrientation, layerStackSpaceRect, + orientedDisplaySpaceRect); } ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() { @@ -248,17 +195,12 @@ ui::Transform::RotationFlags DisplayDevice::getPrimaryDisplayRotationFlags() { } std::string DisplayDevice::getDebugName() const { - std::string displayId; - if (const auto id = getId()) { - displayId = to_string(*id) + ", "; - } - const char* type = "virtual"; if (mConnectionType) { type = *mConnectionType == DisplayConnectionType::Internal ? "internal" : "external"; } - return base::StringPrintf("DisplayDevice{%s%s%s, \"%s\"}", displayId.c_str(), type, + return base::StringPrintf("DisplayDevice{%s, %s%s, \"%s\"}", to_string(getId()).c_str(), type, isPrimary() ? ", primary" : "", mDisplayName.c_str()); } @@ -269,6 +211,12 @@ void DisplayDevice::dump(std::string& result) const { StringAppendF(&result, "powerMode=%s (%d), ", to_string(mPowerMode).c_str(), static_cast<int32_t>(mPowerMode)); StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value()); + StringAppendF(&result, "deviceProductInfo="); + if (mDeviceProductInfo) { + mDeviceProductInfo->dump(result); + } else { + result.append("{}"); + } getCompositionDisplay()->dump(result); } @@ -276,9 +224,7 @@ bool DisplayDevice::hasRenderIntent(ui::RenderIntent intent) const { return mCompositionDisplay->getDisplayColorProfile()->hasRenderIntent(intent); } -// ---------------------------------------------------------------------------- - -const std::optional<DisplayId>& DisplayDevice::getId() const { +DisplayId DisplayDevice::getId() const { return mCompositionDisplay->getId(); } @@ -287,7 +233,7 @@ bool DisplayDevice::isSecure() const { } const Rect& DisplayDevice::getBounds() const { - return mCompositionDisplay->getState().bounds; + return mCompositionDisplay->getState().displaySpace.bounds; } const Region& DisplayDevice::getUndefinedRegion() const { @@ -302,20 +248,20 @@ ui::LayerStack DisplayDevice::getLayerStack() const { return mCompositionDisplay->getState().layerStackId; } -const ui::Transform& DisplayDevice::getTransform() const { - return mCompositionDisplay->getState().transform; +ui::Transform::RotationFlags DisplayDevice::getTransformHint() const { + return mCompositionDisplay->getTransformHint(); } -const Rect& DisplayDevice::getViewport() const { - return mCompositionDisplay->getState().viewport; +const ui::Transform& DisplayDevice::getTransform() const { + return mCompositionDisplay->getState().transform; } -const Rect& DisplayDevice::getFrame() const { - return mCompositionDisplay->getState().frame; +const Rect& DisplayDevice::getLayerStackSpaceRect() const { + return mCompositionDisplay->getState().layerStackSpace.content; } -const Rect& DisplayDevice::getSourceClip() const { - return mCompositionDisplay->getState().sourceClip; +const Rect& DisplayDevice::getOrientedDisplaySpaceRect() const { + return mCompositionDisplay->getState().orientedDisplaySpace.content; } bool DisplayDevice::hasWideColorGamut() const { diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index cb467ea292..cc38ab0406 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -27,6 +27,7 @@ #include <math/mat4.h> #include <renderengine/RenderEngine.h> #include <system/window.h> +#include <ui/DisplayId.h> #include <ui/DisplayInfo.h> #include <ui/DisplayState.h> #include <ui/GraphicTypes.h> @@ -40,7 +41,6 @@ #include "DisplayHardware/DisplayIdentification.h" #include "DisplayHardware/Hal.h" #include "DisplayHardware/PowerAdvisor.h" -#include "RenderArea.h" #include "Scheduler/HwcStrongTypes.h" namespace android { @@ -59,12 +59,14 @@ class Display; class DisplaySurface; } // namespace compositionengine -class DisplayDevice : public LightRefBase<DisplayDevice> { +class DisplayDevice : public RefBase { public: constexpr static float sDefaultMinLumiance = 0.0; constexpr static float sDefaultMaxLumiance = 500.0; explicit DisplayDevice(DisplayDeviceCreationArgs& args); + + // Must be destroyed on the main thread because it may call into HWComposer. virtual ~DisplayDevice(); std::shared_ptr<compositionengine::Display> getCompositionDisplay() const { @@ -93,18 +95,22 @@ public: static ui::Transform::RotationFlags getPrimaryDisplayRotationFlags(); - ui::Transform::RotationFlags getTransformHint() const { - return static_cast<ui::Transform::RotationFlags>(getTransform().getOrientation()); - } - + ui::Transform::RotationFlags getTransformHint() const; const ui::Transform& getTransform() const; - const Rect& getViewport() const; - const Rect& getFrame() const; - const Rect& getSourceClip() const; + const Rect& getLayerStackSpaceRect() const; + const Rect& getOrientedDisplaySpaceRect() const; bool needsFiltering() const; ui::LayerStack getLayerStack() const; - const std::optional<DisplayId>& getId() const; + // Returns the physical ID of this display. This function asserts the ID is physical and it + // shouldn't be called for other display types, e.g. virtual. + PhysicalDisplayId getPhysicalId() const { + const auto displayIdOpt = PhysicalDisplayId::tryCast(getId()); + LOG_FATAL_IF(!displayIdOpt); + return *displayIdOpt; + } + + DisplayId getId() const; const wp<IBinder>& getDisplayToken() const { return mDisplayToken; } int32_t getSequenceId() const { return mSequenceId; } @@ -136,6 +142,11 @@ public: void setDisplayName(const std::string& displayName); const std::string& getDisplayName() const { return mDisplayName; } + void setDeviceProductInfo(std::optional<DeviceProductInfo> info); + const std::optional<DeviceProductInfo>& getDeviceProductInfo() const { + return mDeviceProductInfo; + } + /* ------------------------------------------------------------------------ * Display power mode management. */ @@ -182,14 +193,16 @@ private: // TODO(b/74619554): Remove special cases for primary display. const bool mIsPrimary; + + std::optional<DeviceProductInfo> mDeviceProductInfo; }; struct DisplayDeviceState { struct Physical { - DisplayId id; + PhysicalDisplayId id; DisplayConnectionType type; hardware::graphics::composer::hal::HWDisplayId hwcDisplayId; - + std::optional<DeviceProductInfo> deviceProductInfo; bool operator==(const Physical& other) const { return id == other.id && type == other.type && hwcDisplayId == other.hwcDisplayId; } @@ -201,8 +214,8 @@ struct DisplayDeviceState { std::optional<Physical> physical; sp<IGraphicBufferProducer> surface; ui::LayerStack layerStack = ui::NO_LAYER_STACK; - Rect viewport; - Rect frame; + Rect layerStackSpaceRect; + Rect orientedDisplaySpaceRect; ui::Rotation orientation = ui::ROTATION_0; uint32_t width = 0; uint32_t height = 0; @@ -237,118 +250,4 @@ struct DisplayDeviceCreationArgs { bool isPrimary{false}; }; -class DisplayRenderArea : public RenderArea { -public: - DisplayRenderArea(const sp<const DisplayDevice>& display, - RotationFlags rotation = ui::Transform::ROT_0) - : DisplayRenderArea(display, display->getBounds(), - static_cast<uint32_t>(display->getWidth()), - static_cast<uint32_t>(display->getHeight()), - display->getCompositionDataSpace(), rotation) {} - - DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, ui::Dataspace reqDataSpace, RotationFlags rotation, - bool allowSecureLayers = true) - : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace, - display->getViewport(), applyDeviceOrientation(rotation, display)), - mDisplay(std::move(display)), - mSourceCrop(sourceCrop), - mAllowSecureLayers(allowSecureLayers) {} - - const ui::Transform& getTransform() const override { return mTransform; } - Rect getBounds() const override { return mDisplay->getBounds(); } - int getHeight() const override { return mDisplay->getHeight(); } - int getWidth() const override { return mDisplay->getWidth(); } - bool isSecure() const override { return mAllowSecureLayers && mDisplay->isSecure(); } - sp<const DisplayDevice> getDisplayDevice() const override { return mDisplay; } - - bool needsFiltering() const override { - // check if the projection from the logical render area - // to the physical render area requires filtering - const Rect& sourceCrop = getSourceCrop(); - int width = sourceCrop.width(); - int height = sourceCrop.height(); - if (getRotationFlags() & ui::Transform::ROT_90) { - std::swap(width, height); - } - return width != getReqWidth() || height != getReqHeight(); - } - - Rect getSourceCrop() const override { - // use the projected display viewport by default. - if (mSourceCrop.isEmpty()) { - return mDisplay->getSourceClip(); - } - - // If there is a source crop provided then it is assumed that the device - // was in portrait orientation. This may not logically be true, so - // correct for the orientation error by undoing the rotation - - ui::Rotation logicalOrientation = mDisplay->getOrientation(); - if (logicalOrientation == ui::Rotation::Rotation90) { - logicalOrientation = ui::Rotation::Rotation270; - } else if (logicalOrientation == ui::Rotation::Rotation270) { - logicalOrientation = ui::Rotation::Rotation90; - } - - const auto flags = ui::Transform::toRotationFlags(logicalOrientation); - int width = mDisplay->getSourceClip().getWidth(); - int height = mDisplay->getSourceClip().getHeight(); - ui::Transform rotation; - rotation.set(flags, width, height); - return rotation.transform(mSourceCrop); - } - -private: - static RotationFlags applyDeviceOrientation(RotationFlags orientationFlag, - const sp<const DisplayDevice>& device) { - uint32_t inverseRotate90 = 0; - uint32_t inverseReflect = 0; - - // Reverse the logical orientation. - ui::Rotation logicalOrientation = device->getOrientation(); - if (logicalOrientation == ui::Rotation::Rotation90) { - logicalOrientation = ui::Rotation::Rotation270; - } else if (logicalOrientation == ui::Rotation::Rotation270) { - logicalOrientation = ui::Rotation::Rotation90; - } - - const ui::Rotation orientation = device->getPhysicalOrientation() + logicalOrientation; - - switch (orientation) { - case ui::ROTATION_0: - return orientationFlag; - - case ui::ROTATION_90: - inverseRotate90 = ui::Transform::ROT_90; - inverseReflect = ui::Transform::ROT_180; - break; - - case ui::ROTATION_180: - inverseReflect = ui::Transform::ROT_180; - break; - - case ui::ROTATION_270: - inverseRotate90 = ui::Transform::ROT_90; - break; - } - - const uint32_t rotate90 = orientationFlag & ui::Transform::ROT_90; - uint32_t reflect = orientationFlag & ui::Transform::ROT_180; - - // Apply reflection for double rotation. - if (rotate90 & inverseRotate90) { - reflect = ~reflect & ui::Transform::ROT_180; - } - - return static_cast<RotationFlags>((rotate90 ^ inverseRotate90) | - (reflect ^ inverseReflect)); - } - - const sp<const DisplayDevice> mDisplay; - const Rect mSourceCrop; - const bool mAllowSecureLayers; - const ui::Transform mTransform = ui::Transform(); -}; - } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index a3f1b52378..1bf43dacdf 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -117,63 +117,7 @@ Error unwrapRet(Return<Error>& ret) namespace impl { -#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER -Composer::CommandWriter::CommandWriter(uint32_t initialMaxSize) - : CommandWriterBase(initialMaxSize) {} - -Composer::CommandWriter::~CommandWriter() -{ -} - -void Composer::CommandWriter::setLayerInfo(uint32_t type, uint32_t appId) -{ - constexpr uint16_t kSetLayerInfoLength = 2; - beginCommand(static_cast<V2_1::IComposerClient::Command>( - IVrComposerClient::VrCommand::SET_LAYER_INFO), - kSetLayerInfoLength); - write(type); - write(appId); - endCommand(); -} - -void Composer::CommandWriter::setClientTargetMetadata( - const IVrComposerClient::BufferMetadata& metadata) -{ - constexpr uint16_t kSetClientTargetMetadataLength = 7; - beginCommand(static_cast<V2_1::IComposerClient::Command>( - IVrComposerClient::VrCommand::SET_CLIENT_TARGET_METADATA), - kSetClientTargetMetadataLength); - writeBufferMetadata(metadata); - endCommand(); -} - -void Composer::CommandWriter::setLayerBufferMetadata( - const IVrComposerClient::BufferMetadata& metadata) -{ - constexpr uint16_t kSetLayerBufferMetadataLength = 7; - beginCommand(static_cast<V2_1::IComposerClient::Command>( - IVrComposerClient::VrCommand::SET_LAYER_BUFFER_METADATA), - kSetLayerBufferMetadataLength); - writeBufferMetadata(metadata); - endCommand(); -} - -void Composer::CommandWriter::writeBufferMetadata( - const IVrComposerClient::BufferMetadata& metadata) -{ - write(metadata.width); - write(metadata.height); - write(metadata.stride); - write(metadata.layerCount); - writeSigned(static_cast<int32_t>(metadata.format)); - write64(metadata.usage); -} -#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - -Composer::Composer(const std::string& serviceName) - : mWriter(kWriterInitialSize), - mIsUsingVrComposer(serviceName == std::string("vr")) -{ +Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize) { mComposer = V2_1::IComposer::getService(serviceName); if (mComposer == nullptr) { @@ -215,15 +159,6 @@ Composer::Composer(const std::string& serviceName) if (mClient == nullptr) { LOG_ALWAYS_FATAL("failed to create composer client"); } - -#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - if (mIsUsingVrComposer) { - sp<IVrComposerClient> vrClient = IVrComposerClient::castFrom(mClient); - if (vrClient == nullptr) { - LOG_ALWAYS_FATAL("failed to create vr composer client"); - } - } -#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER } Composer::~Composer() = default; @@ -262,10 +197,6 @@ void Composer::registerCallback(const sp<IComposerCallback>& callback) } } -bool Composer::isRemote() { - return mClient->isRemote(); -} - void Composer::resetCommands() { mWriter.reset(); } @@ -587,20 +518,6 @@ Error Composer::setClientTarget(Display display, uint32_t slot, { mWriter.selectDisplay(display); -#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - if (mIsUsingVrComposer && target.get()) { - IVrComposerClient::BufferMetadata metadata = { - .width = target->getWidth(), - .height = target->getHeight(), - .stride = target->getStride(), - .layerCount = target->getLayerCount(), - .format = static_cast<types::V1_2::PixelFormat>(target->getPixelFormat()), - .usage = target->getUsage(), - }; - mWriter.setClientTargetMetadata(metadata); - } -#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - const native_handle_t* handle = nullptr; if (target.get()) { handle = target->getNativeBuffer()->handle; @@ -720,20 +637,6 @@ Error Composer::setLayerBuffer(Display display, Layer layer, mWriter.selectDisplay(display); mWriter.selectLayer(layer); -#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - if (mIsUsingVrComposer && buffer.get()) { - IVrComposerClient::BufferMetadata metadata = { - .width = buffer->getWidth(), - .height = buffer->getHeight(), - .stride = buffer->getStride(), - .layerCount = buffer->getLayerCount(), - .format = static_cast<types::V1_2::PixelFormat>(buffer->getPixelFormat()), - .usage = buffer->getUsage(), - }; - mWriter.setLayerBufferMetadata(metadata); - } -#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - const native_handle_t* handle = nullptr; if (buffer.get()) { handle = buffer->getNativeBuffer()->handle; @@ -850,27 +753,6 @@ Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z) return Error::NONE; } -#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER -Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type, - uint32_t appId) -{ - if (mIsUsingVrComposer) { - mWriter.selectDisplay(display); - mWriter.selectLayer(layer); - mWriter.setLayerInfo(type, appId); - } - return Error::NONE; -} -#else -Error Composer::setLayerInfo(Display display, Layer layer, uint32_t, uint32_t) { - if (mIsUsingVrComposer) { - mWriter.selectDisplay(display); - mWriter.selectLayer(layer); - } - return Error::NONE; -} -#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - Error Composer::execute() { // prepare input command queue diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 00ef782ef7..5b66809d53 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -27,9 +27,6 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" -#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER -#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> -#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER #include <android/hardware/graphics/common/1.1/types.h> #include <android/hardware/graphics/composer/2.4/IComposer.h> #include <android/hardware/graphics/composer/2.4/IComposerClient.h> @@ -47,10 +44,6 @@ namespace android { namespace Hwc2 { -#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER -using frameworks::vr::composer::V2_0::IVrComposerClient; -#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - namespace types = hardware::graphics::common; namespace V2_1 = hardware::graphics::composer::V2_1; @@ -91,11 +84,6 @@ public: virtual void registerCallback(const sp<IComposerCallback>& callback) = 0; - // Returns true if the connected composer service is running in a remote - // process, false otherwise. This will return false if the service is - // configured in passthrough mode, for example. - virtual bool isRemote() = 0; - // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. virtual void resetCommands() = 0; @@ -104,7 +92,6 @@ public: virtual Error executeCommands() = 0; virtual uint32_t getMaxVirtualDisplayCount() = 0; - virtual bool isUsingVrComposer() const = 0; virtual Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, Display* outDisplay) = 0; virtual Error destroyVirtualDisplay(Display display) = 0; @@ -188,7 +175,6 @@ public: virtual Error setLayerVisibleRegion(Display display, Layer layer, const std::vector<IComposerClient::Rect>& visible) = 0; virtual Error setLayerZOrder(Display display, Layer layer, uint32_t z) = 0; - virtual Error setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) = 0; // Composer HAL 2.2 virtual Error setLayerPerFrameMetadata( @@ -344,11 +330,6 @@ public: void registerCallback(const sp<IComposerCallback>& callback) override; - // Returns true if the connected composer service is running in a remote - // process, false otherwise. This will return false if the service is - // configured in passthrough mode, for example. - bool isRemote() override; - // Reset all pending commands in the command buffer. Useful if you want to // skip a frame but have already queued some commands. void resetCommands() override; @@ -357,7 +338,6 @@ public: Error executeCommands() override; uint32_t getMaxVirtualDisplayCount() override; - bool isUsingVrComposer() const override { return mIsUsingVrComposer; } Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, Display* outDisplay) override; Error destroyVirtualDisplay(Display display) override; @@ -436,7 +416,6 @@ public: Error setLayerVisibleRegion(Display display, Layer layer, const std::vector<IComposerClient::Rect>& visible) override; Error setLayerZOrder(Display display, Layer layer, uint32_t z) override; - Error setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) override; // Composer HAL 2.2 Error setLayerPerFrameMetadata( @@ -490,29 +469,11 @@ public: IComposerClient::ClientTargetProperty* outClientTargetProperty) override; private: -#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER - class CommandWriter : public CommandWriterBase { - public: - explicit CommandWriter(uint32_t initialMaxSize); - ~CommandWriter() override; - - void setLayerInfo(uint32_t type, uint32_t appId); - void setClientTargetMetadata( - const IVrComposerClient::BufferMetadata& metadata); - void setLayerBufferMetadata( - const IVrComposerClient::BufferMetadata& metadata); - - private: - void writeBufferMetadata( - const IVrComposerClient::BufferMetadata& metadata); - }; -#else class CommandWriter : public CommandWriterBase { public: explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {} ~CommandWriter() override {} }; -#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER // Many public functions above simply write a command into the command // queue to batch the calls. validateDisplay and presentDisplay will call @@ -531,10 +492,6 @@ private: 64 * 1024 / sizeof(uint32_t) - 16; CommandWriter mWriter; CommandReader mReader; - - // When true, the we attach to the vr_hwcomposer service instead of the - // hwcomposer. This allows us to redirect surfaces to 3d surfaces in vr. - const bool mIsUsingVrComposer; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp index 4dfc7431de..98209bb9e4 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #undef LOG_TAG #define LOG_TAG "DisplayIdentification" @@ -38,7 +34,6 @@ using byte_view = std::basic_string_view<uint8_t>; constexpr size_t kEdidBlockSize = 128; constexpr size_t kEdidHeaderLength = 5; -constexpr uint16_t kFallbackEdidManufacturerId = 0; constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu; std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) { @@ -71,12 +66,8 @@ char getPnpLetter(uint16_t id) { DeviceProductInfo buildDeviceProductInfo(const Edid& edid) { DeviceProductInfo info; - std::copy(edid.displayName.begin(), edid.displayName.end(), info.name.begin()); - info.name[edid.displayName.size()] = '\0'; - - const auto productId = std::to_string(edid.productId); - std::copy(productId.begin(), productId.end(), info.productId.begin()); - info.productId[productId.size()] = '\0'; + info.name.assign(edid.displayName); + info.productId = std::to_string(edid.productId); info.manufacturerPnpId = edid.pnpId; constexpr uint8_t kModelYearFlag = 0xff; @@ -99,8 +90,6 @@ DeviceProductInfo buildDeviceProductInfo(const Edid& edid) { if (edid.cea861Block && edid.cea861Block->hdmiVendorDataBlock) { const auto& address = edid.cea861Block->hdmiVendorDataBlock->physicalAddress; info.relativeAddress = {address.a, address.b, address.c, address.d}; - } else { - info.relativeAddress = DeviceProductInfo::NO_RELATIVE_ADDRESS; } return info; } @@ -132,8 +121,8 @@ Cea861ExtensionBlock parseCea861Block(const byte_view& block) { constexpr uint8_t kVendorSpecificDataBlockTag = 0x3; if (tag == kVendorSpecificDataBlockTag) { - const uint32_t ieeeRegistrationId = - dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16); + const uint32_t ieeeRegistrationId = static_cast<uint32_t>( + dataBlock[1] | (dataBlock[2] << 8) | (dataBlock[3] << 16)); constexpr uint32_t kHdmiIeeeRegistrationId = 0xc03; if (ieeeRegistrationId == kHdmiIeeeRegistrationId) { @@ -158,14 +147,6 @@ Cea861ExtensionBlock parseCea861Block(const byte_view& block) { } // namespace -uint16_t DisplayId::manufacturerId() const { - return static_cast<uint16_t>(value >> 40); -} - -DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) { - return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(modelHash) << 8) | port}; -} - bool isEdid(const DisplayIdentificationData& data) { const uint8_t kMagic[] = {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}; return data.size() >= sizeof(kMagic) && @@ -190,7 +171,7 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { // Plug and play ID encoded as big-endian 16-bit value. const uint16_t manufacturerId = - (edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]; + static_cast<uint16_t>((edid[kManufacturerOffset] << 8) | edid[kManufacturerOffset + 1]); const auto pnpId = getPnpId(manufacturerId); if (!pnpId) { @@ -203,7 +184,8 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { ALOGE("Invalid EDID: product ID is truncated."); return {}; } - const uint16_t productId = edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8); + const uint16_t productId = + static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8)); constexpr size_t kManufactureWeekOffset = 16; if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) { @@ -238,7 +220,6 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { constexpr size_t kDescriptorCount = 4; constexpr size_t kDescriptorLength = 18; - static_assert(kDescriptorLength - kEdidHeaderLength < DeviceProductInfo::TEXT_BUFFER_SIZE); for (size_t i = 0; i < kDescriptorCount; i++) { if (view.size() < kDescriptorLength) { @@ -330,8 +311,8 @@ std::optional<PnpId> getPnpId(uint16_t manufacturerId) { return a && b && c ? std::make_optional(PnpId{a, b, c}) : std::nullopt; } -std::optional<PnpId> getPnpId(DisplayId displayId) { - return getPnpId(displayId.manufacturerId()); +std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) { + return getPnpId(displayId.getManufacturerId()); } std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( @@ -346,21 +327,15 @@ std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( return {}; } - const auto displayId = DisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash); + const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash); return DisplayIdentificationInfo{.id = displayId, .name = std::string(edid->displayName), .deviceProductInfo = buildDeviceProductInfo(*edid)}; } -DisplayId getFallbackDisplayId(uint8_t port) { - return DisplayId::fromEdid(port, kFallbackEdidManufacturerId, 0); -} - -DisplayId getVirtualDisplayId(uint32_t id) { - return DisplayId::fromEdid(0, kVirtualEdidManufacturerId, id); +PhysicalDisplayId getVirtualDisplayId(uint32_t id) { + return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id); } } // namespace android -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h index 4819d1d18b..fbea4e5fe3 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h +++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,38 +24,18 @@ #include <vector> #include <ui/DeviceProductInfo.h> -#include <ui/PhysicalDisplayId.h> +#include <ui/DisplayId.h> #define LEGACY_DISPLAY_TYPE_PRIMARY 0 #define LEGACY_DISPLAY_TYPE_EXTERNAL 1 namespace android { -struct DisplayId { - using Type = PhysicalDisplayId; - Type value; - - uint16_t manufacturerId() const; - - static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash); -}; - -inline bool operator==(DisplayId lhs, DisplayId rhs) { - return lhs.value == rhs.value; -} - -inline bool operator!=(DisplayId lhs, DisplayId rhs) { - return !(lhs == rhs); -} - -inline std::string to_string(DisplayId displayId) { - return std::to_string(displayId.value); -} using DisplayIdentificationData = std::vector<uint8_t>; struct DisplayIdentificationInfo { - DisplayId id; + PhysicalDisplayId id; std::string name; std::optional<DeviceProductInfo> deviceProductInfo; }; @@ -94,23 +74,12 @@ struct Edid { bool isEdid(const DisplayIdentificationData&); std::optional<Edid> parseEdid(const DisplayIdentificationData&); std::optional<PnpId> getPnpId(uint16_t manufacturerId); -std::optional<PnpId> getPnpId(DisplayId); +std::optional<PnpId> getPnpId(PhysicalDisplayId); std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( uint8_t port, const DisplayIdentificationData&); -DisplayId getFallbackDisplayId(uint8_t port); -DisplayId getVirtualDisplayId(uint32_t id); +PhysicalDisplayId getVirtualDisplayId(uint32_t id); } // namespace android -namespace std { - -template <> -struct hash<android::DisplayId> { - size_t operator()(android::DisplayId displayId) const { - return hash<android::DisplayId::Type>()(displayId.value); - } -}; - -} // namespace std diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 4c3b3e502d..14b54cd6ae 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -56,7 +56,7 @@ using ui::Dataspace; * */ -FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId, +FramebufferSurface::FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId, const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth, uint32_t maxHeight) : ConsumerBase(consumer), diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h index a1859f33bb..759943afa3 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h @@ -23,6 +23,7 @@ #include <compositionengine/DisplaySurface.h> #include <compositionengine/impl/HwcBufferCache.h> #include <gui/ConsumerBase.h> +#include <ui/DisplayId.h> #include <ui/Size.h> #include "DisplayIdentification.h" @@ -39,7 +40,7 @@ class HWComposer; class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface { public: - FramebufferSurface(HWComposer& hwc, DisplayId displayId, + FramebufferSurface(HWComposer& hwc, PhysicalDisplayId displayId, const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth, uint32_t maxHeight); @@ -69,7 +70,7 @@ private: status_t nextBuffer(uint32_t& outSlot, sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence, ui::Dataspace& outDataspace); - const DisplayId mDisplayId; + const PhysicalDisplayId mDisplayId; // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of // the device. diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 08559bd939..e6bff04601 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -977,12 +977,6 @@ Error Layer::setZOrder(uint32_t z) return static_cast<Error>(intError); } -Error Layer::setInfo(uint32_t type, uint32_t appId) -{ - auto intError = mComposer.setLayerInfo(mDisplayId, mId, type, appId); - return static_cast<Error>(intError); -} - // Composer HAL 2.3 Error Layer::setColorTransform(const android::mat4& matrix) { if (matrix == mColorMatrix) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 6819ff43d2..89df84b580 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROID_SF_HWC2_H -#define ANDROID_SF_HWC2_H +#pragma once #include <gui/HdrMetadata.h> #include <math/mat4.h> @@ -36,15 +35,16 @@ #include "Hal.h" namespace android { - struct DisplayedFrameStats; - class Fence; - class FloatRect; - class GraphicBuffer; - namespace Hwc2 { - class Composer; - } - class TestableSurfaceFlinger; +class Fence; +class FloatRect; +class GraphicBuffer; +class TestableSurfaceFlinger; +struct DisplayedFrameStats; + +namespace Hwc2 { +class Composer; +} // namespace Hwc2 namespace HWC2 { @@ -61,19 +61,17 @@ namespace hal = android::hardware::graphics::composer::hal; // All calls receive a sequenceId, which will be the value that was supplied to // HWC2::Device::registerCallback(). It's used to help differentiate callbacks // from different hardware composer instances. -class ComposerCallback { - public: - virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId display, - hal::Connection connection) = 0; - virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId display) = 0; - virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp, - std::optional<hal::VsyncPeriodNanos> vsyncPeriod) = 0; - virtual void onVsyncPeriodTimingChangedReceived( - int32_t sequenceId, hal::HWDisplayId display, - const hal::VsyncPeriodChangeTimeline& updatedTimeline) = 0; - virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) = 0; - - virtual ~ComposerCallback() = default; +struct ComposerCallback { + virtual void onHotplugReceived(int32_t sequenceId, hal::HWDisplayId, hal::Connection) = 0; + virtual void onRefreshReceived(int32_t sequenceId, hal::HWDisplayId) = 0; + virtual void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp, + std::optional<hal::VsyncPeriodNanos>) = 0; + virtual void onVsyncPeriodTimingChangedReceived(int32_t sequenceId, hal::HWDisplayId, + const hal::VsyncPeriodChangeTimeline&) = 0; + virtual void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId) = 0; + +protected: + ~ComposerCallback() = default; }; // Convenience C++ class to access per display functions directly. @@ -242,15 +240,14 @@ namespace impl { class Display : public HWC2::Display { public: - Display(android::Hwc2::Composer& composer, - const std::unordered_set<hal::Capability>& capabilities, hal::HWDisplayId id, - hal::DisplayType type); + Display(android::Hwc2::Composer&, const std::unordered_set<hal::Capability>&, hal::HWDisplayId, + hal::DisplayType); ~Display() override; // Required by HWC2 hal::Error acceptChanges() override; hal::Error createLayer(Layer** outLayer) override; - hal::Error destroyLayer(Layer* layer) override; + hal::Error destroyLayer(Layer*) override; hal::Error getActiveConfig(std::shared_ptr<const Config>* outConfig) const override; hal::Error getActiveConfigIndex(int* outIndex) const override; hal::Error getChangedCompositionTypes( @@ -260,8 +257,7 @@ public: int32_t getSupportedPerFrameMetadata() const override; hal::Error getRenderIntents(hal::ColorMode colorMode, std::vector<hal::RenderIntent>* outRenderIntents) const override; - hal::Error getDataspaceSaturationMatrix(hal::Dataspace dataspace, - android::mat4* outMatrix) override; + hal::Error getDataspaceSaturationMatrix(hal::Dataspace, android::mat4* outMatrix) override; // Doesn't call into the HWC2 device, so no errors are possible std::vector<std::shared_ptr<const Config>> getConfigs() const override; @@ -287,11 +283,11 @@ public: hal::Error setClientTarget(uint32_t slot, const android::sp<android::GraphicBuffer>& target, const android::sp<android::Fence>& acquireFence, hal::Dataspace dataspace) override; - hal::Error setColorMode(hal::ColorMode mode, hal::RenderIntent renderIntent) override; + hal::Error setColorMode(hal::ColorMode, hal::RenderIntent) override; hal::Error setColorTransform(const android::mat4& matrix, hal::ColorTransform hint) override; - hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>& buffer, + hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>&, const android::sp<android::Fence>& releaseFence) override; - hal::Error setPowerMode(hal::PowerMode mode) override; + hal::Error setPowerMode(hal::PowerMode) override; hal::Error setVsyncEnabled(hal::Vsync enabled) override; hal::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override; hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests, @@ -319,13 +315,13 @@ public: virtual bool isVsyncPeriodSwitchSupported() const override; private: - int32_t getAttribute(hal::HWConfigId configId, hal::Attribute attribute); - void loadConfig(hal::HWConfigId configId); + int32_t getAttribute(hal::HWConfigId, hal::Attribute); + void loadConfig(hal::HWConfigId); void loadConfigs(); // This may fail (and return a null pointer) if no layer with this ID exists // on this display - Layer* getLayerById(hal::HWLayerId id) const; + Layer* getLayerById(hal::HWLayerId) const; friend android::TestableSurfaceFlinger; @@ -380,7 +376,6 @@ public: [[clang::warn_unused_result]] virtual hal::Error setVisibleRegion( const android::Region& region) = 0; [[clang::warn_unused_result]] virtual hal::Error setZOrder(uint32_t z) = 0; - [[clang::warn_unused_result]] virtual hal::Error setInfo(uint32_t type, uint32_t appId) = 0; // Composer HAL 2.3 [[clang::warn_unused_result]] virtual hal::Error setColorTransform( @@ -422,7 +417,6 @@ public: hal::Error setTransform(hal::Transform transform) override; hal::Error setVisibleRegion(const android::Region& region) override; hal::Error setZOrder(uint32_t z) override; - hal::Error setInfo(uint32_t type, uint32_t appId) override; // Composer HAL 2.3 hal::Error setColorTransform(const android::mat4& matrix) override; @@ -454,5 +448,3 @@ private: } // namespace impl } // namespace HWC2 } // namespace android - -#endif // ANDROID_SF_HWC2_H diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 7a2f0f34ee..1548d18652 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -26,6 +26,7 @@ #include "HWComposer.h" +#include <android-base/properties.h> #include <compositionengine/Output.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> @@ -38,6 +39,7 @@ #include "../Layer.h" // needed only for debugging #include "../Promise.h" #include "../SurfaceFlinger.h" +#include "../SurfaceFlingerProperties.h" #include "ComposerHal.h" #include "HWC2.h" @@ -143,12 +145,13 @@ HWComposer::~HWComposer() = default; namespace impl { -HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) { -} +HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) + : mComposer(std::move(composer)), + mUpdateDeviceProductInfoOnHotplugReconnect( + android::sysprop::update_device_product_info_on_hotplug_reconnect(false)) {} HWComposer::HWComposer(const std::string& composerServiceName) - : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) { -} + : HWComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {} HWComposer::~HWComposer() { mDisplayData.clear(); @@ -186,7 +189,7 @@ bool HWComposer::hasCapability(hal::Capability capability) const { return mCapabilities.count(capability) > 0; } -bool HWComposer::hasDisplayCapability(DisplayId displayId, +bool HWComposer::hasDisplayCapability(HalDisplayId displayId, hal::DisplayCapability capability) const { RETURN_IF_INVALID_DISPLAY(displayId, false); return mDisplayData.at(displayId).hwcDisplay->getCapabilities().count(capability) > 0; @@ -204,6 +207,10 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hal::HWDisplayId } } +bool HWComposer::updatesDeviceProductInfoOnHotplugReconnect() const { + return mUpdateDeviceProductInfoOnHotplugReconnect; +} + bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) { const auto displayId = toPhysicalDisplayId(hwcDisplayId); if (!displayId) { @@ -214,10 +221,8 @@ bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) { RETURN_IF_INVALID_DISPLAY(*displayId, false); auto& displayData = mDisplayData[*displayId]; - if (displayData.isVirtual) { - LOG_DISPLAY_ERROR(*displayId, "Invalid operation on virtual display"); - return false; - } + LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s", + __FUNCTION__, to_string(*displayId).c_str()); { std::lock_guard lock(displayData.lastHwVsyncLock); @@ -244,11 +249,6 @@ bool HWComposer::onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) { std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, ui::PixelFormat* format) { - if (mRemainingHwcVirtualDisplays == 0) { - ALOGE("%s: No remaining virtual displays", __FUNCTION__); - return {}; - } - if (SurfaceFlinger::maxVirtualDisplaySize != 0 && (width > SurfaceFlinger::maxVirtualDisplaySize || height > SurfaceFlinger::maxVirtualDisplaySize)) { @@ -256,35 +256,33 @@ std::optional<DisplayId> HWComposer::allocateVirtualDisplay(uint32_t width, uint height, SurfaceFlinger::maxVirtualDisplaySize); return {}; } + + const auto displayId = mVirtualIdGenerator.nextId(); + if (!displayId) { + ALOGE("%s: No remaining virtual displays", __FUNCTION__); + return {}; + } + hal::HWDisplayId hwcDisplayId = 0; const auto error = static_cast<hal::Error>( mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId)); if (error != hal::Error::NONE) { ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__); + mVirtualIdGenerator.markUnused(*displayId); return {}; } auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId, hal::DisplayType::VIRTUAL); display->setConnected(true); - - DisplayId displayId; - if (mFreeVirtualDisplayIds.empty()) { - displayId = getVirtualDisplayId(mNextVirtualDisplayId++); - } else { - displayId = *mFreeVirtualDisplayIds.begin(); - mFreeVirtualDisplayIds.erase(displayId); - } - - auto& displayData = mDisplayData[displayId]; + auto& displayData = mDisplayData[*displayId]; displayData.hwcDisplay = std::move(display); displayData.isVirtual = true; - - --mRemainingHwcVirtualDisplays; return displayId; } -void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) { +void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, + PhysicalDisplayId displayId) { if (!mInternalHwcDisplayId) { mInternalHwcDisplayId = hwcDisplayId; } else if (mInternalHwcDisplayId != hwcDisplayId && !mExternalHwcDisplayId) { @@ -300,7 +298,7 @@ void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayI mPhysicalDisplayIdMap[hwcDisplayId] = displayId; } -HWC2::Layer* HWComposer::createLayer(DisplayId displayId) { +HWC2::Layer* HWComposer::createLayer(HalDisplayId displayId) { RETURN_IF_INVALID_DISPLAY(displayId, nullptr); HWC2::Layer* layer; @@ -309,14 +307,14 @@ HWC2::Layer* HWComposer::createLayer(DisplayId displayId) { return layer; } -void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) { +void HWComposer::destroyLayer(HalDisplayId displayId, HWC2::Layer* layer) { RETURN_IF_INVALID_DISPLAY(displayId); auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer); RETURN_IF_HWC_ERROR(error, displayId); } -nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const { +nsecs_t HWComposer::getRefreshTimestamp(PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, 0); const auto& displayData = mDisplayData.at(displayId); // this returns the last refresh timestamp. @@ -328,13 +326,13 @@ nsecs_t HWComposer::getRefreshTimestamp(DisplayId displayId) const { return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos); } -bool HWComposer::isConnected(DisplayId displayId) const { +bool HWComposer::isConnected(PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, false); return mDisplayData.at(displayId).hwcDisplay->isConnected(); } std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs( - DisplayId displayId) const { + PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, {}); const auto& displayData = mDisplayData.at(displayId); @@ -348,7 +346,7 @@ std::vector<std::shared_ptr<const HWC2::Display::Config>> HWComposer::getConfigs } std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig( - DisplayId displayId) const { + PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, nullptr); std::shared_ptr<const HWC2::Display::Config> config; @@ -370,7 +368,7 @@ std::shared_ptr<const HWC2::Display::Config> HWComposer::getActiveConfig( // Composer 2.4 -DisplayConnectionType HWComposer::getDisplayConnectionType(DisplayId displayId) const { +DisplayConnectionType HWComposer::getDisplayConnectionType(PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, DisplayConnectionType::Internal); const auto& hwcDisplay = mDisplayData.at(displayId).hwcDisplay; @@ -385,12 +383,12 @@ DisplayConnectionType HWComposer::getDisplayConnectionType(DisplayId displayId) return type; } -bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const { +bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, false); return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported(); } -nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const { +nsecs_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, 0); nsecs_t vsyncPeriodNanos; @@ -399,7 +397,7 @@ nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const { return vsyncPeriodNanos; } -int HWComposer::getActiveConfigIndex(DisplayId displayId) const { +int HWComposer::getActiveConfigIndex(PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, -1); int index; @@ -419,7 +417,7 @@ int HWComposer::getActiveConfigIndex(DisplayId displayId) const { return index; } -std::vector<ui::ColorMode> HWComposer::getColorModes(DisplayId displayId) const { +std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, {}); std::vector<ui::ColorMode> modes; @@ -428,7 +426,7 @@ std::vector<ui::ColorMode> HWComposer::getColorModes(DisplayId displayId) const return modes; } -status_t HWComposer::setActiveColorMode(DisplayId displayId, ui::ColorMode mode, +status_t HWComposer::setActiveColorMode(PhysicalDisplayId displayId, ui::ColorMode mode, ui::RenderIntent renderIntent) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); @@ -442,14 +440,12 @@ status_t HWComposer::setActiveColorMode(DisplayId displayId, ui::ColorMode mode, return NO_ERROR; } -void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) { +void HWComposer::setVsyncEnabled(PhysicalDisplayId displayId, hal::Vsync enabled) { RETURN_IF_INVALID_DISPLAY(displayId); auto& displayData = mDisplayData[displayId]; - if (displayData.isVirtual) { - LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display"); - return; - } + LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s", + __FUNCTION__, to_string(displayId).c_str()); // NOTE: we use our own internal lock here because we have to call // into the HWC with the lock held, and we want to make sure @@ -470,7 +466,7 @@ void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) { ATRACE_INT(tag.c_str(), enabled == hal::Vsync::ENABLE ? 1 : 0); } -status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot, +status_t HWComposer::setClientTarget(HalDisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, ui::Dataspace dataspace) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); @@ -483,7 +479,7 @@ status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot, } status_t HWComposer::getDeviceCompositionChanges( - DisplayId displayId, bool frameUsesClientComposition, + HalDisplayId displayId, bool frameUsesClientComposition, std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) { ATRACE_CALL(); @@ -553,12 +549,12 @@ status_t HWComposer::getDeviceCompositionChanges( return NO_ERROR; } -sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const { +sp<Fence> HWComposer::getPresentFence(HalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE); return mDisplayData.at(displayId).lastPresentFence; } -sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const { +sp<Fence> HWComposer::getLayerReleaseFence(HalDisplayId displayId, HWC2::Layer* layer) const { RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE); const auto& displayFences = mDisplayData.at(displayId).releaseFences; auto fence = displayFences.find(layer); @@ -569,7 +565,7 @@ sp<Fence> HWComposer::getLayerReleaseFence(DisplayId displayId, HWC2::Layer* lay return fence->second; } -status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) { +status_t HWComposer::presentAndGetReleaseFences(HalDisplayId displayId) { ATRACE_CALL(); RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); @@ -597,14 +593,12 @@ status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) { return NO_ERROR; } -status_t HWComposer::setPowerMode(DisplayId displayId, hal::PowerMode mode) { +status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto& displayData = mDisplayData[displayId]; - if (displayData.isVirtual) { - LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display"); - return INVALID_OPERATION; - } + LOG_FATAL_IF(displayData.isVirtual, "%s: Invalid operation on virtual display with ID %s", + __FUNCTION__, to_string(displayId).c_str()); if (mode == hal::PowerMode::OFF) { setVsyncEnabled(displayId, hal::Vsync::DISABLE); @@ -653,7 +647,8 @@ status_t HWComposer::setPowerMode(DisplayId displayId, hal::PowerMode mode) { } status_t HWComposer::setActiveConfigWithConstraints( - DisplayId displayId, size_t configId, const hal::VsyncPeriodChangeConstraints& constraints, + PhysicalDisplayId displayId, size_t configId, + const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline* outTimeline) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); @@ -670,7 +665,7 @@ status_t HWComposer::setActiveConfigWithConstraints( return NO_ERROR; } -status_t HWComposer::setColorTransform(DisplayId displayId, const mat4& transform) { +status_t HWComposer::setColorTransform(HalDisplayId displayId, const mat4& transform) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); auto& displayData = mDisplayData[displayId]; @@ -683,15 +678,14 @@ status_t HWComposer::setColorTransform(DisplayId displayId, const mat4& transfor return NO_ERROR; } -void HWComposer::disconnectDisplay(DisplayId displayId) { +void HWComposer::disconnectDisplay(HalDisplayId displayId) { RETURN_IF_INVALID_DISPLAY(displayId); auto& displayData = mDisplayData[displayId]; // If this was a virtual display, add its slot back for reuse by future // virtual displays if (displayData.isVirtual) { - mFreeVirtualDisplayIds.insert(displayId); - ++mRemainingHwcVirtualDisplays; + mVirtualIdGenerator.markUnused(*HalVirtualDisplayId::tryCast(displayId)); } const auto hwcDisplayId = displayData.hwcDisplay->getId(); @@ -706,27 +700,25 @@ void HWComposer::disconnectDisplay(DisplayId displayId) { mDisplayData.erase(displayId); } -status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence, +status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto& displayData = mDisplayData[displayId]; - if (!displayData.isVirtual) { - LOG_DISPLAY_ERROR(displayId, "Invalid operation on physical display"); - return INVALID_OPERATION; - } + LOG_FATAL_IF(!displayData.isVirtual, "%s: Invalid operation on physical display with ID %s", + __FUNCTION__, to_string(displayId).c_str()); auto error = displayData.hwcDisplay->setOutputBuffer(buffer, acquireFence); RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); return NO_ERROR; } -void HWComposer::clearReleaseFences(DisplayId displayId) { +void HWComposer::clearReleaseFences(HalDisplayId displayId) { RETURN_IF_INVALID_DISPLAY(displayId); mDisplayData[displayId].releaseFences.clear(); } -status_t HWComposer::getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) { +status_t HWComposer::getHdrCapabilities(HalDisplayId displayId, HdrCapabilities* outCapabilities) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; @@ -735,12 +727,12 @@ status_t HWComposer::getHdrCapabilities(DisplayId displayId, HdrCapabilities* ou return NO_ERROR; } -int32_t HWComposer::getSupportedPerFrameMetadata(DisplayId displayId) const { +int32_t HWComposer::getSupportedPerFrameMetadata(HalDisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, 0); return mDisplayData.at(displayId).hwcDisplay->getSupportedPerFrameMetadata(); } -std::vector<ui::RenderIntent> HWComposer::getRenderIntents(DisplayId displayId, +std::vector<ui::RenderIntent> HWComposer::getRenderIntents(HalDisplayId displayId, ui::ColorMode colorMode) const { RETURN_IF_INVALID_DISPLAY(displayId, {}); @@ -750,7 +742,7 @@ std::vector<ui::RenderIntent> HWComposer::getRenderIntents(DisplayId displayId, return renderIntents; } -mat4 HWComposer::getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) { +mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Dataspace dataspace) { RETURN_IF_INVALID_DISPLAY(displayId, {}); mat4 matrix; @@ -760,7 +752,7 @@ mat4 HWComposer::getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace return matrix; } -status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId, +status_t HWComposer::getDisplayedContentSamplingAttributes(HalDisplayId displayId, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, uint8_t* outComponentMask) { @@ -774,7 +766,7 @@ status_t HWComposer::getDisplayedContentSamplingAttributes(DisplayId displayId, return NO_ERROR; } -status_t HWComposer::setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled, +status_t HWComposer::setDisplayContentSamplingEnabled(HalDisplayId displayId, bool enabled, uint8_t componentMask, uint64_t maxFrames) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = @@ -788,7 +780,7 @@ status_t HWComposer::setDisplayContentSamplingEnabled(DisplayId displayId, bool return NO_ERROR; } -status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, +status_t HWComposer::getDisplayedContentSample(HalDisplayId displayId, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = @@ -798,7 +790,8 @@ status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t max return NO_ERROR; } -std::future<status_t> HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) { +std::future<status_t> HWComposer::setDisplayBrightness(PhysicalDisplayId displayId, + float brightness) { RETURN_IF_INVALID_DISPLAY(displayId, promise::yield<status_t>(BAD_INDEX)); auto& display = mDisplayData[displayId].hwcDisplay; @@ -815,11 +808,7 @@ std::future<status_t> HWComposer::setDisplayBrightness(DisplayId displayId, floa }); } -bool HWComposer::isUsingVrComposer() const { - return getComposer()->isUsingVrComposer(); -} - -status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) { +status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on); if (error == hal::Error::UNSUPPORTED) { @@ -833,7 +822,7 @@ status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) { } status_t HWComposer::getSupportedContentTypes( - DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) { + PhysicalDisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes); @@ -843,7 +832,7 @@ status_t HWComposer::getSupportedContentTypes( return NO_ERROR; } -status_t HWComposer::setContentType(DisplayId displayId, hal::ContentType contentType) { +status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentType contentType) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType); if (error == hal::Error::UNSUPPORTED) { @@ -865,7 +854,8 @@ void HWComposer::dump(std::string& result) const { result.append(mComposer->dumpDebugInfo()); } -std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const { +std::optional<PhysicalDisplayId> HWComposer::toPhysicalDisplayId( + hal::HWDisplayId hwcDisplayId) const { if (const auto it = mPhysicalDisplayIdMap.find(hwcDisplayId); it != mPhysicalDisplayIdMap.end()) { return it->second; @@ -873,7 +863,8 @@ std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hal::HWDisplayId hwcDis return {}; } -std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(DisplayId displayId) const { +std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId( + PhysicalDisplayId displayId) const { if (const auto it = mDisplayData.find(displayId); it != mDisplayData.end() && !it->second.isVirtual) { return it->second.hwcDisplay->getId(); @@ -883,11 +874,6 @@ std::optional<hal::HWDisplayId> HWComposer::fromPhysicalDisplayId(DisplayId disp bool HWComposer::shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId, bool hasDisplayIdentificationData) const { - if (isUsingVrComposer() && mInternalHwcDisplayId) { - ALOGE("Ignoring connection of external display %" PRIu64 " in VR mode", hwcDisplayId); - return true; - } - if (mHasMultiDisplaySupport && !hasDisplayIdentificationData) { ALOGE("Ignoring connection of display %" PRIu64 " without identification data", hwcDisplayId); @@ -909,6 +895,16 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( info = DisplayIdentificationInfo{.id = *displayId, .name = std::string(), .deviceProductInfo = std::nullopt}; + if (mUpdateDeviceProductInfoOnHotplugReconnect) { + uint8_t port; + DisplayIdentificationData data; + getDisplayIdentificationData(hwcDisplayId, &port, &data); + if (auto newInfo = parseDisplayIdentificationData(port, data)) { + info->deviceProductInfo = std::move(newInfo->deviceProductInfo); + } else { + ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId); + } + } } else { uint8_t port; DisplayIdentificationData data; @@ -937,7 +933,7 @@ std::optional<DisplayIdentificationInfo> HWComposer::onHotplugConnect( port = isPrimary ? LEGACY_DISPLAY_TYPE_PRIMARY : LEGACY_DISPLAY_TYPE_EXTERNAL; } - return DisplayIdentificationInfo{.id = getFallbackDisplayId(port), + return DisplayIdentificationInfo{.id = PhysicalDisplayId::fromPort(port), .name = isPrimary ? "Internal display" : "External display", .deviceProductInfo = std::nullopt}; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index c355ebd4a6..d8af5bf449 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -38,6 +38,7 @@ #include <utils/StrongPointer.h> #include <utils/Timers.h> +#include "DisplayIdGenerator.h" #include "DisplayIdentification.h" #include "HWC2.h" #include "Hal.h" @@ -64,6 +65,8 @@ struct KnownHWCGenericLayerMetadata { const uint32_t id; }; +// See the comment for SurfaceFlinger::getHwComposer for the thread safety rules for accessing +// this class. class HWComposer { public: struct DeviceRequestedChanges { @@ -82,23 +85,22 @@ public: virtual void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) = 0; - virtual bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort, + virtual bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort, DisplayIdentificationData* outData) const = 0; - virtual bool hasCapability(hal::Capability capability) const = 0; - virtual bool hasDisplayCapability(DisplayId displayId, - hal::DisplayCapability capability) const = 0; + virtual bool hasCapability(hal::Capability) const = 0; + virtual bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const = 0; // Attempts to allocate a virtual display and returns its ID if created on the HWC device. virtual std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height, - ui::PixelFormat* format) = 0; + ui::PixelFormat*) = 0; - virtual void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) = 0; + virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0; // Attempts to create a new layer on this display - virtual HWC2::Layer* createLayer(DisplayId displayId) = 0; + virtual HWC2::Layer* createLayer(HalDisplayId) = 0; // Destroy a previously created layer - virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0; + virtual void destroyLayer(HalDisplayId, HWC2::Layer*) = 0; // Gets any required composition change requests from the HWC device. // @@ -108,107 +110,103 @@ public: // with fewer handshakes, but this does not work if client composition is // expected. virtual status_t getDeviceCompositionChanges( - DisplayId, bool frameUsesClientComposition, + HalDisplayId, bool frameUsesClientComposition, std::optional<DeviceRequestedChanges>* outChanges) = 0; - virtual status_t setClientTarget(DisplayId displayId, uint32_t slot, - const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, - ui::Dataspace dataspace) = 0; + virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence, + const sp<GraphicBuffer>& target, ui::Dataspace) = 0; // Present layers to the display and read releaseFences. - virtual status_t presentAndGetReleaseFences(DisplayId displayId) = 0; + virtual status_t presentAndGetReleaseFences(HalDisplayId) = 0; // set power mode - virtual status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) = 0; + virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0; // Sets a color transform to be applied to the result of composition - virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0; + virtual status_t setColorTransform(HalDisplayId, const mat4& transform) = 0; - // reset state when an external, non-virtual display is disconnected - virtual void disconnectDisplay(DisplayId displayId) = 0; + // reset state when a display is disconnected + virtual void disconnectDisplay(HalDisplayId) = 0; // get the present fence received from the last call to present. - virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0; + virtual sp<Fence> getPresentFence(HalDisplayId) const = 0; // Get last release fence for the given layer - virtual sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const = 0; + virtual sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const = 0; // Set the output buffer and acquire fence for a virtual display. - // Returns INVALID_OPERATION if displayId is not a virtual display. - virtual status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence, + virtual status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) = 0; // After SurfaceFlinger has retrieved the release fences for all the frames, // it can call this to clear the shared pointers in the release fence map - virtual void clearReleaseFences(DisplayId displayId) = 0; + virtual void clearReleaseFences(HalDisplayId) = 0; // Fetches the HDR capabilities of the given display - virtual status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) = 0; + virtual status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) = 0; - virtual int32_t getSupportedPerFrameMetadata(DisplayId displayId) const = 0; + virtual int32_t getSupportedPerFrameMetadata(HalDisplayId) const = 0; // Returns the available RenderIntent of the given display. - virtual std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId, - ui::ColorMode colorMode) const = 0; + virtual std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const = 0; - virtual mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) = 0; + virtual mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) = 0; // Returns the attributes of the color sampling engine. - virtual status_t getDisplayedContentSamplingAttributes(DisplayId displayId, - ui::PixelFormat* outFormat, + virtual status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, uint8_t* outComponentMask) = 0; - virtual status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled, + virtual status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled, uint8_t componentMask, uint64_t maxFrames) = 0; - virtual status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, - uint64_t timestamp, + virtual status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) = 0; // Sets the brightness of a display. - virtual std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) = 0; + virtual std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) = 0; // Events handling --------------------------------------------------------- // Returns stable display ID (and display name on connection of new or previously disconnected // display), or std::nullopt if hotplug event was ignored. // This function is called from SurfaceFlinger. - virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId, - hal::Connection connection) = 0; + virtual std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, + hal::Connection) = 0; - virtual bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) = 0; - virtual void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) = 0; + // If true we'll update the DeviceProductInfo on subsequent hotplug connected events. + // TODO(b/157555476): Remove when the framework has proper support for headless mode + virtual bool updatesDeviceProductInfoOnHotplugReconnect() const = 0; - virtual nsecs_t getRefreshTimestamp(DisplayId displayId) const = 0; - virtual bool isConnected(DisplayId displayId) const = 0; + virtual bool onVsync(hal::HWDisplayId, int64_t timestamp) = 0; + virtual void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) = 0; + + virtual nsecs_t getRefreshTimestamp(PhysicalDisplayId) const = 0; + virtual bool isConnected(PhysicalDisplayId) const = 0; // Non-const because it can update configMap inside of mDisplayData virtual std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs( - DisplayId displayId) const = 0; + PhysicalDisplayId) const = 0; virtual std::shared_ptr<const HWC2::Display::Config> getActiveConfig( - DisplayId displayId) const = 0; - virtual int getActiveConfigIndex(DisplayId displayId) const = 0; - - virtual std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const = 0; + PhysicalDisplayId) const = 0; + virtual int getActiveConfigIndex(PhysicalDisplayId) const = 0; - virtual status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode, - ui::RenderIntent renderIntent) = 0; + virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0; - virtual bool isUsingVrComposer() const = 0; + virtual status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode mode, + ui::RenderIntent) = 0; // Composer 2.4 - virtual DisplayConnectionType getDisplayConnectionType(DisplayId) const = 0; - virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0; - virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0; + virtual DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0; + virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0; + virtual nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const = 0; virtual status_t setActiveConfigWithConstraints( - DisplayId displayId, size_t configId, - const hal::VsyncPeriodChangeConstraints& constraints, + PhysicalDisplayId, size_t configId, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline* outTimeline) = 0; - virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0; + virtual status_t setAutoLowLatencyMode(PhysicalDisplayId, bool on) = 0; virtual status_t getSupportedContentTypes( - DisplayId displayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0; - virtual status_t setContentType(DisplayId displayId, hal::ContentType contentType) = 0; + PhysicalDisplayId, std::vector<hal::ContentType>* outSupportedContentTypes) = 0; + virtual status_t setContentType(PhysicalDisplayId, hal::ContentType) = 0; virtual const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const = 0; @@ -221,8 +219,8 @@ public: virtual std::optional<hal::HWDisplayId> getInternalHwcDisplayId() const = 0; virtual std::optional<hal::HWDisplayId> getExternalHwcDisplayId() const = 0; - virtual std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const = 0; - virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const = 0; + virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0; + virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0; }; namespace impl { @@ -236,118 +234,112 @@ public: void setConfiguration(HWC2::ComposerCallback* callback, int32_t sequenceId) override; - bool getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort, + bool getDisplayIdentificationData(hal::HWDisplayId, uint8_t* outPort, DisplayIdentificationData* outData) const override; - bool hasCapability(hal::Capability capability) const override; - bool hasDisplayCapability(DisplayId displayId, - hal::DisplayCapability capability) const override; + bool hasCapability(hal::Capability) const override; + bool hasDisplayCapability(HalDisplayId, hal::DisplayCapability) const override; // Attempts to allocate a virtual display and returns its ID if created on the HWC device. std::optional<DisplayId> allocateVirtualDisplay(uint32_t width, uint32_t height, - ui::PixelFormat* format) override; + ui::PixelFormat*) override; // Called from SurfaceFlinger, when the state for a new physical display needs to be recreated. - void allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, DisplayId displayId) override; + void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override; // Attempts to create a new layer on this display - HWC2::Layer* createLayer(DisplayId displayId) override; + HWC2::Layer* createLayer(HalDisplayId) override; // Destroy a previously created layer - void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override; + void destroyLayer(HalDisplayId, HWC2::Layer*) override; status_t getDeviceCompositionChanges( - DisplayId, bool frameUsesClientComposition, + HalDisplayId, bool frameUsesClientComposition, std::optional<DeviceRequestedChanges>* outChanges) override; - status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence, - const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override; + status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence, + const sp<GraphicBuffer>& target, ui::Dataspace) override; // Present layers to the display and read releaseFences. - status_t presentAndGetReleaseFences(DisplayId displayId) override; + status_t presentAndGetReleaseFences(HalDisplayId) override; // set power mode - status_t setPowerMode(DisplayId displayId, hal::PowerMode mode) override; + status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override; // Sets a color transform to be applied to the result of composition - status_t setColorTransform(DisplayId displayId, const mat4& transform) override; + status_t setColorTransform(HalDisplayId, const mat4& transform) override; - // reset state when an external, non-virtual display is disconnected - void disconnectDisplay(DisplayId displayId) override; + // reset state when a display is disconnected + void disconnectDisplay(HalDisplayId) override; // get the present fence received from the last call to present. - sp<Fence> getPresentFence(DisplayId displayId) const override; + sp<Fence> getPresentFence(HalDisplayId) const override; // Get last release fence for the given layer - sp<Fence> getLayerReleaseFence(DisplayId displayId, HWC2::Layer* layer) const override; + sp<Fence> getLayerReleaseFence(HalDisplayId, HWC2::Layer*) const override; // Set the output buffer and acquire fence for a virtual display. - // Returns INVALID_OPERATION if displayId is not a virtual display. - status_t setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence, + status_t setOutputBuffer(HalVirtualDisplayId, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) override; // After SurfaceFlinger has retrieved the release fences for all the frames, // it can call this to clear the shared pointers in the release fence map - void clearReleaseFences(DisplayId displayId) override; + void clearReleaseFences(HalDisplayId) override; // Fetches the HDR capabilities of the given display - status_t getHdrCapabilities(DisplayId displayId, HdrCapabilities* outCapabilities) override; + status_t getHdrCapabilities(HalDisplayId, HdrCapabilities* outCapabilities) override; - int32_t getSupportedPerFrameMetadata(DisplayId displayId) const override; + int32_t getSupportedPerFrameMetadata(HalDisplayId) const override; // Returns the available RenderIntent of the given display. - std::vector<ui::RenderIntent> getRenderIntents(DisplayId displayId, - ui::ColorMode colorMode) const override; + std::vector<ui::RenderIntent> getRenderIntents(HalDisplayId, ui::ColorMode) const override; - mat4 getDataspaceSaturationMatrix(DisplayId displayId, ui::Dataspace dataspace) override; + mat4 getDataspaceSaturationMatrix(HalDisplayId, ui::Dataspace) override; // Returns the attributes of the color sampling engine. - status_t getDisplayedContentSamplingAttributes(DisplayId displayId, ui::PixelFormat* outFormat, + status_t getDisplayedContentSamplingAttributes(HalDisplayId, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, uint8_t* outComponentMask) override; - status_t setDisplayContentSamplingEnabled(DisplayId displayId, bool enabled, - uint8_t componentMask, uint64_t maxFrames) override; - status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp, + status_t setDisplayContentSamplingEnabled(HalDisplayId, bool enabled, uint8_t componentMask, + uint64_t maxFrames) override; + status_t getDisplayedContentSample(HalDisplayId, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) override; - std::future<status_t> setDisplayBrightness(DisplayId displayId, float brightness) override; + std::future<status_t> setDisplayBrightness(PhysicalDisplayId, float brightness) override; // Events handling --------------------------------------------------------- - // Returns stable display ID (and display name on connection of new or previously disconnected + // Returns PhysicalDisplayId (and display name on connection of new or previously disconnected // display), or std::nullopt if hotplug event was ignored. - std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId hwcDisplayId, - hal::Connection connection) override; + std::optional<DisplayIdentificationInfo> onHotplug(hal::HWDisplayId, hal::Connection) override; - bool onVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp) override; - void setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) override; + bool updatesDeviceProductInfoOnHotplugReconnect() const override; - nsecs_t getRefreshTimestamp(DisplayId displayId) const override; - bool isConnected(DisplayId displayId) const override; + bool onVsync(hal::HWDisplayId, int64_t timestamp) override; + void setVsyncEnabled(PhysicalDisplayId, hal::Vsync enabled) override; + + nsecs_t getRefreshTimestamp(PhysicalDisplayId) const override; + bool isConnected(PhysicalDisplayId) const override; // Non-const because it can update configMap inside of mDisplayData std::vector<std::shared_ptr<const HWC2::Display::Config>> getConfigs( - DisplayId displayId) const override; - - std::shared_ptr<const HWC2::Display::Config> getActiveConfig( - DisplayId displayId) const override; - int getActiveConfigIndex(DisplayId displayId) const override; + PhysicalDisplayId) const override; - std::vector<ui::ColorMode> getColorModes(DisplayId displayId) const override; + std::shared_ptr<const HWC2::Display::Config> getActiveConfig(PhysicalDisplayId) const override; + int getActiveConfigIndex(PhysicalDisplayId) const override; - status_t setActiveColorMode(DisplayId displayId, ui::ColorMode mode, - ui::RenderIntent renderIntent) override; + std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override; - bool isUsingVrComposer() const override; + status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override; // Composer 2.4 - DisplayConnectionType getDisplayConnectionType(DisplayId) const override; - bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override; - nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override; - status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId, - const hal::VsyncPeriodChangeConstraints& constraints, + DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; + bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override; + nsecs_t getDisplayVsyncPeriod(PhysicalDisplayId) const override; + status_t setActiveConfigWithConstraints(PhysicalDisplayId, size_t configId, + const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline* outTimeline) override; - status_t setAutoLowLatencyMode(DisplayId displayId, bool) override; - status_t getSupportedContentTypes(DisplayId displayId, std::vector<hal::ContentType>*) override; - status_t setContentType(DisplayId displayId, hal::ContentType) override; + status_t setAutoLowLatencyMode(PhysicalDisplayId, bool) override; + status_t getSupportedContentTypes(PhysicalDisplayId, std::vector<hal::ContentType>*) override; + status_t setContentType(PhysicalDisplayId, hal::ContentType) override; const std::unordered_map<std::string, bool>& getSupportedLayerGenericMetadata() const override; @@ -364,17 +356,16 @@ public: return mExternalHwcDisplayId; } - std::optional<DisplayId> toPhysicalDisplayId(hal::HWDisplayId hwcDisplayId) const override; - std::optional<hal::HWDisplayId> fromPhysicalDisplayId(DisplayId displayId) const override; + std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const override; + std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const override; private: // For unit tests friend TestableSurfaceFlinger; - std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId hwcDisplayId); - std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId hwcDisplayId); - bool shouldIgnoreHotplugConnect(hal::HWDisplayId hwcDisplayId, - bool hasDisplayIdentificationData) const; + std::optional<DisplayIdentificationInfo> onHotplugConnect(hal::HWDisplayId); + std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId); + bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const; void loadCapabilities(); void loadLayerMetadataSupport(); @@ -402,21 +393,21 @@ private: nsecs_t lastHwVsync GUARDED_BY(lastHwVsyncLock) = 0; }; - std::unordered_map<DisplayId, DisplayData> mDisplayData; + std::unordered_map<HalDisplayId, DisplayData> mDisplayData; std::unique_ptr<android::Hwc2::Composer> mComposer; std::unordered_set<hal::Capability> mCapabilities; std::unordered_map<std::string, bool> mSupportedLayerGenericMetadata; bool mRegisteredCallback = false; - std::unordered_map<hal::HWDisplayId, DisplayId> mPhysicalDisplayIdMap; + std::unordered_map<hal::HWDisplayId, PhysicalDisplayId> mPhysicalDisplayIdMap; std::optional<hal::HWDisplayId> mInternalHwcDisplayId; std::optional<hal::HWDisplayId> mExternalHwcDisplayId; bool mHasMultiDisplaySupport = false; - std::unordered_set<DisplayId> mFreeVirtualDisplayIds; - uint32_t mNextVirtualDisplayId = 0; - uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()}; + RandomDisplayIdGenerator<HalVirtualDisplayId> mVirtualIdGenerator{getMaxVirtualDisplayCount()}; + + const bool mUpdateDeviceProductInfoOnHotplugReconnect; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index fba3261388..247ee23fa7 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -57,8 +57,7 @@ static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::Comp } } -VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, - const std::optional<DisplayId>& displayId, +VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId, const sp<IGraphicBufferProducer>& sink, const sp<IGraphicBufferProducer>& bqProducer, const sp<IGraphicBufferConsumer>& bqConsumer, @@ -125,7 +124,7 @@ VirtualDisplaySurface::~VirtualDisplaySurface() { } status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) { - if (!mDisplayId) { + if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return NO_ERROR; } @@ -139,7 +138,7 @@ status_t VirtualDisplaySurface::beginFrame(bool mustRecompose) { } status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { - if (!mDisplayId) { + if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return NO_ERROR; } @@ -187,7 +186,7 @@ status_t VirtualDisplaySurface::prepareFrame(CompositionType compositionType) { } status_t VirtualDisplaySurface::advanceFrame() { - if (!mDisplayId) { + if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return NO_ERROR; } @@ -219,9 +218,11 @@ status_t VirtualDisplaySurface::advanceFrame() { mFbProducerSlot, fbBuffer.get(), mOutputProducerSlot, outBuffer.get()); + const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); + LOG_FATAL_IF(!halDisplayId); // At this point we know the output buffer acquire fence, // so update HWC state with it. - mHwc.setOutputBuffer(*mDisplayId, mOutputFence, outBuffer); + mHwc.setOutputBuffer(*halDisplayId, mOutputFence, outBuffer); status_t result = NO_ERROR; if (fbBuffer != nullptr) { @@ -230,7 +231,7 @@ status_t VirtualDisplaySurface::advanceFrame() { mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, &hwcSlot, &hwcBuffer); // TODO: Correctly propagate the dataspace from GL composition - result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer, + result = mHwc.setClientTarget(*halDisplayId, hwcSlot, mFbFence, hwcBuffer, ui::Dataspace::UNKNOWN); } @@ -238,7 +239,8 @@ status_t VirtualDisplaySurface::advanceFrame() { } void VirtualDisplaySurface::onFrameCommitted() { - if (!mDisplayId) { + const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); + if (!halDisplayId) { return; } @@ -246,7 +248,7 @@ void VirtualDisplaySurface::onFrameCommitted() { "Unexpected onFrameCommitted() in %s state", dbgStateStr()); mDbgState = DBG_STATE_IDLE; - sp<Fence> retireFence = mHwc.getPresentFence(*mDisplayId); + sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId); if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) { // release the scratch buffer back to the pool Mutex::Autolock lock(mMutex); @@ -301,7 +303,7 @@ const sp<Fence>& VirtualDisplaySurface::getClientTargetAcquireFence() const { status_t VirtualDisplaySurface::requestBuffer(int pslot, sp<GraphicBuffer>* outBuf) { - if (!mDisplayId) { + if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf); } @@ -323,7 +325,7 @@ status_t VirtualDisplaySurface::setAsyncMode(bool async) { status_t VirtualDisplaySurface::dequeueBuffer(Source source, PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) { - LOG_FATAL_IF(!mDisplayId); + LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId)); status_t result = mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight, @@ -369,7 +371,7 @@ status_t VirtualDisplaySurface::dequeueBuffer(int* pslot, sp<Fence>* fence, uint PixelFormat format, uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps) { - if (!mDisplayId) { + if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return mSource[SOURCE_SINK]->dequeueBuffer(pslot, fence, w, h, format, usage, outBufferAge, outTimestamps); } @@ -456,7 +458,7 @@ status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */, status_t VirtualDisplaySurface::queueBuffer(int pslot, const QueueBufferInput& input, QueueBufferOutput* output) { - if (!mDisplayId) { + if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output); } @@ -514,7 +516,7 @@ status_t VirtualDisplaySurface::queueBuffer(int pslot, status_t VirtualDisplaySurface::cancelBuffer(int pslot, const sp<Fence>& fence) { - if (!mDisplayId) { + if (GpuVirtualDisplayId::tryCast(mDisplayId)) { return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence); } @@ -626,7 +628,7 @@ void VirtualDisplaySurface::resetPerFrameState() { } status_t VirtualDisplaySurface::refreshOutputBuffer() { - LOG_FATAL_IF(!mDisplayId); + LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId)); if (mOutputProducerSlot >= 0) { mSource[SOURCE_SINK]->cancelBuffer( @@ -645,7 +647,9 @@ status_t VirtualDisplaySurface::refreshOutputBuffer() { // until after GPU calls queueBuffer(). So here we just set the buffer // (for use in HWC prepare) but not the fence; we'll call this again with // the proper fence once we have it. - result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE, + const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId); + LOG_FATAL_IF(!halDisplayId); + result = mHwc.setOutputBuffer(*halDisplayId, Fence::NO_FENCE, mProducerBuffers[mOutputProducerSlot]); return result; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index 3cbad8f8ae..19746252ab 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -24,6 +24,7 @@ #include <compositionengine/impl/HwcBufferCache.h> #include <gui/ConsumerBase.h> #include <gui/IGraphicBufferProducer.h> +#include <ui/DisplayId.h> #include "DisplayIdentification.h" @@ -77,8 +78,7 @@ class VirtualDisplaySurface : public compositionengine::DisplaySurface, public BnGraphicBufferProducer, private ConsumerBase { public: - VirtualDisplaySurface(HWComposer& hwc, const std::optional<DisplayId>& displayId, - const sp<IGraphicBufferProducer>& sink, + VirtualDisplaySurface(HWComposer&, VirtualDisplayId, const sp<IGraphicBufferProducer>& sink, const sp<IGraphicBufferProducer>& bqProducer, const sp<IGraphicBufferConsumer>& bqConsumer, const std::string& name); @@ -86,7 +86,7 @@ public: // DisplaySurface interface // virtual status_t beginFrame(bool mustRecompose); - virtual status_t prepareFrame(CompositionType compositionType); + virtual status_t prepareFrame(CompositionType); virtual status_t advanceFrame(); virtual void onFrameCommitted(); virtual void dumpAsString(String8& result) const; @@ -104,25 +104,22 @@ private: virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf); virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers); virtual status_t setAsyncMode(bool async); - virtual status_t dequeueBuffer(int* pslot, sp<Fence>* fence, uint32_t w, uint32_t h, - PixelFormat format, uint64_t usage, uint64_t* outBufferAge, + virtual status_t dequeueBuffer(int* pslot, sp<Fence>*, uint32_t w, uint32_t h, PixelFormat, + uint64_t usage, uint64_t* outBufferAge, FrameEventHistoryDelta* outTimestamps); virtual status_t detachBuffer(int slot); - virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, - sp<Fence>* outFence); - virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer); - virtual status_t queueBuffer(int pslot, - const QueueBufferInput& input, QueueBufferOutput* output); - virtual status_t cancelBuffer(int pslot, const sp<Fence>& fence); + virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence); + virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>&); + virtual status_t queueBuffer(int pslot, const QueueBufferInput&, QueueBufferOutput*); + virtual status_t cancelBuffer(int pslot, const sp<Fence>&); virtual int query(int what, int* value); - virtual status_t connect(const sp<IProducerListener>& listener, - int api, bool producerControlledByApp, QueueBufferOutput* output); - virtual status_t disconnect(int api, DisconnectMode mode); + virtual status_t connect(const sp<IProducerListener>&, int api, bool producerControlledByApp, + QueueBufferOutput*); + virtual status_t disconnect(int api, DisconnectMode); virtual status_t setSidebandStream(const sp<NativeHandle>& stream); - virtual void allocateBuffers(uint32_t width, uint32_t height, - PixelFormat format, uint64_t usage); + virtual void allocateBuffers(uint32_t width, uint32_t height, PixelFormat, uint64_t usage); virtual status_t allowAllocation(bool allow); - virtual status_t setGenerationNumber(uint32_t generationNumber); + virtual status_t setGenerationNumber(uint32_t); virtual String8 getConsumerName() const override; virtual status_t setSharedBufferMode(bool sharedBufferMode) override; virtual status_t setAutoRefresh(bool autoRefresh) override; @@ -135,10 +132,9 @@ private: // // Utility methods // - static Source fbSourceForCompositionType(CompositionType type); - status_t dequeueBuffer(Source source, PixelFormat format, uint64_t usage, - int* sslot, sp<Fence>* fence); - void updateQueueBufferOutput(QueueBufferOutput&& qbo); + static Source fbSourceForCompositionType(CompositionType); + status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*); + void updateQueueBufferOutput(QueueBufferOutput&&); void resetPerFrameState(); status_t refreshOutputBuffer(); @@ -148,14 +144,14 @@ private: // internally in the VirtualDisplaySurface. To minimize the number of times // a producer slot switches which source it comes from, we map source slot // numbers to producer slot numbers differently for each source. - static int mapSource2ProducerSlot(Source source, int sslot); - static int mapProducer2SourceSlot(Source source, int pslot); + static int mapSource2ProducerSlot(Source, int sslot); + static int mapProducer2SourceSlot(Source, int pslot); // // Immutable after construction // HWComposer& mHwc; - const std::optional<DisplayId> mDisplayId; + const VirtualDisplayId mDisplayId; const std::string mDisplayName; sp<IGraphicBufferProducer> mSource[2]; // indexed by SOURCE_* uint32_t mDefaultOutputFormat; diff --git a/services/surfaceflinger/DisplayIdGenerator.h b/services/surfaceflinger/DisplayIdGenerator.h new file mode 100644 index 0000000000..e7c69a8094 --- /dev/null +++ b/services/surfaceflinger/DisplayIdGenerator.h @@ -0,0 +1,75 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ui/DisplayId.h> + +#include <limits> +#include <optional> +#include <random> +#include <unordered_set> + +#include <log/log.h> + +namespace android { + +template <typename T> +class DisplayIdGenerator { +public: + virtual std::optional<T> nextId() = 0; + virtual void markUnused(T id) = 0; + +protected: + ~DisplayIdGenerator() {} +}; + +template <typename T> +class RandomDisplayIdGenerator final : public DisplayIdGenerator<T> { +public: + explicit RandomDisplayIdGenerator(size_t maxIdsCount = std::numeric_limits<size_t>::max()) + : mMaxIdsCount(maxIdsCount) {} + + std::optional<T> nextId() override { + if (mUsedIds.size() >= mMaxIdsCount) { + return std::nullopt; + } + + constexpr int kMaxAttempts = 1000; + + for (int attempts = 0; attempts < kMaxAttempts; attempts++) { + const auto baseId = mDistribution(mGenerator); + const T id(baseId); + if (mUsedIds.count(id) == 0) { + mUsedIds.insert(id); + return id; + } + } + + LOG_ALWAYS_FATAL("Couldn't generate ID after %d attempts", kMaxAttempts); + } + + void markUnused(T id) override { mUsedIds.erase(id); } + +private: + const size_t mMaxIdsCount; + + std::unordered_set<T> mUsedIds; + std::default_random_engine mGenerator{std::random_device()()}; + std::uniform_int_distribution<typename T::BaseId> mDistribution; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp new file mode 100644 index 0000000000..20486e0aa3 --- /dev/null +++ b/services/surfaceflinger/DisplayRenderArea.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayRenderArea.h" +#include "DisplayDevice.h" + +namespace android { +namespace { + +RenderArea::RotationFlags applyDeviceOrientation(bool useIdentityTransform, + const DisplayDevice& display) { + if (!useIdentityTransform) { + return RenderArea::RotationFlags::ROT_0; + } + + return ui::Transform::toRotationFlags(display.getOrientation()); +} + +} // namespace + +std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak, + const Rect& sourceCrop, ui::Size reqSize, + ui::Dataspace reqDataSpace, + bool useIdentityTransform, + bool allowSecureLayers) { + if (auto display = displayWeak.promote()) { + // Using new to access a private constructor. + return std::unique_ptr<DisplayRenderArea>( + new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace, + useIdentityTransform, allowSecureLayers)); + } + return nullptr; +} + +DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop, + ui::Size reqSize, ui::Dataspace reqDataSpace, + bool useIdentityTransform, bool allowSecureLayers) + : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, display->getLayerStackSpaceRect(), + allowSecureLayers, applyDeviceOrientation(useIdentityTransform, *display)), + mDisplay(std::move(display)), + mSourceCrop(sourceCrop) {} + +const ui::Transform& DisplayRenderArea::getTransform() const { + return mTransform; +} + +Rect DisplayRenderArea::getBounds() const { + return mDisplay->getBounds(); +} + +int DisplayRenderArea::getHeight() const { + return mDisplay->getHeight(); +} + +int DisplayRenderArea::getWidth() const { + return mDisplay->getWidth(); +} + +bool DisplayRenderArea::isSecure() const { + return mAllowSecureLayers && mDisplay->isSecure(); +} + +sp<const DisplayDevice> DisplayRenderArea::getDisplayDevice() const { + return mDisplay; +} + +bool DisplayRenderArea::needsFiltering() const { + // check if the projection from the logical render area + // to the physical render area requires filtering + const Rect& sourceCrop = getSourceCrop(); + int width = sourceCrop.width(); + int height = sourceCrop.height(); + if (getRotationFlags() & ui::Transform::ROT_90) { + std::swap(width, height); + } + return width != getReqWidth() || height != getReqHeight(); +} + +Rect DisplayRenderArea::getSourceCrop() const { + // use the projected display viewport by default. + if (mSourceCrop.isEmpty()) { + return mDisplay->getLayerStackSpaceRect(); + } + + // Correct for the orientation when the screen capture request contained + // useIdentityTransform. This will cause the rotation flag to be non 0 since + // it needs to rotate based on the screen orientation to allow the screenshot + // to be taken in the ROT_0 orientation + const auto flags = getRotationFlags(); + int width = mDisplay->getLayerStackSpaceRect().getWidth(); + int height = mDisplay->getLayerStackSpaceRect().getHeight(); + ui::Transform rotation; + rotation.set(flags, width, height); + return rotation.transform(mSourceCrop); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h new file mode 100644 index 0000000000..3478fc16c4 --- /dev/null +++ b/services/surfaceflinger/DisplayRenderArea.h @@ -0,0 +1,53 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ui/GraphicTypes.h> +#include <ui/Transform.h> + +#include "RenderArea.h" + +namespace android { + +class DisplayDevice; + +class DisplayRenderArea : public RenderArea { +public: + static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop, + ui::Size reqSize, ui::Dataspace, + bool useIdentityTransform, + bool allowSecureLayers = true); + + const ui::Transform& getTransform() const override; + Rect getBounds() const override; + int getHeight() const override; + int getWidth() const override; + bool isSecure() const override; + sp<const DisplayDevice> getDisplayDevice() const override; + bool needsFiltering() const override; + Rect getSourceCrop() const override; + +private: + DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize, + ui::Dataspace, bool useIdentityTransform, bool allowSecureLayers = true); + + const sp<const DisplayDevice> mDisplay; + const Rect mSourceCrop; + const ui::Transform mTransform; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp new file mode 100644 index 0000000000..e075d3e1a0 --- /dev/null +++ b/services/surfaceflinger/FrameTimeline/Android.bp @@ -0,0 +1,18 @@ +cc_library_static { + name: "libframetimeline", + defaults: ["surfaceflinger_defaults"], + srcs: [ + "FrameTimeline.cpp", + ], + shared_libs: [ + "android.hardware.graphics.composer@2.4", + "libbase", + "libcutils", + "liblog", + "libgui", + "libtimestats", + "libui", + "libutils", + ], + export_include_dirs: ["."], +} diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp new file mode 100644 index 0000000000..bd87482dc6 --- /dev/null +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -0,0 +1,572 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "FrameTimeline" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "FrameTimeline.h" +#include <android-base/stringprintf.h> +#include <utils/Log.h> +#include <utils/Trace.h> +#include <chrono> +#include <cinttypes> +#include <numeric> + +namespace android::frametimeline::impl { + +using base::StringAppendF; + +void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals, + const std::string& indent, PredictionState predictionState, nsecs_t baseTime) { + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "\t\t"); + StringAppendF(&result, " Start time\t\t|"); + StringAppendF(&result, " End time\t\t|"); + StringAppendF(&result, " Present time\n"); + if (predictionState == PredictionState::Valid) { + // Dump the Predictions only if they are valid + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Expected\t|"); + std::chrono::nanoseconds startTime(predictions.startTime - baseTime); + std::chrono::nanoseconds endTime(predictions.endTime - baseTime); + std::chrono::nanoseconds presentTime(predictions.presentTime - baseTime); + StringAppendF(&result, "\t%10.2f\t|\t%10.2f\t|\t%10.2f\n", + std::chrono::duration<double, std::milli>(startTime).count(), + std::chrono::duration<double, std::milli>(endTime).count(), + std::chrono::duration<double, std::milli>(presentTime).count()); + } + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Actual \t|"); + + if (actuals.startTime == 0) { + StringAppendF(&result, "\t\tN/A\t|"); + } else { + std::chrono::nanoseconds startTime(std::max<nsecs_t>(0, actuals.startTime - baseTime)); + StringAppendF(&result, "\t%10.2f\t|", + std::chrono::duration<double, std::milli>(startTime).count()); + } + if (actuals.endTime == 0) { + StringAppendF(&result, "\t\tN/A\t|"); + } else { + std::chrono::nanoseconds endTime(actuals.endTime - baseTime); + StringAppendF(&result, "\t%10.2f\t|", + std::chrono::duration<double, std::milli>(endTime).count()); + } + if (actuals.presentTime == 0) { + StringAppendF(&result, "\t\tN/A\n"); + } else { + std::chrono::nanoseconds presentTime(std::max<nsecs_t>(0, actuals.presentTime - baseTime)); + StringAppendF(&result, "\t%10.2f\n", + std::chrono::duration<double, std::milli>(presentTime).count()); + } + + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "----------------------"); + StringAppendF(&result, "----------------------"); + StringAppendF(&result, "----------------------"); + StringAppendF(&result, "----------------------\n"); +} + +std::string toString(PredictionState predictionState) { + switch (predictionState) { + case PredictionState::Valid: + return "Valid"; + case PredictionState::Expired: + return "Expired"; + case PredictionState::None: + default: + return "None"; + } +} + +std::string toString(TimeStats::JankType jankType) { + switch (jankType) { + case TimeStats::JankType::None: + return "None"; + case TimeStats::JankType::Display: + return "Composer/Display - outside SF and App"; + case TimeStats::JankType::SurfaceFlingerDeadlineMissed: + return "SurfaceFlinger Deadline Missed"; + case TimeStats::JankType::AppDeadlineMissed: + return "App Deadline Missed"; + case TimeStats::JankType::PredictionExpired: + return "Prediction Expired"; + case TimeStats::JankType::SurfaceFlingerEarlyLatch: + return "SurfaceFlinger Early Latch"; + default: + return "Unclassified"; + } +} + +std::string jankMetadataBitmaskToString(int32_t jankMetadata) { + std::vector<std::string> jankInfo; + + if (jankMetadata & EarlyStart) { + jankInfo.emplace_back("Early Start"); + } else if (jankMetadata & LateStart) { + jankInfo.emplace_back("Late Start"); + } + + if (jankMetadata & EarlyFinish) { + jankInfo.emplace_back("Early Finish"); + } else if (jankMetadata & LateFinish) { + jankInfo.emplace_back("Late Finish"); + } + + if (jankMetadata & EarlyPresent) { + jankInfo.emplace_back("Early Present"); + } else if (jankMetadata & LatePresent) { + jankInfo.emplace_back("Late Present"); + } + // TODO(b/169876734): add GPU composition metadata here + + if (jankInfo.empty()) { + return "None"; + } + return std::accumulate(jankInfo.begin(), jankInfo.end(), std::string(), + [](const std::string& l, const std::string& r) { + return l.empty() ? r : l + ", " + r; + }); +} + +int64_t TokenManager::generateTokenForPredictions(TimelineItem&& predictions) { + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mMutex); + const int64_t assignedToken = mCurrentToken++; + mPredictions[assignedToken] = predictions; + mTokens.emplace_back(std::make_pair(assignedToken, systemTime())); + flushTokens(systemTime()); + return assignedToken; +} + +std::optional<TimelineItem> TokenManager::getPredictionsForToken(int64_t token) { + std::lock_guard<std::mutex> lock(mMutex); + flushTokens(systemTime()); + auto predictionsIterator = mPredictions.find(token); + if (predictionsIterator != mPredictions.end()) { + return predictionsIterator->second; + } + return {}; +} + +void TokenManager::flushTokens(nsecs_t flushTime) { + for (size_t i = 0; i < mTokens.size(); i++) { + if (flushTime - mTokens[i].second >= kMaxRetentionTime) { + mPredictions.erase(mTokens[i].first); + mTokens.erase(mTokens.begin() + static_cast<int>(i)); + --i; + } else { + // Tokens are ordered by time. If i'th token is within the retention time, then the + // i+1'th token will also be within retention time. + break; + } + } +} + +SurfaceFrame::SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName, + std::string debugName, PredictionState predictionState, + frametimeline::TimelineItem&& predictions) + : mOwnerPid(ownerPid), + mOwnerUid(ownerUid), + mLayerName(std::move(layerName)), + mDebugName(std::move(debugName)), + mPresentState(PresentState::Unknown), + mPredictionState(predictionState), + mPredictions(predictions), + mActuals({0, 0, 0}), + mActualQueueTime(0), + mJankType(TimeStats::JankType::None), + mJankMetadata(0) {} + +void SurfaceFrame::setPresentState(PresentState state) { + std::lock_guard<std::mutex> lock(mMutex); + mPresentState = state; +} + +SurfaceFrame::PresentState SurfaceFrame::getPresentState() const { + std::lock_guard<std::mutex> lock(mMutex); + return mPresentState; +} + +TimelineItem SurfaceFrame::getActuals() const { + std::lock_guard<std::mutex> lock(mMutex); + return mActuals; +} + +nsecs_t SurfaceFrame::getActualQueueTime() const { + std::lock_guard<std::mutex> lock(mMutex); + return mActualQueueTime; +} + +void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) { + std::lock_guard<std::mutex> lock(mMutex); + mActuals.startTime = actualStartTime; +} + +void SurfaceFrame::setActualQueueTime(nsecs_t actualQueueTime) { + std::lock_guard<std::mutex> lock(mMutex); + mActualQueueTime = actualQueueTime; +} +void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) { + std::lock_guard<std::mutex> lock(mMutex); + mActuals.endTime = std::max(acquireFenceTime, mActualQueueTime); +} + +void SurfaceFrame::setActualPresentTime(nsecs_t presentTime) { + std::lock_guard<std::mutex> lock(mMutex); + mActuals.presentTime = presentTime; +} + +void SurfaceFrame::setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata) { + std::lock_guard<std::mutex> lock(mMutex); + mJankType = jankType; + mJankMetadata = jankMetadata; +} + +TimeStats::JankType SurfaceFrame::getJankType() const { + std::lock_guard<std::mutex> lock(mMutex); + return mJankType; +} + +nsecs_t SurfaceFrame::getBaseTime() const { + std::lock_guard<std::mutex> lock(mMutex); + nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); + if (mPredictionState == PredictionState::Valid) { + baseTime = std::min(baseTime, mPredictions.startTime); + } + if (mActuals.startTime != 0) { + baseTime = std::min(baseTime, mActuals.startTime); + } + baseTime = std::min(baseTime, mActuals.endTime); + return baseTime; +} + +std::string presentStateToString(SurfaceFrame::PresentState presentState) { + using PresentState = SurfaceFrame::PresentState; + switch (presentState) { + case PresentState::Presented: + return "Presented"; + case PresentState::Dropped: + return "Dropped"; + case PresentState::Unknown: + default: + return "Unknown"; + } +} + +void SurfaceFrame::dump(std::string& result, const std::string& indent, nsecs_t baseTime) { + std::lock_guard<std::mutex> lock(mMutex); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Layer - %s", mDebugName.c_str()); + if (mJankType != TimeStats::JankType::None) { + // Easily identify a janky Surface Frame in the dump + StringAppendF(&result, " [*] "); + } + StringAppendF(&result, "\n"); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Owner Pid : %d\n", mOwnerPid); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Present State : %s\n", presentStateToString(mPresentState).c_str()); + 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", toString(mJankType).c_str()); + StringAppendF(&result, "%s", indent.c_str()); + StringAppendF(&result, "Jank Metadata: %s\n", + jankMetadataBitmaskToString(mJankMetadata).c_str()); + dumpTable(result, mPredictions, mActuals, indent, mPredictionState, baseTime); +} + +FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats) + : mCurrentDisplayFrame(std::make_shared<DisplayFrame>()), + mMaxDisplayFrames(kDefaultMaxDisplayFrames), + mTimeStats(std::move(timeStats)) {} + +FrameTimeline::DisplayFrame::DisplayFrame() + : surfaceFlingerPredictions(TimelineItem()), + surfaceFlingerActuals(TimelineItem()), + predictionState(PredictionState::None), + jankType(TimeStats::JankType::None), + jankMetadata(0) { + this->surfaceFrames.reserve(kNumSurfaceFramesInitial); +} + +std::unique_ptr<android::frametimeline::SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( + pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName, + std::optional<int64_t> token) { + ATRACE_CALL(); + if (!token) { + return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName), + std::move(debugName), PredictionState::None, + TimelineItem()); + } + std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token); + if (predictions) { + return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName), + std::move(debugName), PredictionState::Valid, + std::move(*predictions)); + } + return std::make_unique<impl::SurfaceFrame>(ownerPid, ownerUid, std::move(layerName), + std::move(debugName), PredictionState::Expired, + TimelineItem()); +} + +void FrameTimeline::addSurfaceFrame( + std::unique_ptr<android::frametimeline::SurfaceFrame> surfaceFrame, + SurfaceFrame::PresentState state) { + ATRACE_CALL(); + surfaceFrame->setPresentState(state); + std::unique_ptr<impl::SurfaceFrame> implSurfaceFrame( + static_cast<impl::SurfaceFrame*>(surfaceFrame.release())); + std::lock_guard<std::mutex> lock(mMutex); + mCurrentDisplayFrame->surfaceFrames.push_back(std::move(implSurfaceFrame)); +} + +void FrameTimeline::setSfWakeUp(int64_t token, nsecs_t wakeUpTime) { + ATRACE_CALL(); + const std::optional<TimelineItem> prediction = mTokenManager.getPredictionsForToken(token); + std::lock_guard<std::mutex> lock(mMutex); + if (!prediction) { + mCurrentDisplayFrame->predictionState = PredictionState::Expired; + } else { + mCurrentDisplayFrame->surfaceFlingerPredictions = *prediction; + mCurrentDisplayFrame->predictionState = PredictionState::Valid; + } + mCurrentDisplayFrame->surfaceFlingerActuals.startTime = wakeUpTime; +} + +void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, + const std::shared_ptr<FenceTime>& presentFence) { + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mMutex); + mCurrentDisplayFrame->surfaceFlingerActuals.endTime = sfPresentTime; + mPendingPresentFences.emplace_back(std::make_pair(presentFence, mCurrentDisplayFrame)); + flushPendingPresentFences(); + finalizeCurrentDisplayFrame(); +} + +void FrameTimeline::flushPendingPresentFences() { + for (size_t i = 0; i < mPendingPresentFences.size(); i++) { + const auto& pendingPresentFence = mPendingPresentFences[i]; + nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; + if (pendingPresentFence.first && pendingPresentFence.first->isValid()) { + signalTime = pendingPresentFence.first->getSignalTime(); + if (signalTime == Fence::SIGNAL_TIME_PENDING) { + continue; + } + } + if (signalTime != Fence::SIGNAL_TIME_INVALID) { + int32_t totalJankReasons = TimeStats::JankType::None; + auto& displayFrame = pendingPresentFence.second; + displayFrame->surfaceFlingerActuals.presentTime = signalTime; + + // Jank Analysis for DisplayFrame + const auto& sfActuals = displayFrame->surfaceFlingerActuals; + const auto& sfPredictions = displayFrame->surfaceFlingerPredictions; + if (std::abs(sfActuals.presentTime - sfPredictions.presentTime) > kPresentThreshold) { + displayFrame->jankMetadata |= sfActuals.presentTime > sfPredictions.presentTime + ? LatePresent + : EarlyPresent; + } + if (std::abs(sfActuals.endTime - sfPredictions.endTime) > kDeadlineThreshold) { + if (sfActuals.endTime > sfPredictions.endTime) { + displayFrame->jankMetadata |= LateFinish; + } else { + displayFrame->jankMetadata |= EarlyFinish; + } + + if ((displayFrame->jankMetadata & EarlyFinish) && + (displayFrame->jankMetadata & EarlyPresent)) { + displayFrame->jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch; + } else if ((displayFrame->jankMetadata & LateFinish) && + (displayFrame->jankMetadata & LatePresent)) { + displayFrame->jankType = TimeStats::JankType::SurfaceFlingerDeadlineMissed; + } else if (displayFrame->jankMetadata & EarlyPresent || + displayFrame->jankMetadata & LatePresent) { + // Cases where SF finished early but frame was presented late and vice versa + displayFrame->jankType = TimeStats::JankType::Display; + } + } + + if (std::abs(sfActuals.startTime - sfPredictions.startTime) > kSFStartThreshold) { + displayFrame->jankMetadata |= + sfActuals.startTime > sfPredictions.startTime ? LateStart : EarlyStart; + } + + totalJankReasons |= displayFrame->jankType; + + for (auto& surfaceFrame : displayFrame->surfaceFrames) { + if (surfaceFrame->getPresentState() == SurfaceFrame::PresentState::Presented) { + // Only presented SurfaceFrames need to be updated + surfaceFrame->setActualPresentTime(signalTime); + + // Jank Analysis for SurfaceFrame + const auto& predictionState = surfaceFrame->getPredictionState(); + if (predictionState == PredictionState::Expired) { + // Jank analysis cannot be done on apps that don't use predictions + surfaceFrame->setJankInfo(TimeStats::JankType::PredictionExpired, 0); + continue; + } else if (predictionState == PredictionState::Valid) { + const auto& actuals = surfaceFrame->getActuals(); + const auto& predictions = surfaceFrame->getPredictions(); + int32_t jankMetadata = 0; + TimeStats::JankType jankType = TimeStats::JankType::None; + if (std::abs(actuals.endTime - predictions.endTime) > kDeadlineThreshold) { + jankMetadata |= actuals.endTime > predictions.endTime ? LateFinish + : EarlyFinish; + } + if (std::abs(actuals.presentTime - predictions.presentTime) > + kPresentThreshold) { + jankMetadata |= actuals.presentTime > predictions.presentTime + ? LatePresent + : EarlyPresent; + } + if (jankMetadata & EarlyPresent) { + jankType = TimeStats::JankType::SurfaceFlingerEarlyLatch; + } else if (jankMetadata & LatePresent) { + if (jankMetadata & EarlyFinish) { + // TODO(b/169890654): Classify this properly + jankType = TimeStats::JankType::Display; + } else { + jankType = TimeStats::JankType::AppDeadlineMissed; + } + } + + totalJankReasons |= jankType; + mTimeStats->incrementJankyFrames(surfaceFrame->getOwnerUid(), + surfaceFrame->getName(), + jankType | displayFrame->jankType); + surfaceFrame->setJankInfo(jankType, jankMetadata); + } + } + } + + mTimeStats->incrementJankyFrames(totalJankReasons); + } + + mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i)); + --i; + } +} + +void FrameTimeline::finalizeCurrentDisplayFrame() { + while (mDisplayFrames.size() >= mMaxDisplayFrames) { + // We maintain only a fixed number of frames' data. Pop older frames + mDisplayFrames.pop_front(); + } + mDisplayFrames.push_back(mCurrentDisplayFrame); + mCurrentDisplayFrame.reset(); + mCurrentDisplayFrame = std::make_shared<DisplayFrame>(); +} + +nsecs_t FrameTimeline::findBaseTime(const std::shared_ptr<DisplayFrame>& displayFrame) { + nsecs_t baseTime = std::numeric_limits<nsecs_t>::max(); + if (displayFrame->predictionState == PredictionState::Valid) { + baseTime = std::min(baseTime, displayFrame->surfaceFlingerPredictions.startTime); + } + baseTime = std::min(baseTime, displayFrame->surfaceFlingerActuals.startTime); + for (const auto& surfaceFrame : displayFrame->surfaceFrames) { + nsecs_t surfaceFrameBaseTime = surfaceFrame->getBaseTime(); + if (surfaceFrameBaseTime != 0) { + baseTime = std::min(baseTime, surfaceFrameBaseTime); + } + } + return baseTime; +} + +void FrameTimeline::dumpDisplayFrame(std::string& result, + const std::shared_ptr<DisplayFrame>& displayFrame, + nsecs_t baseTime) { + if (displayFrame->jankType != TimeStats::JankType::None) { + // Easily identify a janky Display Frame in the dump + StringAppendF(&result, " [*] "); + } + StringAppendF(&result, "\n"); + StringAppendF(&result, "Prediction State : %s\n", + toString(displayFrame->predictionState).c_str()); + StringAppendF(&result, "Jank Type : %s\n", toString(displayFrame->jankType).c_str()); + StringAppendF(&result, "Jank Metadata: %s\n", + jankMetadataBitmaskToString(displayFrame->jankMetadata).c_str()); + dumpTable(result, displayFrame->surfaceFlingerPredictions, displayFrame->surfaceFlingerActuals, + "", displayFrame->predictionState, baseTime); + StringAppendF(&result, "\n"); + std::string indent = " "; // 4 spaces + for (const auto& surfaceFrame : displayFrame->surfaceFrames) { + surfaceFrame->dump(result, indent, baseTime); + } + StringAppendF(&result, "\n"); +} +void FrameTimeline::dumpAll(std::string& result) { + std::lock_guard<std::mutex> lock(mMutex); + StringAppendF(&result, "Number of display frames : %d\n", (int)mDisplayFrames.size()); + nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); + for (size_t i = 0; i < mDisplayFrames.size(); i++) { + StringAppendF(&result, "Display Frame %d", static_cast<int>(i)); + dumpDisplayFrame(result, mDisplayFrames[i], baseTime); + } +} + +void FrameTimeline::dumpJank(std::string& result) { + std::lock_guard<std::mutex> lock(mMutex); + nsecs_t baseTime = (mDisplayFrames.empty()) ? 0 : findBaseTime(mDisplayFrames[0]); + for (size_t i = 0; i < mDisplayFrames.size(); i++) { + const auto& displayFrame = mDisplayFrames[i]; + if (displayFrame->jankType == TimeStats::JankType::None) { + // Check if any Surface Frame has been janky + bool isJanky = false; + for (const auto& surfaceFrame : displayFrame->surfaceFrames) { + if (surfaceFrame->getJankType() != TimeStats::JankType::None) { + isJanky = true; + break; + } + } + if (!isJanky) { + continue; + } + } + StringAppendF(&result, "Display Frame %d", static_cast<int>(i)); + dumpDisplayFrame(result, displayFrame, baseTime); + } +} +void FrameTimeline::parseArgs(const Vector<String16>& args, std::string& result) { + ATRACE_CALL(); + std::unordered_map<std::string, bool> argsMap; + for (size_t i = 0; i < args.size(); i++) { + argsMap[std::string(String8(args[i]).c_str())] = true; + } + if (argsMap.count("-jank")) { + dumpJank(result); + } + if (argsMap.count("-all")) { + dumpAll(result); + } +} + +void FrameTimeline::setMaxDisplayFrames(uint32_t size) { + std::lock_guard<std::mutex> lock(mMutex); + + // The size can either increase or decrease, clear everything, to be consistent + mDisplayFrames.clear(); + mPendingPresentFences.clear(); + mMaxDisplayFrames = size; +} + +void FrameTimeline::reset() { + setMaxDisplayFrames(kDefaultMaxDisplayFrames); +} + +} // namespace android::frametimeline::impl diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h new file mode 100644 index 0000000000..e61567ecae --- /dev/null +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -0,0 +1,314 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <../TimeStats/TimeStats.h> +#include <gui/ISurfaceComposer.h> +#include <ui/FenceTime.h> +#include <utils/RefBase.h> +#include <utils/String16.h> +#include <utils/Timers.h> +#include <utils/Vector.h> + +#include <deque> +#include <mutex> + +namespace android::frametimeline { + +enum JankMetadata { + // Frame was presented earlier than expected + EarlyPresent = 0x1, + // Frame was presented later than expected + LatePresent = 0x2, + // App/SF started earlier than expected + EarlyStart = 0x4, + // App/SF started later than expected + LateStart = 0x8, + // App/SF finished work earlier than the deadline + EarlyFinish = 0x10, + // App/SF finished work later than the deadline + LateFinish = 0x20, + // SF was in GPU composition + GpuComposition = 0x40, +}; + +class FrameTimelineTest; + +/* + * Collection of timestamps that can be used for both predictions and actual times. + */ +struct TimelineItem { + TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0, + const nsecs_t presentTime = 0) + : startTime(startTime), endTime(endTime), presentTime(presentTime) {} + + nsecs_t startTime; + nsecs_t endTime; + nsecs_t presentTime; + + bool operator==(const TimelineItem& other) const { + return startTime == other.startTime && endTime == other.endTime && + presentTime == other.presentTime; + } + + bool operator!=(const TimelineItem& other) const { return !(*this == other); } +}; + +/* + * TokenManager generates a running number token for a set of predictions made by VsyncPredictor. It + * saves these predictions for a short period of time and returns the predictions for a given token, + * if it hasn't expired. + */ +class TokenManager { +public: + virtual ~TokenManager() = default; + + // Generates a token for the given set of predictions. Stores the predictions for 120ms and + // destroys it later. + virtual int64_t generateTokenForPredictions(TimelineItem&& prediction) = 0; +}; + +enum class PredictionState { + Valid, // Predictions obtained successfully from the TokenManager + Expired, // TokenManager no longer has the predictions + None, // Predictions are either not present or didn't come from TokenManager +}; + +/* + * Stores a set of predictions and the corresponding actual timestamps pertaining to a single frame + * from the app + */ +class SurfaceFrame { +public: + enum class PresentState { + Presented, // Buffer was latched and presented by SurfaceFlinger + Dropped, // Buffer was dropped by SurfaceFlinger + Unknown, // Initial state, SurfaceFlinger hasn't seen this buffer yet + }; + + virtual ~SurfaceFrame() = default; + + virtual TimelineItem getPredictions() const = 0; + virtual TimelineItem getActuals() const = 0; + virtual nsecs_t getActualQueueTime() const = 0; + virtual PresentState getPresentState() const = 0; + virtual PredictionState getPredictionState() const = 0; + virtual pid_t getOwnerPid() const = 0; + + virtual void setPresentState(PresentState state) = 0; + + // 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. + virtual void setActualStartTime(nsecs_t actualStartTime) = 0; + virtual void setActualQueueTime(nsecs_t actualQueueTime) = 0; + virtual void setAcquireFenceTime(nsecs_t acquireFenceTime) = 0; +}; + +/* + * Maintains a history of SurfaceFrames grouped together by the vsync time in which they were + * presented + */ +class FrameTimeline { +public: + virtual ~FrameTimeline() = default; + virtual TokenManager* getTokenManager() = 0; + + // Create a new surface frame, set the predictions based on a token and return it to the caller. + // Sets the PredictionState of SurfaceFrame. + // Debug name is the human-readable debugging string for dumpsys. + virtual std::unique_ptr<SurfaceFrame> createSurfaceFrameForToken( + pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName, + std::optional<int64_t> token) = 0; + + // Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be + // composited into one display frame. + virtual void addSurfaceFrame(std::unique_ptr<SurfaceFrame> surfaceFrame, + SurfaceFrame::PresentState state) = 0; + + // 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) = 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 + // that vsync. + virtual void setSfPresent(nsecs_t sfPresentTime, + const std::shared_ptr<FenceTime>& presentFence) = 0; + + // Args: + // -jank : Dumps only the Display Frames that are either janky themselves + // or contain janky Surface Frames. + // -all : Dumps the entire list of DisplayFrames and the SurfaceFrames contained within + virtual void parseArgs(const Vector<String16>& args, std::string& result) = 0; + + // Sets the max number of display frames that can be stored. Called by SF backdoor. + virtual void setMaxDisplayFrames(uint32_t size); + + // Restores the max number of display frames to default. Called by SF backdoor. + virtual void reset() = 0; +}; + +namespace impl { + +using namespace std::chrono_literals; + +class TokenManager : public android::frametimeline::TokenManager { +public: + TokenManager() : mCurrentToken(ISurfaceComposer::INVALID_VSYNC_ID + 1) {} + ~TokenManager() = default; + + int64_t generateTokenForPredictions(TimelineItem&& predictions) override; + std::optional<TimelineItem> getPredictionsForToken(int64_t token); + +private: + // Friend class for testing + friend class android::frametimeline::FrameTimelineTest; + + void flushTokens(nsecs_t flushTime) REQUIRES(mMutex); + + std::unordered_map<int64_t, TimelineItem> mPredictions GUARDED_BY(mMutex); + std::vector<std::pair<int64_t, nsecs_t>> mTokens GUARDED_BY(mMutex); + int64_t mCurrentToken GUARDED_BY(mMutex); + std::mutex mMutex; + static constexpr nsecs_t kMaxRetentionTime = + std::chrono::duration_cast<std::chrono::nanoseconds>(120ms).count(); +}; + +class SurfaceFrame : public android::frametimeline::SurfaceFrame { +public: + SurfaceFrame(pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName, + PredictionState predictionState, TimelineItem&& predictions); + ~SurfaceFrame() = default; + + TimelineItem getPredictions() const override { return mPredictions; }; + TimelineItem getActuals() const override; + nsecs_t getActualQueueTime() const override; + PresentState getPresentState() const override; + PredictionState getPredictionState() const override { return mPredictionState; }; + pid_t getOwnerPid() const override { return mOwnerPid; }; + TimeStats::JankType getJankType() const; + nsecs_t getBaseTime() const; + uid_t getOwnerUid() const { return mOwnerUid; }; + const std::string& getName() const { return mLayerName; }; + + void setActualStartTime(nsecs_t actualStartTime) override; + void setActualQueueTime(nsecs_t actualQueueTime) override; + void setAcquireFenceTime(nsecs_t acquireFenceTime) override; + void setPresentState(PresentState state) override; + void setActualPresentTime(nsecs_t presentTime); + void setJankInfo(TimeStats::JankType jankType, int32_t jankMetadata); + + // All the timestamps are dumped relative to the baseTime + void dump(std::string& result, const std::string& indent, nsecs_t baseTime); + +private: + const pid_t mOwnerPid; + const uid_t mOwnerUid; + const std::string mLayerName; + const std::string mDebugName; + PresentState mPresentState GUARDED_BY(mMutex); + const PredictionState mPredictionState; + const TimelineItem mPredictions; + TimelineItem mActuals GUARDED_BY(mMutex); + nsecs_t mActualQueueTime GUARDED_BY(mMutex); + mutable std::mutex mMutex; + TimeStats::JankType mJankType GUARDED_BY(mMutex); // Enum for the type of jank + int32_t mJankMetadata GUARDED_BY(mMutex); // Additional details about the jank +}; + +class FrameTimeline : public android::frametimeline::FrameTimeline { +public: + FrameTimeline(std::shared_ptr<TimeStats> timeStats); + ~FrameTimeline() = default; + + frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; } + std::unique_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForToken( + pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName, + std::optional<int64_t> token) override; + void addSurfaceFrame(std::unique_ptr<frametimeline::SurfaceFrame> surfaceFrame, + SurfaceFrame::PresentState state) override; + void setSfWakeUp(int64_t token, nsecs_t wakeupTime) override; + void setSfPresent(nsecs_t sfPresentTime, + const std::shared_ptr<FenceTime>& presentFence) override; + void parseArgs(const Vector<String16>& args, std::string& result) override; + void setMaxDisplayFrames(uint32_t size) override; + void reset() override; + +private: + // Friend class for testing + friend class android::frametimeline::FrameTimelineTest; + + /* + * DisplayFrame should be used only internally within FrameTimeline. + */ + struct DisplayFrame { + DisplayFrame(); + + /* Usage of TimelineItem w.r.t SurfaceFlinger + * startTime Time when SurfaceFlinger wakes up to handle transactions and buffer updates + * endTime Time when SurfaceFlinger sends a composited frame to Display + * presentTime Time when the composited frame was presented on screen + */ + TimelineItem surfaceFlingerPredictions; + TimelineItem surfaceFlingerActuals; + + // Collection of predictions and actual values sent over by Layers + std::vector<std::unique_ptr<SurfaceFrame>> surfaceFrames; + + PredictionState predictionState; + TimeStats::JankType jankType = TimeStats::JankType::None; // Enum for the type of jank + int32_t jankMetadata = 0x0; // Additional details about the jank + }; + + void flushPendingPresentFences() REQUIRES(mMutex); + void finalizeCurrentDisplayFrame() REQUIRES(mMutex); + // BaseTime is the smallest timestamp in a DisplayFrame. + // Used for dumping all timestamps relative to the oldest, making it easy to read. + nsecs_t findBaseTime(const std::shared_ptr<DisplayFrame>&) REQUIRES(mMutex); + void dumpDisplayFrame(std::string& result, const std::shared_ptr<DisplayFrame>&, + nsecs_t baseTime) REQUIRES(mMutex); + void dumpAll(std::string& result); + void dumpJank(std::string& result); + + // Sliding window of display frames. TODO(b/168072834): compare perf with fixed size array + std::deque<std::shared_ptr<DisplayFrame>> mDisplayFrames GUARDED_BY(mMutex); + std::vector<std::pair<std::shared_ptr<FenceTime>, std::shared_ptr<DisplayFrame>>> + mPendingPresentFences GUARDED_BY(mMutex); + std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex); + TokenManager mTokenManager; + std::mutex mMutex; + uint32_t mMaxDisplayFrames; + std::shared_ptr<TimeStats> mTimeStats; + static constexpr uint32_t kDefaultMaxDisplayFrames = 64; + // The initial container size for the vector<SurfaceFrames> inside display frame. Although this + // number doesn't represent any bounds on the number of surface frames that can go in a display + // frame, this is a good starting size for the vector so that we can avoid the internal vector + // resizing that happens with push_back. + static constexpr uint32_t kNumSurfaceFramesInitial = 10; + // The various thresholds for App and SF. If the actual timestamp falls within the threshold + // compared to prediction, we don't treat it as a jank. + static constexpr nsecs_t kPresentThreshold = + std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); + static constexpr nsecs_t kDeadlineThreshold = + std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count(); + static constexpr nsecs_t kSFStartThreshold = + std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(); +}; + +} // namespace impl +} // namespace android::frametimeline diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp index b986f3844a..2dfb9a9d11 100644 --- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp +++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp @@ -25,7 +25,7 @@ #include "FrameTracer.h" #include <android-base/stringprintf.h> -#include <perfetto/trace/clock_snapshot.pbzero.h> +#include <perfetto/common/builtin_clock.pbzero.h> #include <algorithm> #include <mutex> @@ -34,7 +34,6 @@ PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::FrameTracer::FrameTracerData namespace android { -using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock; void FrameTracer::initialize() { std::call_once(mInitializationFlag, [this]() { perfetto::TracingInitArgs args; @@ -136,7 +135,7 @@ void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp, FrameEvent::BufferEventType type, nsecs_t duration) { auto packet = ctx.NewTracePacket(); - packet->set_timestamp_clock_id(Clock::MONOTONIC); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); packet->set_timestamp(timestamp); auto* event = packet->set_graphics_frame_event()->set_buffer_event(); event->set_buffer_id(static_cast<uint32_t>(bufferID)); diff --git a/services/surfaceflinger/FrameTracer/OWNERS b/services/surfaceflinger/FrameTracer/OWNERS new file mode 100644 index 0000000000..e4f5d4554c --- /dev/null +++ b/services/surfaceflinger/FrameTracer/OWNERS @@ -0,0 +1 @@ +adsrini@google.com diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index e9965d4717..b60acde2a6 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -39,6 +39,7 @@ #include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <math.h> +#include <private/android_filesystem_config.h> #include <renderengine/RenderEngine.h> #include <stdint.h> #include <stdlib.h> @@ -61,18 +62,22 @@ #include "DisplayDevice.h" #include "DisplayHardware/HWComposer.h" #include "EffectLayer.h" +#include "FrameTimeline.h" #include "FrameTracer/FrameTracer.h" #include "LayerProtoHelper.h" #include "LayerRejecter.h" #include "MonitoredProducer.h" #include "SurfaceFlinger.h" #include "TimeStats/TimeStats.h" +#include "input/InputWindow.h" #define DEBUG_RESIZE 0 namespace android { using base::StringAppendF; +using namespace android::flag_operators; +using PresentState = frametimeline::SurfaceFrame::PresentState; std::atomic<int32_t> Layer::sSequence{1}; @@ -80,11 +85,14 @@ Layer::Layer(const LayerCreationArgs& args) : mFlinger(args.flinger), mName(args.name), mClientRef(args.client), - mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) { + mWindowType(static_cast<InputWindowInfo::Type>( + args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) { uint32_t layerFlags = 0; if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden; if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque; if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure; + if (args.flags & ISurfaceComposerClient::eSkipScreenshot) + layerFlags |= layer_state_t::eLayerSkipScreenshot; mCurrentState.active_legacy.w = args.w; mCurrentState.active_legacy.h = args.h; @@ -118,6 +126,8 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.shadowRadius = 0.f; mCurrentState.treeHasFrameRateVote = false; mCurrentState.fixedTransformHint = ui::Transform::ROT_INVALID; + mCurrentState.frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID; + mCurrentState.postTime = -1; if (args.flags & ISurfaceComposerClient::eNoColorFill) { // Set an invalid color so there is no color fill. @@ -136,6 +146,16 @@ Layer::Layer(const LayerCreationArgs& args) mCallingPid = args.callingPid; mCallingUid = args.callingUid; + + if (mCallingUid == AID_GRAPHICS || mCallingUid == AID_SYSTEM) { + // If the system didn't send an ownerUid, use the callingUid for the ownerUid. + mOwnerUid = args.metadata.getInt32(METADATA_OWNER_UID, mCallingUid); + mOwnerPid = args.metadata.getInt32(METADATA_OWNER_PID, mCallingPid); + } else { + // A create layer request from a non system request cannot specify the owner uid + mOwnerUid = mCallingUid; + mOwnerPid = mCallingPid; + } } void Layer::onFirstRef() { @@ -464,6 +484,7 @@ void Layer::prepareBasicGeometryCompositionState() { compositionState->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); compositionState->alpha = alpha; compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius; + compositionState->blurRegions = drawingState.blurRegions; } void Layer::prepareGeometryCompositionState() { @@ -492,9 +513,6 @@ void Layer::prepareGeometryCompositionState() { compositionState->geomUsesSourceCrop = usesSourceCrop(); compositionState->isSecure = isSecure(); - compositionState->type = type; - compositionState->appId = appId; - compositionState->metadata.clear(); const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata(); for (const auto& [key, mandatory] : supportedMetadata) { @@ -535,7 +553,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()) { + if (isHdrY410() || usesRoundedCorners || drawShadows() || + getDrawingState().blurRegions.size() > 0) { compositionState->forceClientComposition = true; } } @@ -608,7 +627,7 @@ const char* Layer::getDebugName() const { // --------------------------------------------------------------------------- std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientComposition( - compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { + compositionengine::LayerFE::ClientCompositionTargetSettings& /* targetSettings */) { if (!getCompositionState()) { return {}; } @@ -618,11 +637,7 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom compositionengine::LayerFE::LayerSettings layerSettings; layerSettings.geometry.boundaries = bounds; - if (targetSettings.useIdentityTransform) { - layerSettings.geometry.positionTransform = mat4(); - } else { - layerSettings.geometry.positionTransform = getTransform().asMatrix4(); - } + layerSettings.geometry.positionTransform = getTransform().asMatrix4(); if (hasColorTransform()) { layerSettings.colorTransform = getColorTransform(); @@ -635,13 +650,14 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCom layerSettings.alpha = alpha; layerSettings.sourceDataspace = getDataSpace(); layerSettings.backgroundBlurRadius = getBackgroundBlurRadius(); + layerSettings.blurRegions = getBlurRegions(); return layerSettings; } std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowClientComposition( - const LayerFE::LayerSettings& casterLayerSettings, const Rect& displayViewport, + const LayerFE::LayerSettings& casterLayerSettings, const Rect& layerStackRect, ui::Dataspace outputDataspace) { - renderengine::ShadowSettings shadow = getShadowSettings(displayViewport); + renderengine::ShadowSettings shadow = getShadowSettings(layerStackRect); if (shadow.length <= 0.f) { return {}; } @@ -767,7 +783,12 @@ bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) { bool Layer::isSecure() const { const State& s(mDrawingState); - return (s.flags & layer_state_t::eLayerSecure); + if (s.flags & layer_state_t::eLayerSecure) { + return true; + } + + const auto p = mDrawingParent.promote(); + return (p != nullptr) ? p->isSecure() : false; } // ---------------------------------------------------------------------------- @@ -793,10 +814,10 @@ void Layer::pushPendingState() { // to be applied as per normal (no synchronization). mCurrentState.barrierLayer_legacy = nullptr; } else { - auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.frameNumber_legacy, this); + auto syncPoint = std::make_shared<SyncPoint>(mCurrentState.barrierFrameNumber, this); if (barrierLayer->addSyncPoint(syncPoint)) { std::stringstream ss; - ss << "Adding sync point " << mCurrentState.frameNumber_legacy; + ss << "Adding sync point " << mCurrentState.barrierFrameNumber; ATRACE_NAME(ss.str().c_str()); mRemoteSyncPoints.push_back(std::move(syncPoint)); } else { @@ -816,8 +837,8 @@ void Layer::pushPendingState() { void Layer::popPendingState(State* stateToCommit) { ATRACE_CALL(); - *stateToCommit = mPendingStates[0]; + *stateToCommit = mPendingStates[0]; mPendingStates.removeAt(0); ATRACE_INT(mTransactionName.c_str(), mPendingStates.size()); } @@ -837,7 +858,7 @@ bool Layer::applyPendingStates(State* stateToCommit) { } if (mRemoteSyncPoints.front()->getFrameNumber() != - mPendingStates[0].frameNumber_legacy) { + mPendingStates[0].barrierFrameNumber) { ALOGE("[%s] Unexpected sync point frame number found", getDebugName()); // Signal our end of the sync point and then dispose of it @@ -874,6 +895,25 @@ bool Layer::applyPendingStates(State* stateToCommit) { mFlinger->setTraversalNeeded(); } + if (stateUpdateAvailable) { + const auto vsyncId = + stateToCommit->frameTimelineVsyncId == ISurfaceComposer::INVALID_VSYNC_ID + ? std::nullopt + : std::make_optional(stateToCommit->frameTimelineVsyncId); + + auto surfaceFrame = + mFlinger->mFrameTimeline->createSurfaceFrameForToken(getOwnerPid(), getOwnerUid(), + mName, mTransactionName, + vsyncId); + surfaceFrame->setActualQueueTime(stateToCommit->postTime); + // For transactions we set the acquire fence time to the post time as we + // don't have a buffer. For BufferStateLayer it is overridden in + // BufferStateLayer::applyPendingStates + surfaceFrame->setAcquireFenceTime(stateToCommit->postTime); + + mSurfaceFrame = std::move(surfaceFrame); + } + mCurrentState.modified = false; return stateUpdateAvailable; } @@ -993,8 +1033,7 @@ uint32_t Layer::doTransaction(uint32_t flags) { this->contentDirty = true; // we may use linear filtering, if the matrix scales us - const uint8_t type = getActiveTransform(c).getType(); - mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE); + mNeedsFiltering = getActiveTransform(c).needsBilinearFiltering(); } if (mCurrentState.inputInfoChanged) { @@ -1002,6 +1041,12 @@ uint32_t Layer::doTransaction(uint32_t flags) { mCurrentState.inputInfoChanged = false; } + // Add the callbacks from the drawing state into the current state. This is so when the current + // state gets copied to drawing, we don't lose the callback handles that are still in drawing. + for (auto& handle : s.callbackHandles) { + c.callbackHandles.push_back(handle); + } + // Commit the transaction commitTransaction(c); mPendingStatesSnapshot = mPendingStates; @@ -1012,6 +1057,7 @@ uint32_t Layer::doTransaction(uint32_t flags) { void Layer::commitTransaction(const State& stateToCommit) { mDrawingState = stateToCommit; + mFlinger->mFrameTimeline->addSurfaceFrame(std::move(mSurfaceFrame), PresentState::Presented); } uint32_t Layer::getTransactionFlags(uint32_t flags) { @@ -1246,6 +1292,14 @@ bool Layer::setTransparentRegionHint(const Region& transparent) { return true; } +bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) { + mCurrentState.sequence++; + mCurrentState.blurRegions = blurRegions; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + bool Layer::setFlags(uint8_t flags, uint8_t mask) { const uint32_t newFlags = (mCurrentState.flags & ~mask) | (flags & mask); if (mCurrentState.flags == newFlags) return false; @@ -1267,13 +1321,6 @@ bool Layer::setCrop_legacy(const Rect& crop) { return true; } -bool Layer::setOverrideScalingMode(int32_t scalingMode) { - if (scalingMode == mOverrideScalingMode) return false; - mOverrideScalingMode = scalingMode; - setTransactionFlags(eTransactionNeeded); - return true; -} - bool Layer::setMetadata(const LayerMetadata& data) { if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false; mCurrentState.sequence++; @@ -1428,6 +1475,13 @@ bool Layer::setFrameRate(FrameRate frameRate) { return true; } +void Layer::setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime) { + mCurrentState.frameTimelineVsyncId = frameTimelineVsyncId; + mCurrentState.postTime = postTime; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); +} + Layer::FrameRate Layer::getFrameRateForLayerTree() const { const auto frameRate = getDrawingState().frameRate; if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) { @@ -1453,13 +1507,13 @@ void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t } mCurrentState.barrierLayer_legacy = barrierLayer; - mCurrentState.frameNumber_legacy = frameNumber; + mCurrentState.barrierFrameNumber = frameNumber; // We don't set eTransactionNeeded, because just receiving a deferral // request without any other state updates shouldn't actually induce a delay mCurrentState.modified = true; pushPendingState(); mCurrentState.barrierLayer_legacy = nullptr; - mCurrentState.frameNumber_legacy = 0; + mCurrentState.barrierFrameNumber = 0; mCurrentState.modified = false; } @@ -1673,8 +1727,8 @@ void Layer::dumpFrameEvents(std::string& result) { } void Layer::dumpCallingUidPid(std::string& result) const { - StringAppendF(&result, "Layer %s (%s) pid:%d uid:%d\n", getName().c_str(), getType(), - mCallingPid, mCallingUid); + StringAppendF(&result, "Layer %s (%s) callingPid:%d callingUid:%d ownerUid:%d\n", + getName().c_str(), getType(), mCallingPid, mCallingUid, mOwnerUid); } void Layer::onDisconnect() { @@ -1689,7 +1743,7 @@ void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) { if (newTimestamps) { mFlinger->mTimeStats->setPostTime(getSequence(), newTimestamps->frameNumber, - getName().c_str(), newTimestamps->postedTime); + getName().c_str(), mOwnerUid, newTimestamps->postedTime); mFlinger->mTimeStats->setAcquireFence(getSequence(), newTimestamps->frameNumber, newTimestamps->acquireFence); } @@ -1827,7 +1881,7 @@ bool Layer::reparent(const sp<IBinder>& newParentHandle) { onRemovedFromCurrentState(); } - if (callSetTransactionFlags || attachChildren()) { + if (attachChildren() || callSetTransactionFlags) { setTransactionFlags(eTransactionNeeded); } return true; @@ -1855,9 +1909,9 @@ bool Layer::attachChildren() { if (client != nullptr && parentClient != client) { if (child->mLayerDetached) { child->mLayerDetached = false; + child->attachChildren(); changed = true; } - changed |= child->attachChildren(); } } @@ -2133,6 +2187,10 @@ int32_t Layer::getBackgroundBlurRadius() const { return getDrawingState().backgroundBlurRadius; } +const std::vector<BlurRegion>& Layer::getBlurRegions() const { + return getDrawingState().blurRegions; +} + Layer::RoundedCornerState Layer::getRoundedCornerState() const { const auto& p = mDrawingParent.promote(); if (p != nullptr) { @@ -2157,12 +2215,12 @@ Layer::RoundedCornerState Layer::getRoundedCornerState() const { : RoundedCornerState(); } -renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const { +renderengine::ShadowSettings Layer::getShadowSettings(const Rect& layerStackRect) const { renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings; // Shift the spot light x-position to the middle of the display and then // offset it by casting layer's screen pos. - state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left; + state.lightPos.x = (layerStackRect.width() / 2.f) - mScreenBounds.left; state.lightPos.y -= mScreenBounds.top; state.length = mEffectiveShadowRadius; @@ -2202,7 +2260,7 @@ void Layer::setInputInfo(const InputWindowInfo& info) { } LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags, - const DisplayDevice* display) const { + const DisplayDevice* display) { LayerProto* layerProto = layersProto.add_layers(); writeToProtoDrawingState(layerProto, traceFlags, display); writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags); @@ -2223,8 +2281,8 @@ LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags, } void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, - const DisplayDevice* display) const { - ui::Transform transform = getTransform(); + const DisplayDevice* display) { + const ui::Transform transform = getTransform(); if (traceFlags & SurfaceTracing::TRACE_CRITICAL) { for (const auto& pendingState : mPendingStatesSnapshot) { @@ -2232,7 +2290,7 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, if (barrierLayer != nullptr) { BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer(); barrierLayerProto->set_id(barrierLayer->sequence); - barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy); + barrierLayerProto->set_frame_number(pendingState.barrierFrameNumber); } } @@ -2252,6 +2310,7 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, layerInfo->set_effective_scaling_mode(getEffectiveScalingMode()); layerInfo->set_corner_radius(getRoundedCornerState().radius); + layerInfo->set_background_blur_radius(getBackgroundBlurRadius()); LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform()); LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), [&]() { return layerInfo->mutable_position(); }); @@ -2279,7 +2338,7 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, } void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet, - uint32_t traceFlags) const { + uint32_t traceFlags) { const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; const State& state = useDrawing ? mDrawingState : mCurrentState; @@ -2346,10 +2405,19 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet } layerInfo->set_is_relative_of(state.isRelativeOf); + + layerInfo->set_owner_uid(mOwnerUid); } if (traceFlags & SurfaceTracing::TRACE_INPUT) { - LayerProtoHelper::writeToProto(state.inputInfo, state.touchableRegionCrop, + InputWindowInfo info; + if (useDrawing) { + info = fillInputInfo(); + } else { + info = state.inputInfo; + } + + LayerProtoHelper::writeToProto(info, state.touchableRegionCrop, [&]() { return layerInfo->mutable_input_window_info(); }); } @@ -2370,9 +2438,8 @@ InputWindowInfo Layer::fillInputInfo() { mDrawingState.inputInfo.name = getName(); mDrawingState.inputInfo.ownerUid = mCallingUid; mDrawingState.inputInfo.ownerPid = mCallingPid; - mDrawingState.inputInfo.inputFeatures = - InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; - mDrawingState.inputInfo.layoutParamsFlags = InputWindowInfo::FLAG_NOT_TOUCH_MODAL; + mDrawingState.inputInfo.inputFeatures = InputWindowInfo::Feature::NO_INPUT_CHANNEL; + mDrawingState.inputInfo.flags = InputWindowInfo::Flag::NOT_TOUCH_MODAL; mDrawingState.inputInfo.displayId = getLayerStack(); } @@ -2384,17 +2451,8 @@ InputWindowInfo Layer::fillInputInfo() { } ui::Transform t = getTransform(); - const float xScale = t.sx(); - const float yScale = t.sy(); int32_t xSurfaceInset = info.surfaceInset; int32_t ySurfaceInset = info.surfaceInset; - if (xScale != 1.0f || yScale != 1.0f) { - info.windowXScale *= (xScale != 0.0f) ? 1.0f / xScale : 0.0f; - info.windowYScale *= (yScale != 0.0f) ? 1.0f / yScale : 0.0f; - info.touchableRegion.scaleSelf(xScale, yScale); - xSurfaceInset = std::round(xSurfaceInset * xScale); - ySurfaceInset = std::round(ySurfaceInset * yScale); - } // Transform layer size to screen space and inset it by surface insets. // If this is a portal window, set the touchableRegion to the layerBounds. @@ -2404,31 +2462,69 @@ InputWindowInfo Layer::fillInputInfo() { if (!layerBounds.isValid()) { layerBounds = getCroppedBufferSize(getDrawingState()); } - layerBounds = t.transform(layerBounds); + + const float xScale = t.getScaleX(); + const float yScale = t.getScaleY(); + if (xScale != 1.0f || yScale != 1.0f) { + xSurfaceInset = std::round(xSurfaceInset * xScale); + ySurfaceInset = std::round(ySurfaceInset * yScale); + } + + // Transform the layer bounds from layer coordinate space to display coordinate space. + Rect transformedLayerBounds = t.transform(layerBounds); // clamp inset to layer bounds - xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0; - ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0; + xSurfaceInset = (xSurfaceInset >= 0) + ? std::min(xSurfaceInset, transformedLayerBounds.getWidth() / 2) + : 0; + ySurfaceInset = (ySurfaceInset >= 0) + ? std::min(ySurfaceInset, transformedLayerBounds.getHeight() / 2) + : 0; // inset while protecting from overflow TODO(b/161235021): What is going wrong // in the overflow scenario? { int32_t tmp; - if (!__builtin_add_overflow(layerBounds.left, xSurfaceInset, &tmp)) layerBounds.left = tmp; - if (!__builtin_sub_overflow(layerBounds.right, xSurfaceInset, &tmp)) layerBounds.right = tmp; - if (!__builtin_add_overflow(layerBounds.top, ySurfaceInset, &tmp)) layerBounds.top = tmp; - if (!__builtin_sub_overflow(layerBounds.bottom, ySurfaceInset, &tmp)) layerBounds.bottom = tmp; - } - - // Input coordinate should match the layer bounds. - info.frameLeft = layerBounds.left; - info.frameTop = layerBounds.top; - info.frameRight = layerBounds.right; - info.frameBottom = layerBounds.bottom; + if (!__builtin_add_overflow(transformedLayerBounds.left, xSurfaceInset, &tmp)) + transformedLayerBounds.left = tmp; + if (!__builtin_sub_overflow(transformedLayerBounds.right, xSurfaceInset, &tmp)) + transformedLayerBounds.right = tmp; + if (!__builtin_add_overflow(transformedLayerBounds.top, ySurfaceInset, &tmp)) + transformedLayerBounds.top = tmp; + if (!__builtin_sub_overflow(transformedLayerBounds.bottom, ySurfaceInset, &tmp)) + transformedLayerBounds.bottom = tmp; + } + + // Input coordinates should be in display coordinate space. + info.frameLeft = transformedLayerBounds.left; + info.frameTop = transformedLayerBounds.top; + info.frameRight = transformedLayerBounds.right; + info.frameBottom = transformedLayerBounds.bottom; + + // Compute the correct transform to send to input. This will allow it to transform the + // input coordinates from display space into window space. Therefore, it needs to use the + // final layer frame to create the inverse transform. Since surface insets are added later, + // along with the overflow, the best way to ensure we get the correct transform is to use + // the final frame calculated. + // 1. Take the original transform set on the window and get the inverse transform. This is + // used to get the final bounds in display space (ignorning the transform). Apply the + // inverse transform on the layerBounds to get the untransformed frame (in layer space) + // 2. Take the top and left of the untransformed frame to get the real position on screen. + // Apply the layer transform on top/left so it includes any scale or rotation. These will + // be the new translation values for the transform. + // 3. Update the translation of the original transform to the new translation values. + // 4. Send the inverse transform to input so the coordinates can be transformed back into + // window space. + ui::Transform inverseTransform = t.inverse(); + Rect nonTransformedBounds = inverseTransform.transform(transformedLayerBounds); + vec2 translation = t.transform(nonTransformedBounds.left, nonTransformedBounds.top); + ui::Transform inputTransform(t); + inputTransform.set(translation.x, translation.y); + info.transform = inputTransform.inverse(); // Position the touchable region relative to frame screen location and restrict it to frame // bounds. - info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop); + info.touchableRegion = inputTransform.transform(info.touchableRegion); // For compatibility reasons we let layers which can receive input // receive input before they have actually submitted a buffer. Because // of this we use canReceiveInput instead of isVisible to check the @@ -2438,6 +2534,7 @@ InputWindowInfo Layer::fillInputInfo() { // InputDispatcher, and obviously if they aren't visible they can't occlude // anything. info.visible = hasInputInfo() ? canReceiveInput() : isVisible(); + info.alpha = getAlpha(); auto cropLayer = mDrawingState.touchableRegionCrop.promote(); if (info.replaceTouchableRegionWithCrop) { @@ -2582,7 +2679,7 @@ void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLa } // Cloned layers shouldn't handle watch outside since their z order is not determined by // WM or the client. - mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH; + mDrawingState.inputInfo.flags &= ~InputWindowInfo::Flag::WATCH_OUTSIDE_TOUCH; } void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { @@ -2637,6 +2734,16 @@ Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t comp } } +bool Layer::getPrimaryDisplayOnly() const { + const State& s(mDrawingState); + if (s.flags & layer_state_t::eLayerSkipScreenshot) { + return true; + } + + sp<Layer> parent = mDrawingParent.promote(); + return parent == nullptr ? false : parent->getPrimaryDisplayOnly(); +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2c90c92f6c..b1ab9ec306 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -26,6 +26,7 @@ #include <renderengine/Mesh.h> #include <renderengine/Texture.h> #include <sys/types.h> +#include <ui/BlurRegion.h> #include <ui/FloatRect.h> #include <ui/FrameStats.h> #include <ui/GraphicBuffer.h> @@ -55,8 +56,6 @@ using namespace android::surfaceflinger; namespace android { -// --------------------------------------------------------------------------- - class Client; class Colorizer; class DisplayDevice; @@ -73,7 +72,9 @@ namespace impl { class SurfaceInterceptor; } -// --------------------------------------------------------------------------- +namespace frametimeline { +class SurfaceFrame; +} // namespace frametimeline struct LayerCreationArgs { LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h, @@ -106,14 +107,6 @@ class Layer : public virtual RefBase, compositionengine::LayerFE { static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2; public: - mutable bool contentDirty{false}; - Region surfaceDamageRegion; - - // Layer serial number. This gives layers an explicit ordering, so we - // have a stable sort order when their layer stack and Z-order are - // the same. - int32_t sequence{sSequence++}; - enum { // flags for doTransaction() eDontUpdateGeometryState = 0x00000001, eVisibleRegion = 0x00000002, @@ -199,7 +192,7 @@ public: // If set, defers this state update until the identified Layer // receives a frame with the given frameNumber wp<Layer> barrierLayer_legacy; - uint64_t frameNumber_legacy; + uint64_t barrierFrameNumber; // the transparentRegion hint is a bit special, it's latched only // when we receive a buffer -- this is because it's "content" @@ -263,6 +256,9 @@ public: // be rendered around the layer. float shadowRadius; + // Layer regions that are made of custom materials, like frosted glass + std::vector<BlurRegion> blurRegions; + // Priority of the layer assigned by Window Manager. int32_t frameRateSelectionPriority; @@ -279,19 +275,64 @@ public: // a buffer of a different size. ui::Transform::ROT_INVALID means the // a fixed transform hint is not set. ui::Transform::RotationFlags fixedTransformHint; + + // The vsync id that was used to start the transaction + int64_t frameTimelineVsyncId; + + // When the transaction was posted + nsecs_t postTime; + }; + + /* + * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer) + * is called. + */ + class LayerCleaner { + sp<SurfaceFlinger> mFlinger; + sp<Layer> mLayer; + + protected: + ~LayerCleaner() { + // destroy client resources + mFlinger->onHandleDestroyed(mLayer); + } + + public: + LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) + : mFlinger(flinger), mLayer(layer) {} + }; + + /* + * The layer handle is just a BBinder object passed to the client + * (remote process) -- we don't keep any reference on our side such that + * the dtor is called when the remote side let go of its reference. + * + * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for + * this layer when the handle is destroyed. + */ + class Handle : public BBinder, public LayerCleaner { + public: + Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) + : LayerCleaner(flinger, layer), owner(layer) {} + + wp<Layer> owner; }; explicit Layer(const LayerCreationArgs& args); virtual ~Layer(); - void onFirstRef() override; + static bool isLayerFocusedBasedOnPriority(int32_t priority); + static void miniDumpHeader(std::string& result); + static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility); - int getWindowType() const { return mWindowType; } + // Provide unique string for each class type in the Layer hierarchy + virtual const char* getType() const = 0; + + // true if this layer is visible, false otherwise + virtual bool isVisible() const = 0; - void setPrimaryDisplayOnly() { mPrimaryDisplayOnly = true; } - bool getPrimaryDisplayOnly() const { return mPrimaryDisplayOnly; } + virtual sp<Layer> createClone() = 0; - // ------------------------------------------------------------------------ // Geometry setting functions. // // The following group of functions are used to specify the layers @@ -304,7 +345,6 @@ public: // // The first set of geometry functions are controlled by the scaling mode, described // in window.h. The scaling mode may be set by the client, as it submits buffers. - // This value may be overriden through SurfaceControl, with setOverrideScalingMode. // // Put simply, if our scaling mode is SCALING_MODE_FREEZE, then // matrix updates will not be applied while a resize is pending @@ -355,6 +395,7 @@ public: // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which // is specified in pixels. virtual bool setBackgroundBlurRadius(int backgroundBlurRadius); + virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions); virtual bool setTransparentRegionHint(const Region& transparent); virtual bool setFlags(uint8_t flags, uint8_t mask); virtual bool setLayerStack(uint32_t layerStack); @@ -362,15 +403,10 @@ public: virtual void deferTransactionUntil_legacy(const sp<IBinder>& barrierHandle, uint64_t frameNumber); virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber); - virtual bool setOverrideScalingMode(int32_t overrideScalingMode); virtual bool setMetadata(const LayerMetadata& data); - bool reparentChildren(const sp<IBinder>& newParentHandle); - void reparentChildren(const sp<Layer>& newParent); - virtual void setChildrenDrawingParent(const sp<Layer>& layer); + virtual void setChildrenDrawingParent(const sp<Layer>&); virtual bool reparent(const sp<IBinder>& newParentHandle); virtual bool detachChildren(); - bool attachChildren(); - bool isLayerDetached() const { return mLayerDetached; } virtual bool setColorTransform(const mat4& matrix); virtual mat4 getColorTransform() const; virtual bool hasColorTransform() const; @@ -383,7 +419,7 @@ public: virtual bool setFrame(const Rect& /*frame*/) { return false; }; virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/, nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/, - const client_cache_t& /*clientCacheId*/) { + const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */) { return false; }; virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; }; @@ -403,23 +439,13 @@ public: } virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace); virtual bool setColorSpaceAgnostic(const bool agnostic); - bool setShadowRadius(float shadowRadius); virtual bool setFrameRateSelectionPriority(int32_t priority); virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint); // If the variable is not set on the layer, it traverses up the tree to inherit the frame // rate priority from its parent. virtual int32_t getFrameRateSelectionPriority() const; - static bool isLayerFocusedBasedOnPriority(int32_t priority); - virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; } - // Before color management is introduced, contents on Android have to be - // desaturated in order to match what they appears like visually. - // With color management, these contents will appear desaturated, thus - // needed to be saturated so that they match what they are designed for - // visually. - bool isLegacyDataSpace() const; - virtual sp<compositionengine::LayerFE> getCompositionEngineLayerFE() const; virtual compositionengine::LayerFECompositionState* editCompositionState(); @@ -429,49 +455,6 @@ public: virtual void useSurfaceDamage() {} virtual void useEmptyDamage() {} - uint32_t getTransactionFlags() const { return mTransactionFlags; } - uint32_t getTransactionFlags(uint32_t flags); - uint32_t setTransactionFlags(uint32_t flags); - - // Deprecated, please use compositionengine::Output::belongsInOutput() - // instead. - // TODO(lpique): Move the remaining callers (screencap) to the new function. - bool belongsToDisplay(uint32_t layerStack, bool isPrimaryDisplay) const { - return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay); - } - - FloatRect getBounds(const Region& activeTransparentRegion) const; - FloatRect getBounds() const; - - // Compute bounds for the layer and cache the results. - void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius); - - // Returns the buffer scale transform if a scaling mode is set. - ui::Transform getBufferScaleTransform() const; - - // Get effective layer transform, taking into account all its parent transform with any - // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE. - ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const; - - // Returns the bounds of the layer without any buffer scaling. - FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const; - - int32_t getSequence() const { return sequence; } - - // For tracing. - // TODO: Replace with raw buffer id from buffer metadata when that becomes available. - // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces - // creates its tracks by buffer id and has no way of associating a buffer back to the process - // that created it, the current implementation is only sufficient for cases where a buffer is - // only used within a single layer. - uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; } - - // ----------------------------------------------------------------------- - // Virtuals - - // Provide unique string for each class type in the Layer hierarchy - virtual const char* getType() const = 0; - /* * isOpaque - true if this surface is opaque * @@ -482,31 +465,12 @@ public: virtual bool isOpaque(const Layer::State&) const { return false; } /* - * isSecure - true if this surface is secure, that is if it prevents - * screenshots or VNC servers. - */ - bool isSecure() const; - - /* - * isVisible - true if this layer is visible, false otherwise - */ - virtual bool isVisible() const = 0; - - /* - * isHiddenByPolicy - true if this layer has been forced invisible. - * just because this is false, doesn't mean isVisible() is true. - * For example if this layer has no active buffer, it may not be hidden by - * policy, but it still can not be visible. - */ - bool isHiddenByPolicy() const; - - /* * Returns whether this layer can receive input. */ virtual bool canReceiveInput() const; /* - * isProtected - true if the layer may contain protected content in the + * isProtected - true if the layer may contain protected contents in the * GRALLOC_USAGE_PROTECTED sense. */ virtual bool isProtected() const { return false; } @@ -526,22 +490,6 @@ public: // to avoid grabbing the lock again to avoid deadlock virtual bool isCreatedFromMainThread() const { return false; } - bool isRemovedFromCurrentState() const; - - LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, - const DisplayDevice*) const; - - // Write states that are modified by the main thread. This includes drawing - // state as well as buffer data. This should be called in the main or tracing - // thread. - void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, - const DisplayDevice*) const; - // Write drawing or current state. If writing current state, the caller should hold the - // external mStateLock. If writing drawing state, this function should be called on the - // main or tracing thread. - void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet, - uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; - virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; } virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; } virtual uint32_t getActiveHeight(const Layer::State& s) const { return s.active_legacy.h; } @@ -553,6 +501,7 @@ public: } virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; } virtual bool needsFiltering(const DisplayDevice*) const { return false; } + // True if this layer requires filtering // This method is distinct from needsFiltering() in how the filter // requirement is computed. needsFiltering() compares displayFrame and crop, @@ -566,65 +515,16 @@ public: return false; } - // This layer is not a clone, but it's the parent to the cloned hierarchy. The - // variable mClonedChild represents the top layer that will be cloned so this - // layer will be the parent of mClonedChild. - // The layers in the cloned hierarchy will match the lifetime of the real layers. That is - // if the real layer is destroyed, then the clone layer will also be destroyed. - sp<Layer> mClonedChild; - - virtual sp<Layer> createClone() = 0; - void updateMirrorInfo(); virtual void updateCloneBufferInfo(){}; -protected: - sp<compositionengine::LayerFE> asLayerFE() const; - sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; } - bool isClone() { return mClonedFrom != nullptr; } - bool isClonedFromAlive() { return getClonedFrom() != nullptr; } - - virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom); - - void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void updateClonedChildren(const sp<Layer>& mirrorRoot, - std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - void addChildToDrawing(const sp<Layer>& layer); - void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition( - compositionengine::LayerFE::ClientCompositionTargetSettings&); - virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition( - const LayerFE::LayerSettings& layerSettings, const Rect& displayViewport, - ui::Dataspace outputDataspace); - // Modifies the passed in layer settings to clear the contents. If the blackout flag is set, - // the settings clears the content with a solid black fill. - void prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, bool blackout) const; - -public: - /* - * compositionengine::LayerFE overrides - */ - const compositionengine::LayerFECompositionState* getCompositionState() const override; - bool onPreComposition(nsecs_t) override; - void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override; - std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList( - compositionengine::LayerFE::ClientCompositionTargetSettings&) override; - void onLayerDisplayed(const sp<Fence>& releaseFence) override; - const char* getDebugName() const override; - -protected: - void prepareBasicGeometryCompositionState(); - void prepareGeometryCompositionState(); - virtual void preparePerFrameCompositionState(); - void prepareCursorCompositionState(); - -public: virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {} virtual bool isHdrY410() const { return false; } virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; } + virtual uint64_t getHeadFrameNumber(nsecs_t /* expectedPresentTime */) const { return 0; } + /* * called after composition. * returns true if the layer latched a new buffer this frame. @@ -641,11 +541,6 @@ public: virtual void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& /*glDoneFence*/, const CompositorTiming& /*compositorTiming*/) {} - /* - * doTransaction - process the transaction. This is a good place to figure - * out which attributes of the surface have changed. - */ - uint32_t doTransaction(uint32_t transactionFlags); /* * latchBuffer - called each time the screen is redrawn and returns whether @@ -663,62 +558,202 @@ public: virtual void latchAndReleaseBuffer() {} /* - * Remove relative z for the layer if its relative parent is not part of the - * provided layer tree. + * returns the rectangle that crops the content of the layer and scales it + * to the layer's size. */ - void removeRelativeZ(const std::vector<Layer*>& layersInTree); + virtual Rect getBufferCrop() const { return Rect(); } /* - * Remove from current state and mark for removal. + * Returns the transform applied to the buffer. */ - void removeFromCurrentState(); + virtual uint32_t getBufferTransform() const { return 0; } + + virtual sp<GraphicBuffer> getBuffer() const { return nullptr; } + + virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; } /* - * called with the state lock from a binder thread when the layer is - * removed from the current list to the pending removal list + * Returns if a frame is ready */ - void onRemovedFromCurrentState(); + virtual bool hasReadyFrame() const { return false; } + + virtual int32_t getQueuedFrameCount() const { return 0; } + + virtual void pushPendingState(); + + /** + * Returns active buffer size in the correct orientation. Buffer size is determined by undoing + * any buffer transformations. If the layer has no buffer then return INVALID_RECT. + */ + virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; } + + /** + * Returns the source bounds. If the bounds are not defined, it is inferred from the + * buffer size. Failing that, the bounds are determined from the passed in parent bounds. + * For the root layer, this is the display viewport size. + */ + virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const { + return parentBounds; + } + virtual FrameRate getFrameRateForLayerTree() const; + + virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) { + return {}; + } + + virtual bool getTransformToDisplayInverse() const { return false; } + + // Returns how rounded corners should be drawn for this layer. + // This will traverse the hierarchy until it reaches its root, finding topmost rounded + // corner definition and converting it into current layer's coordinates. + // As of now, only 1 corner radius per display list is supported. Subsequent ones will be + // ignored. + virtual RoundedCornerState getRoundedCornerState() const; + + virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {} + virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; } + /** + * Return whether this layer needs an input info. For most layer types + * this is only true if they explicitly set an input-info but BufferLayer + * overrides this so we can generate input-info for Buffered layers that don't + * have them (for input occlusion detection checks). + */ + virtual bool needsInputInfo() const { return hasInputInfo(); } + + // Implements RefBase. + void onFirstRef() override; + + // implements compositionengine::LayerFE + const compositionengine::LayerFECompositionState* getCompositionState() const override; + bool onPreComposition(nsecs_t) override; + void prepareCompositionState(compositionengine::LayerFE::StateSubset subset) override; + std::vector<compositionengine::LayerFE::LayerSettings> prepareClientCompositionList( + compositionengine::LayerFE::ClientCompositionTargetSettings&) override; + void onLayerDisplayed(const sp<Fence>& releaseFence) override; + const char* getDebugName() const override; + + bool reparentChildren(const sp<IBinder>& newParentHandle); + void reparentChildren(const sp<Layer>& newParent); + bool attachChildren(); + bool isLayerDetached() const { return mLayerDetached; } + bool setShadowRadius(float shadowRadius); + + // Before color management is introduced, contents on Android have to be + // desaturated in order to match what they appears like visually. + // With color management, these contents will appear desaturated, thus + // needed to be saturated so that they match what they are designed for + // visually. + bool isLegacyDataSpace() const; + + uint32_t getTransactionFlags() const { return mTransactionFlags; } + uint32_t getTransactionFlags(uint32_t flags); + uint32_t setTransactionFlags(uint32_t flags); + + // Deprecated, please use compositionengine::Output::belongsInOutput() + // instead. + // TODO(lpique): Move the remaining callers (screencap) to the new function. + bool belongsToDisplay(uint32_t layerStack) const { return getLayerStack() == layerStack; } + + FloatRect getBounds(const Region& activeTransparentRegion) const; + FloatRect getBounds() const; + + // Compute bounds for the layer and cache the results. + void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius); + + // Returns the buffer scale transform if a scaling mode is set. + ui::Transform getBufferScaleTransform() const; + + // Get effective layer transform, taking into account all its parent transform with any + // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE. + ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const; + + // Returns the bounds of the layer without any buffer scaling. + FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const; + + int32_t getSequence() const { return sequence; } + + // For tracing. + // TODO: Replace with raw buffer id from buffer metadata when that becomes available. + // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces + // creates its tracks by buffer id and has no way of associating a buffer back to the process + // that created it, the current implementation is only sufficient for cases where a buffer is + // only used within a single layer. + uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; } /* - * Called when the layer is added back to the current state list. + * isSecure - true if this surface is secure, that is if it prevents + * screenshots or VNC servers. A surface can be set to be secure by the + * application, being secure doesn't mean the surface has DRM contents. */ - void addToCurrentState(); + bool isSecure() const; /* - * Sets display transform hint on BufferLayerConsumer. + * isHiddenByPolicy - true if this layer has been forced invisible. + * just because this is false, doesn't mean isVisible() is true. + * For example if this layer has no active buffer, it may not be hidden by + * policy, but it still can not be visible. */ - void updateTransformHint(ui::Transform::RotationFlags); + bool isHiddenByPolicy() const; + + bool isRemovedFromCurrentState() const; + + LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags, const DisplayDevice*); + + // Write states that are modified by the main thread. This includes drawing + // state as well as buffer data. This should be called in the main or tracing + // thread. + void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, const DisplayDevice*); + // Write drawing or current state. If writing current state, the caller should hold the + // external mStateLock. If writing drawing state, this function should be called on the + // main or tracing thread. + void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet, + uint32_t traceFlags = SurfaceTracing::TRACE_ALL); + + InputWindowInfo::Type getWindowType() const { return mWindowType; } + + bool getPrimaryDisplayOnly() const; + + void updateMirrorInfo(); /* - * returns the rectangle that crops the content of the layer and scales it - * to the layer's size. + * doTransaction - process the transaction. This is a good place to figure + * out which attributes of the surface have changed. */ - virtual Rect getBufferCrop() const { return Rect(); } + uint32_t doTransaction(uint32_t transactionFlags); /* - * Returns the transform applied to the buffer. + * Remove relative z for the layer if its relative parent is not part of the + * provided layer tree. */ - virtual uint32_t getBufferTransform() const { return 0; } + void removeRelativeZ(const std::vector<Layer*>& layersInTree); - virtual sp<GraphicBuffer> getBuffer() const { return nullptr; } + /* + * Remove from current state and mark for removal. + */ + void removeFromCurrentState(); - virtual ui::Transform::RotationFlags getTransformHint() const { return ui::Transform::ROT_0; } + /* + * called with the state lock from a binder thread when the layer is + * removed from the current list to the pending removal list + */ + void onRemovedFromCurrentState(); /* - * Returns if a frame is ready + * Called when the layer is added back to the current state list. */ - virtual bool hasReadyFrame() const { return false; } + void addToCurrentState(); - virtual int32_t getQueuedFrameCount() const { return 0; } + /* + * Sets display transform hint on BufferLayerConsumer. + */ + void updateTransformHint(ui::Transform::RotationFlags); - // ----------------------------------------------------------------------- inline const State& getDrawingState() const { return mDrawingState; } inline const State& getCurrentState() const { return mCurrentState; } inline State& getCurrentState() { return mCurrentState; } LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const; - static void miniDumpHeader(std::string& result); void miniDump(std::string& result, const DisplayDevice&) const; void dumpFrameStats(std::string& result) const; void dumpFrameEvents(std::string& result); @@ -726,17 +761,10 @@ public: void clearFrameStats(); void logFrameStats(); void getFrameStats(FrameStats* outStats) const; - - virtual std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool /*forceFlush*/) { - return {}; - } - void onDisconnect(); void addAndGetFrameTimestamps(const NewFrameEventsEntry* newEntry, FrameEventHistoryDelta* outDelta); - virtual bool getTransformToDisplayInverse() const { return false; } - ui::Transform getTransform() const; // Returns the Alpha of the Surface, accounting for the Alpha @@ -753,14 +781,7 @@ public: // is ready to acquire a buffer. ui::Transform::RotationFlags getFixedTransformHint() const; - // Returns how rounded corners should be drawn for this layer. - // This will traverse the hierarchy until it reaches its root, finding topmost rounded - // corner definition and converting it into current layer's coordinates. - // As of now, only 1 corner radius per display list is supported. Subsequent ones will be - // ignored. - virtual RoundedCornerState getRoundedCornerState() const; - - renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const; + renderengine::ShadowSettings getShadowSettings(const Rect& layerStackRect) const; /** * Traverse this layer and it's hierarchy of children directly. Unlike traverseInZOrder @@ -770,17 +791,15 @@ public: * the scene state, but it's also more efficient than traverseInZOrder and so useful for * book-keeping. */ - void traverse(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor); - void traverseInReverseZOrder(LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor); - void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor); + void traverse(LayerVector::StateSet, const LayerVector::Visitor&); + void traverseInReverseZOrder(LayerVector::StateSet, const LayerVector::Visitor&); + void traverseInZOrder(LayerVector::StateSet, const LayerVector::Visitor&); /** * Traverse only children in z order, ignoring relative layers that are not children of the * parent. */ - void traverseChildrenInZOrder(LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor); + void traverseChildrenInZOrder(LayerVector::StateSet, const LayerVector::Visitor&); size_t getChildrenCount() const; @@ -792,7 +811,7 @@ public: // the current state, but should not be called anywhere else! LayerVector& getCurrentChildren() { return mCurrentChildren; } - void addChild(const sp<Layer>& layer); + void addChild(const sp<Layer>&); // Returns index if removed, or negative value otherwise // for symmetry with Vector::remove ssize_t removeChild(const sp<Layer>& layer); @@ -806,23 +825,7 @@ public: // Copy the current list of children to the drawing state. Called by // SurfaceFlinger to complete a transaction. void commitChildList(); - int32_t getZ(LayerVector::StateSet stateSet) const; - virtual void pushPendingState(); - - /** - * Returns active buffer size in the correct orientation. Buffer size is determined by undoing - * any buffer transformations. If the layer has no buffer then return INVALID_RECT. - */ - virtual Rect getBufferSize(const Layer::State&) const { return Rect::INVALID_RECT; } - - /** - * Returns the source bounds. If the bounds are not defined, it is inferred from the - * buffer size. Failing that, the bounds are determined from the passed in parent bounds. - * For the root layer, this is the display viewport size. - */ - virtual FloatRect computeSourceBounds(const FloatRect& parentBounds) const { - return parentBounds; - } + int32_t getZ(LayerVector::StateSet) const; /** * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return @@ -832,53 +835,46 @@ public: */ Rect getCroppedBufferSize(const Layer::State& s) const; - bool setFrameRate(FrameRate frameRate); - virtual FrameRate getFrameRateForLayerTree() const; - static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility); + bool setFrameRate(FrameRate); -protected: - // constant - sp<SurfaceFlinger> mFlinger; - /* - * Trivial class, used to ensure that mFlinger->onLayerDestroyed(mLayer) - * is called. - */ - class LayerCleaner { - sp<SurfaceFlinger> mFlinger; - sp<Layer> mLayer; + virtual void setFrameTimelineVsyncForBuffer(int64_t /*frameTimelineVsyncId*/) {} + void setFrameTimelineVsyncForTransaction(int64_t frameTimelineVsyncId, nsecs_t postTime); - protected: - ~LayerCleaner() { - // destroy client resources - mFlinger->onHandleDestroyed(mLayer); - } + // Creates a new handle each time, so we only expect + // this to be called once. + sp<IBinder> getHandle(); + const std::string& getName() const { return mName; } + bool getPremultipledAlpha() const; + void setInputInfo(const InputWindowInfo& info); - public: - LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) - : mFlinger(flinger), mLayer(layer) {} - }; + InputWindowInfo fillInputInfo(); + /** + * Returns whether this layer has an explicitly set input-info. + */ + bool hasInputInfo() const; - friend class impl::SurfaceInterceptor; + uid_t getOwnerUid() { return mOwnerUid; } - // For unit tests - friend class TestableSurfaceFlinger; - friend class RefreshRateSelectionTest; - friend class SetFrameRateTest; + pid_t getOwnerPid() { return mOwnerPid; } - virtual void commitTransaction(const State& stateToCommit); + // This layer is not a clone, but it's the parent to the cloned hierarchy. The + // variable mClonedChild represents the top layer that will be cloned so this + // layer will be the parent of mClonedChild. + // The layers in the cloned hierarchy will match the lifetime of the real layers. That is + // if the real layer is destroyed, then the clone layer will also be destroyed. + sp<Layer> mClonedChild; - uint32_t getEffectiveUsage(uint32_t usage) const; + mutable bool contentDirty{false}; + Region surfaceDamageRegion; - /** - * Setup rounded corners coordinates of this layer, taking into account the layer bounds and - * crop coordinates, transforming them into layer space. - */ - void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const; - void setParent(const sp<Layer>& layer); - LayerVector makeTraversalList(LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers); - void addZOrderRelative(const wp<Layer>& relative); - void removeZOrderRelative(const wp<Layer>& relative); + // Layer serial number. This gives layers an explicit ordering, so we + // have a stable sort order when their layer stack and Z-order are + // the same. + int32_t sequence{sSequence++}; + bool mPendingHWCDestroy{false}; + +protected: class SyncPoint { public: explicit SyncPoint(uint64_t frameNumber, wp<Layer> requestedSyncLayer) @@ -906,20 +902,21 @@ protected: wp<Layer> mRequestedSyncLayer; }; - // SyncPoints which will be signaled when the correct frame is at the head - // of the queue and dropped after the frame has been latched. Protected by - // mLocalSyncPointMutex. - Mutex mLocalSyncPointMutex; - std::list<std::shared_ptr<SyncPoint>> mLocalSyncPoints; - - // SyncPoints which will be signaled and then dropped when the transaction - // is applied - std::list<std::shared_ptr<SyncPoint>> mRemoteSyncPoints; + friend class impl::SurfaceInterceptor; - // Returns false if the relevant frame has already been latched - bool addSyncPoint(const std::shared_ptr<SyncPoint>& point); + // For unit tests + friend class TestableSurfaceFlinger; + friend class RefreshRateSelectionTest; + friend class SetFrameRateTest; - void popPendingState(State* stateToCommit); + virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom); + virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings&); + virtual std::optional<compositionengine::LayerFE::LayerSettings> prepareShadowClientComposition( + const LayerFE::LayerSettings&, const Rect& layerStackRect, + ui::Dataspace outputDataspace); + virtual void preparePerFrameCompositionState(); + virtual void commitTransaction(const State& stateToCommit); virtual bool applyPendingStates(State* stateToCommit); virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit); @@ -928,58 +925,62 @@ protected: // the Surface Controller) if set. virtual uint32_t getEffectiveScalingMode() const { return 0; } -public: - /* - * The layer handle is just a BBinder object passed to the client - * (remote process) -- we don't keep any reference on our side such that - * the dtor is called when the remote side let go of its reference. - * - * LayerCleaner ensures that mFlinger->onLayerDestroyed() is called for - * this layer when the handle is destroyed. - */ - class Handle : public BBinder, public LayerCleaner { - public: - Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) - : LayerCleaner(flinger, layer), owner(layer) {} + sp<compositionengine::LayerFE> asLayerFE() const; + sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; } + bool isClone() { return mClonedFrom != nullptr; } + bool isClonedFromAlive() { return getClonedFrom() != nullptr; } - wp<Layer> owner; - }; + void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); + void updateClonedChildren(const sp<Layer>& mirrorRoot, + std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); + void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); + void addChildToDrawing(const sp<Layer>&); + void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); - // Creates a new handle each time, so we only expect - // this to be called once. - sp<IBinder> getHandle(); - const std::string& getName() const { return mName; } - virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {} - virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; } - bool getPremultipledAlpha() const; + // Modifies the passed in layer settings to clear the contents. If the blackout flag is set, + // the settings clears the content with a solid black fill. + void prepareClearClientComposition(LayerFE::LayerSettings&, bool blackout) const; - bool mPendingHWCDestroy{false}; - void setInputInfo(const InputWindowInfo& info); + void prepareBasicGeometryCompositionState(); + void prepareGeometryCompositionState(); + void prepareCursorCompositionState(); + + uint32_t getEffectiveUsage(uint32_t usage) const; - InputWindowInfo fillInputInfo(); - /** - * Returns whether this layer has an explicitly set input-info. - */ - bool hasInputInfo() const; /** - * Return whether this layer needs an input info. For most layer types - * this is only true if they explicitly set an input-info but BufferLayer - * overrides this so we can generate input-info for Buffered layers that don't - * have them (for input occlusion detection checks). + * Setup rounded corners coordinates of this layer, taking into account the layer bounds and + * crop coordinates, transforming them into layer space. */ - virtual bool needsInputInfo() const { return hasInputInfo(); } - -protected: + void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const; + void setParent(const sp<Layer>&); + LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers); + void addZOrderRelative(const wp<Layer>& relative); + void removeZOrderRelative(const wp<Layer>& relative); compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const; + bool usingRelativeZ(LayerVector::StateSet) const; + + // SyncPoints which will be signaled when the correct frame is at the head + // of the queue and dropped after the frame has been latched. Protected by + // mLocalSyncPointMutex. + Mutex mLocalSyncPointMutex; + std::list<std::shared_ptr<SyncPoint>> mLocalSyncPoints; - bool usingRelativeZ(LayerVector::StateSet stateSet) const; + // SyncPoints which will be signaled and then dropped when the transaction + // is applied + std::list<std::shared_ptr<SyncPoint>> mRemoteSyncPoints; + + // Returns false if the relevant frame has already been latched + bool addSyncPoint(const std::shared_ptr<SyncPoint>& point); + + void popPendingState(State* stateToCommit); + + // constant + sp<SurfaceFlinger> mFlinger; bool mPremultipliedAlpha{true}; const std::string mName; const std::string mTransactionName{"TX - " + mName}; - bool mPrimaryDisplayOnly = false; - // These are only accessed by the main thread or the tracing thread. State mDrawingState; // Store a copy of the pending state so that the drawing thread can access the @@ -1008,7 +1009,6 @@ protected: bool mIsActiveBufferUpdatedForGpu = true; // We encode unset as -1. - int32_t mOverrideScalingMode{-1}; std::atomic<uint64_t> mCurrentFrameNumber{0}; // Whether filtering is needed b/c of the drawingstate bool mNeedsFiltering{false}; @@ -1040,11 +1040,25 @@ protected: bool mChildrenChanged{false}; // Window types from WindowManager.LayoutParams - const int mWindowType; + const InputWindowInfo::Type mWindowType; + + // Can only be accessed with the SF state lock held. + std::unique_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; + + // The owner pid of the layer. If created from a non system process, it will be the calling pid. + // If created from a system process, the value can be passed in. + pid_t mOwnerPid; private: virtual void setTransformHint(ui::Transform::RotationFlags) {} + // Returns true if the layer can draw shadows on its border. + virtual bool canDrawShadows() const { return true; } + Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const; Region getVisibleRegion(const DisplayDevice*) const; @@ -1052,21 +1066,31 @@ private: * Returns an unsorted vector of all layers that are part of this tree. * That includes the current layer and all its descendants. */ - std::vector<Layer*> getLayersInTree(LayerVector::StateSet stateSet); + std::vector<Layer*> getLayersInTree(LayerVector::StateSet); /** * Traverses layers that are part of this tree in the correct z order. * layersInTree must be sorted before calling this method. */ void traverseChildrenInZOrderInner(const std::vector<Layer*>& layersInTree, - LayerVector::StateSet stateSet, - const LayerVector::Visitor& visitor); - LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet, + LayerVector::StateSet, const LayerVector::Visitor&); + LayerVector makeChildrenTraversalList(LayerVector::StateSet, const std::vector<Layer*>& layersInTree); void updateTreeHasFrameRateVote(); + void setZOrderRelativeOf(const wp<Layer>& relativeOf); + void removeRemoteSyncPoints(); + + // Find the root of the cloned hierarchy, this means the first non cloned parent. + // This will return null if first non cloned parent is not found. + sp<Layer> getClonedRoot(); + + // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is + // null. + sp<Layer> getRootLayer(); // Cached properties computed from drawing state - // Effective transform taking into account parent transforms and any parent scaling. + // Effective transform taking into account parent transforms and any parent scaling, which is + // a transform from the current layer coordinate space to display(screen) coordinate space. ui::Transform mEffectiveTransform; // Bounds of the layer before any transformation is applied and before it has been cropped @@ -1080,12 +1104,8 @@ private: // Layer bounds in screen space. FloatRect mScreenBounds; - void setZOrderRelativeOf(const wp<Layer>& relativeOf); - bool mGetHandleCalled = false; - void removeRemoteSyncPoints(); - // Tracks the process and user id of the caller when creating this layer // to help debugging. pid_t mCallingPid; @@ -1102,16 +1122,8 @@ private: // shadow radius is the set shadow radius, otherwise its the parent's shadow radius. float mEffectiveShadowRadius = 0.f; - // Returns true if the layer can draw shadows on its border. - virtual bool canDrawShadows() const { return true; } - - // Find the root of the cloned hierarchy, this means the first non cloned parent. - // This will return null if first non cloned parent is not found. - sp<Layer> getClonedRoot(); - - // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is - // null. - sp<Layer> getRootLayer(); + // A list of regions on this layer that should have blurs. + const std::vector<BlurRegion>& getBlurRegions() const; }; } // namespace android diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 0fe1421926..59fad9bf0f 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -131,8 +131,12 @@ void LayerProtoHelper::writeToProto( } InputWindowInfoProto* proto = getInputWindowInfoProto(); - proto->set_layout_params_flags(inputInfo.layoutParamsFlags); - proto->set_layout_params_type(inputInfo.layoutParamsType); + proto->set_layout_params_flags(inputInfo.flags.get()); + using U = std::underlying_type_t<InputWindowInfo::Type>; + // TODO(b/129481165): This static assert can be safely removed once conversion warnings + // are re-enabled. + static_assert(std::is_same_v<U, int32_t>); + proto->set_layout_params_type(static_cast<U>(inputInfo.type)); LayerProtoHelper::writeToProto({inputInfo.frameLeft, inputInfo.frameTop, inputInfo.frameRight, inputInfo.frameBottom}, @@ -142,13 +146,11 @@ void LayerProtoHelper::writeToProto( proto->set_surface_inset(inputInfo.surfaceInset); proto->set_visible(inputInfo.visible); - proto->set_can_receive_keys(inputInfo.canReceiveKeys); - proto->set_has_focus(inputInfo.hasFocus); + proto->set_focusable(inputInfo.focusable); proto->set_has_wallpaper(inputInfo.hasWallpaper); proto->set_global_scale_factor(inputInfo.globalScaleFactor); - proto->set_window_x_scale(inputInfo.windowXScale); - proto->set_window_y_scale(inputInfo.windowYScale); + LayerProtoHelper::writeToProto(inputInfo.transform, proto->mutable_transform()); proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop); auto cropLayer = touchableRegionBounds.promote(); if (cropLayer != nullptr) { diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp index e6c8654cbf..053b7f741e 100644 --- a/services/surfaceflinger/LayerRejecter.cpp +++ b/services/surfaceflinger/LayerRejecter.cpp @@ -29,13 +29,12 @@ namespace android { LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions, bool stickySet, const std::string& name, - int32_t overrideScalingMode, bool transformToDisplayInverse) + bool transformToDisplayInverse) : mFront(front), mCurrent(current), mRecomputeVisibleRegions(recomputeVisibleRegions), mStickyTransformSet(stickySet), mName(name), - mOverrideScalingMode(overrideScalingMode), mTransformToDisplayInverse(transformToDisplayInverse) {} bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) { @@ -59,7 +58,7 @@ bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) } } - int actualScalingMode = mOverrideScalingMode >= 0 ? mOverrideScalingMode : item.mScalingMode; + int actualScalingMode = item.mScalingMode; bool isFixedSize = actualScalingMode != NATIVE_WINDOW_SCALING_MODE_FREEZE; if (mFront.active_legacy != mFront.requested_legacy) { if (isFixedSize || diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h index fb5c750571..4981f451d9 100644 --- a/services/surfaceflinger/LayerRejecter.h +++ b/services/surfaceflinger/LayerRejecter.h @@ -24,7 +24,7 @@ namespace android { class LayerRejecter : public BufferLayerConsumer::BufferRejecter { public: LayerRejecter(Layer::State& front, Layer::State& current, bool& recomputeVisibleRegions, - bool stickySet, const std::string& name, int32_t overrideScalingMode, + bool stickySet, const std::string& name, bool transformToDisplayInverse); virtual bool reject(const sp<GraphicBuffer>&, const BufferItem&); @@ -35,7 +35,6 @@ private: bool& mRecomputeVisibleRegions; const bool mStickyTransformSet; const std::string& mName; - const int32_t mOverrideScalingMode; const bool mTransformToDisplayInverse; }; diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp new file mode 100644 index 0000000000..e84508febc --- /dev/null +++ b/services/surfaceflinger/LayerRenderArea.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/GraphicTypes.h> +#include <ui/Transform.h> + +#include "ContainerLayer.h" +#include "DisplayDevice.h" +#include "Layer.h" +#include "LayerRenderArea.h" +#include "SurfaceFlinger.h" + +namespace android { +namespace { + +struct ReparentForDrawing { + const sp<Layer>& oldParent; + + ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent, + const Rect& drawingBounds) + : oldParent(oldParent) { + // Compute and cache the bounds for the new parent layer. + newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(), + 0.f /* shadowRadius */); + oldParent->setChildrenDrawingParent(newParent); + } + ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); } +}; + +} // namespace + +LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, + ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly, + const Rect& layerStackRect, bool allowSecureLayers) + : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, layerStackRect, allowSecureLayers), + mLayer(std::move(layer)), + mCrop(crop), + mFlinger(flinger), + mChildrenOnly(childrenOnly) {} + +const ui::Transform& LayerRenderArea::getTransform() const { + return mTransform; +} + +Rect LayerRenderArea::getBounds() const { + return mLayer->getBufferSize(mLayer->getDrawingState()); +} + +int LayerRenderArea::getHeight() const { + return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight(); +} + +int LayerRenderArea::getWidth() const { + return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth(); +} + +bool LayerRenderArea::isSecure() const { + return mAllowSecureLayers; +} + +bool LayerRenderArea::needsFiltering() const { + return mNeedsFiltering; +} + +sp<const DisplayDevice> LayerRenderArea::getDisplayDevice() const { + return nullptr; +} + +Rect LayerRenderArea::getSourceCrop() const { + if (mCrop.isEmpty()) { + return getBounds(); + } else { + return mCrop; + } +} + +void LayerRenderArea::render(std::function<void()> drawLayers) { + using namespace std::string_literals; + + const Rect sourceCrop = getSourceCrop(); + // no need to check rotation because there is none + mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight(); + + if (!mChildrenOnly) { + mTransform = mLayer->getTransform().inverse(); + drawLayers(); + } else { + uint32_t w = static_cast<uint32_t>(getWidth()); + uint32_t h = static_cast<uint32_t>(getHeight()); + // In the "childrenOnly" case we reparent the children to a screenshot + // layer which has no properties set and which does not draw. + sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer( + {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()}); + + ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop); + drawLayers(); + } +} + +} // namespace android diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h new file mode 100644 index 0000000000..6a906944a7 --- /dev/null +++ b/services/surfaceflinger/LayerRenderArea.h @@ -0,0 +1,61 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> + +#include <ui/GraphicTypes.h> +#include <ui/Transform.h> +#include <utils/StrongPointer.h> + +#include "RenderArea.h" + +namespace android { + +class DisplayDevice; +class Layer; +class SurfaceFlinger; + +class LayerRenderArea : public RenderArea { +public: + LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize, + ui::Dataspace reqDataSpace, bool childrenOnly, const Rect& layerStackRect, + bool allowSecureLayers); + + const ui::Transform& getTransform() const override; + Rect getBounds() const override; + int getHeight() const override; + int getWidth() const override; + bool isSecure() const override; + bool needsFiltering() const override; + sp<const DisplayDevice> getDisplayDevice() const override; + Rect getSourceCrop() const override; + + void render(std::function<void()> drawLayers) override; + +private: + const sp<Layer> mLayer; + const Rect mCrop; + + ui::Transform mTransform; + bool mNeedsFiltering = false; + + SurfaceFlinger& mFlinger; + const bool mChildrenOnly; +}; + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index f602412930..f676d5b44b 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -106,52 +106,83 @@ void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, cons drawSegment(Segment::Buttom, left, color, buffer, pixels); } -sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, - const half4& color) { - if (number < 0 || number > 1000) return nullptr; +std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber( + int number, const half4& color, bool showSpinner) { + if (number < 0 || number > 1000) return {}; const auto hundreds = number / 100; const auto tens = (number / 10) % 10; const auto ones = number % 10; - sp<GraphicBuffer> buffer = - new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, - GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | - GRALLOC_USAGE_HW_TEXTURE, - "RefreshRateOverlayBuffer"); - uint8_t* pixels; - buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); - // Clear buffer content - drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels); - int left = 0; - if (hundreds != 0) { - drawDigit(hundreds, left, color, buffer, pixels); + std::vector<sp<GraphicBuffer>> buffers; + const auto loopCount = showSpinner ? 6 : 1; + for (int i = 0; i < loopCount; i++) { + sp<GraphicBuffer> buffer = + new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, + GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | + GRALLOC_USAGE_HW_TEXTURE, + "RefreshRateOverlayBuffer"); + uint8_t* pixels; + buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); + // Clear buffer content + drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels); + int left = 0; + if (hundreds != 0) { + drawDigit(hundreds, left, color, buffer, pixels); + } left += DIGIT_WIDTH + DIGIT_SPACE; - } - if (tens != 0) { - drawDigit(tens, left, color, buffer, pixels); + if (tens != 0) { + drawDigit(tens, left, color, buffer, pixels); + } left += DIGIT_WIDTH + DIGIT_SPACE; - } - drawDigit(ones, left, color, buffer, pixels); - buffer->unlock(); - return buffer; + drawDigit(ones, left, color, buffer, pixels); + left += DIGIT_WIDTH + DIGIT_SPACE; + + if (showSpinner) { + switch (i) { + case 0: + drawSegment(Segment::Upper, left, color, buffer, pixels); + break; + case 1: + drawSegment(Segment::UpperRight, left, color, buffer, pixels); + break; + case 2: + drawSegment(Segment::LowerRight, left, color, buffer, pixels); + break; + case 3: + drawSegment(Segment::Buttom, left, color, buffer, pixels); + break; + case 4: + drawSegment(Segment::LowerLeft, left, color, buffer, pixels); + break; + case 5: + drawSegment(Segment::UpperLeft, left, color, buffer, pixels); + break; + } + } + + buffer->unlock(); + buffers.emplace_back(buffer); + } + return buffers; } -RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger) - : mFlinger(flinger), mClient(new Client(&mFlinger)) { +RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, bool showSpinner) + : mFlinger(flinger), mClient(new Client(&mFlinger)), mShowSpinner(showSpinner) { createLayer(); primeCache(); } bool RefreshRateOverlay::createLayer() { + int32_t layerId; const status_t ret = mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(), PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(), - &mIBinder, &mGbp, nullptr); + &mIBinder, &mGbp, nullptr, &layerId); if (ret) { ALOGE("failed to create buffer state layer"); return false; @@ -176,7 +207,7 @@ void RefreshRateOverlay::primeCache() { if (allRefreshRates.size() == 1) { auto fps = allRefreshRates.begin()->second->getFps(); half4 color = {LOW_FPS_COLOR, ALPHA}; - mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color)); + mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner)); return; } @@ -196,12 +227,12 @@ void RefreshRateOverlay::primeCache() { color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale); color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale); color.a = ALPHA; - mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color)); + mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner)); } } void RefreshRateOverlay::setViewport(ui::Size viewport) { - Rect frame(viewport.width >> 3, viewport.height >> 5); + Rect frame((3 * viewport.width) >> 4, viewport.height >> 5); frame.offsetBy(viewport.width >> 5, viewport.height >> 4); mLayer->setFrame(frame); @@ -209,8 +240,22 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { } void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) { - auto buffer = mBufferCache[refreshRate.getFps()]; - mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {}); + mCurrentFps = refreshRate.getFps(); + auto buffer = mBufferCache[*mCurrentFps][mFrame]; + mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {}, + mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */)); + + mFlinger.mTransactionFlags.fetch_or(eTransactionMask); +} + +void RefreshRateOverlay::onInvalidate() { + if (!mCurrentFps.has_value()) return; + + const auto& buffers = mBufferCache[*mCurrentFps]; + mFrame = (mFrame + 1) % buffers.size(); + auto buffer = buffers[mFrame]; + mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, {}, + mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */)); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index 35c80201d7..1a8938fbd4 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -38,15 +38,17 @@ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; class RefreshRateOverlay { public: - explicit RefreshRateOverlay(SurfaceFlinger&); + RefreshRateOverlay(SurfaceFlinger&, bool showSpinner); void setViewport(ui::Size); void changeRefreshRate(const RefreshRate&); + void onInvalidate(); private: class SevenSegmentDrawer { public: - static sp<GraphicBuffer> drawNumber(int number, const half4& color); + static std::vector<sp<GraphicBuffer>> drawNumber(int number, const half4& color, + bool showSpinner); static uint32_t getHeight() { return BUFFER_HEIGHT; } static uint32_t getWidth() { return BUFFER_WIDTH; } @@ -65,7 +67,7 @@ private: static constexpr uint32_t DIGIT_SPACE = 16; static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT; static constexpr uint32_t BUFFER_WIDTH = - 3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit + 4 * DIGIT_WIDTH + 3 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit|Space|Spinner }; bool createLayer(); @@ -77,11 +79,14 @@ private: sp<IBinder> mIBinder; sp<IGraphicBufferProducer> mGbp; - std::unordered_map<int, sp<GraphicBuffer>> mBufferCache; - + std::unordered_map<int, std::vector<sp<GraphicBuffer>>> mBufferCache; + std::optional<int> mCurrentFps; + int mFrame = 0; static constexpr float ALPHA = 0.8f; const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f); const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f); + + const bool mShowSpinner; }; } // namespace android diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 27353d8c0b..b7b7e4658e 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -29,14 +29,17 @@ #include <compositionengine/impl/OutputCompositionState.h> #include <cutils/properties.h> #include <gui/IRegionSamplingListener.h> +#include <gui/SyncScreenCaptureListener.h> #include <ui/DisplayStatInfo.h> #include <utils/Trace.h> #include <string> #include "DisplayDevice.h" +#include "DisplayRenderArea.h" #include "Layer.h" -#include "Scheduler/DispSync.h" +#include "Promise.h" +#include "Scheduler/VsyncController.h" #include "SurfaceFlinger.h" namespace android { @@ -59,7 +62,7 @@ enum class samplingStep { constexpr auto timeForRegionSampling = 5000000ns; constexpr auto maxRegionSamplingSkips = 10; -constexpr auto defaultRegionSamplingOffset = -3ms; +constexpr auto defaultRegionSamplingWorkDuration = 3ms; constexpr auto defaultRegionSamplingPeriod = 100ms; constexpr auto defaultRegionSamplingTimerTimeout = 100ms; // TODO: (b/127403193) duration to string conversion could probably be constexpr @@ -71,9 +74,9 @@ inline std::string toNsString(std::chrono::duration<Rep, Per> t) { RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() { char value[PROPERTY_VALUE_MAX] = {}; - property_get("debug.sf.region_sampling_offset_ns", value, - toNsString(defaultRegionSamplingOffset).c_str()); - int const samplingOffsetNsRaw = atoi(value); + property_get("debug.sf.region_sampling_duration_ns", value, + toNsString(defaultRegionSamplingWorkDuration).c_str()); + int const samplingDurationNsRaw = atoi(value); property_get("debug.sf.region_sampling_period_ns", value, toNsString(defaultRegionSamplingPeriod).c_str()); @@ -85,22 +88,26 @@ RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() { if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) { ALOGW("User-specified sampling tuning options nonsensical. Using defaults"); - mSamplingOffset = defaultRegionSamplingOffset; + mSamplingDuration = defaultRegionSamplingWorkDuration; mSamplingPeriod = defaultRegionSamplingPeriod; mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout; } else { - mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw); + mSamplingDuration = std::chrono::nanoseconds(samplingDurationNsRaw); mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw); mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw); } } -struct SamplingOffsetCallback : DispSync::Callback { +struct SamplingOffsetCallback : VSyncSource::Callback { SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler, - std::chrono::nanoseconds targetSamplingOffset) + std::chrono::nanoseconds targetSamplingWorkDuration) : mRegionSamplingThread(samplingThread), - mScheduler(scheduler), - mTargetSamplingOffset(targetSamplingOffset) {} + mTargetSamplingWorkDuration(targetSamplingWorkDuration), + mVSyncSource(scheduler.makePrimaryDispSyncSource("SamplingThreadDispSyncListener", 0ns, + 0ns, + /*traceVsync=*/false)) { + mVSyncSource->setCallback(this); + } ~SamplingOffsetCallback() { stopVsyncListener(); } @@ -112,8 +119,7 @@ struct SamplingOffsetCallback : DispSync::Callback { if (mVsyncListening) return; mPhaseIntervalSetting = Phase::ZERO; - mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this, - mLastCallbackTime); + mVSyncSource->setVSyncEnabled(true); mVsyncListening = true; } @@ -126,23 +132,24 @@ private: void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ { if (!mVsyncListening) return; - mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime); + mVSyncSource->setVSyncEnabled(false); mVsyncListening = false; } - void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) final { + void onVSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/, + nsecs_t /*deadlineTimestamp*/) final { std::unique_lock<decltype(mMutex)> lock(mMutex); if (mPhaseIntervalSetting == Phase::ZERO) { ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase)); mPhaseIntervalSetting = Phase::SAMPLING; - mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count()); + mVSyncSource->setDuration(mTargetSamplingWorkDuration, 0ns); return; } if (mPhaseIntervalSetting == Phase::SAMPLING) { mPhaseIntervalSetting = Phase::ZERO; - mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0); + mVSyncSource->setDuration(0ns, 0ns); stopVsyncListenerLocked(); lock.unlock(); mRegionSamplingThread.notifySamplingOffset(); @@ -151,16 +158,15 @@ private: } RegionSamplingThread& mRegionSamplingThread; - Scheduler& mScheduler; - const std::chrono::nanoseconds mTargetSamplingOffset; + const std::chrono::nanoseconds mTargetSamplingWorkDuration; mutable std::mutex mMutex; - nsecs_t mLastCallbackTime = 0; enum class Phase { ZERO, SAMPLING } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/ = Phase::ZERO; bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false; + std::unique_ptr<VSyncSource> mVSyncSource; }; RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler, @@ -168,11 +174,12 @@ RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& s : mFlinger(flinger), mScheduler(scheduler), mTunables(tunables), - mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>( - mTunables.mSamplingTimerTimeout), - [] {}, [this] { checkForStaleLuma(); }), + mIdleTimer( + std::chrono::duration_cast<std::chrono::milliseconds>( + mTunables.mSamplingTimerTimeout), + [] {}, [this] { checkForStaleLuma(); }), mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler, - tunables.mSamplingOffset)), + tunables.mSamplingDuration)), lastSampleTime(0ns) { mThread = std::thread([this]() { threadMain(); }); pthread_setname_np(mThread.native_handle(), "RegionSamplingThread"); @@ -181,7 +188,7 @@ RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& s RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler) : RegionSamplingThread(flinger, scheduler, - TimingTunables{defaultRegionSamplingOffset, + TimingTunables{defaultRegionSamplingWorkDuration, defaultRegionSamplingPeriod, defaultRegionSamplingTimerTimeout}) {} @@ -243,7 +250,7 @@ void RegionSamplingThread::doSample() { // until the next vsync deadline, defer this sampling work // to a later frame, when hopefully there will be more time. DisplayStatInfo stats; - mScheduler.getDisplayStatInfo(&stats); + mScheduler.getDisplayStatInfo(&stats, systemTime()); if (std::chrono::nanoseconds(stats.vsyncTime) - now < timeForRegionSampling) { ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForQuietFrame)); mDiscardedFrames++; @@ -336,8 +343,20 @@ void RegionSamplingThread::captureSample() { return; } - const auto device = mFlinger.getDefaultDisplayDevice(); - const auto orientation = ui::Transform::toRotationFlags(device->getOrientation()); + wp<const DisplayDevice> displayWeak; + + ui::LayerStack layerStack; + ui::Transform::RotationFlags orientation; + ui::Size displaySize; + + { + // TODO(b/159112860): Don't keep sp<DisplayDevice> outside of SF main thread + const sp<const DisplayDevice> display = mFlinger.getDefaultDisplayDevice(); + displayWeak = display; + layerStack = display->getLayerStack(); + orientation = ui::Transform::toRotationFlags(display->getOrientation()); + displaySize = display->getSize(); + } std::vector<RegionSamplingThread::Descriptor> descriptors; Region sampleRegion; @@ -346,20 +365,18 @@ void RegionSamplingThread::captureSample() { descriptors.emplace_back(descriptor); } - const Rect sampledArea = sampleRegion.bounds(); - auto dx = 0; auto dy = 0; switch (orientation) { case ui::Transform::ROT_90: - dx = device->getWidth(); + dx = displaySize.getWidth(); break; case ui::Transform::ROT_180: - dx = device->getWidth(); - dy = device->getHeight(); + dx = displaySize.getWidth(); + dy = displaySize.getHeight(); break; case ui::Transform::ROT_270: - dy = device->getHeight(); + dy = displaySize.getHeight(); break; default: break; @@ -368,8 +385,14 @@ void RegionSamplingThread::captureSample() { ui::Transform t(orientation); auto screencapRegion = t.transform(sampleRegion); screencapRegion = screencapRegion.translate(dx, dy); - DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(), - sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation); + + const Rect sampledBounds = sampleRegion.bounds(); + + SurfaceFlinger::RenderAreaFuture renderAreaFuture = promise::defer([=] { + return DisplayRenderArea::create(displayWeak, screencapRegion.bounds(), + sampledBounds.getSize(), ui::Dataspace::V0_SRGB, + orientation); + }); std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners; @@ -393,9 +416,9 @@ void RegionSamplingThread::captureSample() { constexpr bool roundOutwards = true; Rect transformed = transform.transform(bounds, roundOutwards); - // If this layer doesn't intersect with the larger sampledArea, skip capturing it + // If this layer doesn't intersect with the larger sampledBounds, skip capturing it Rect ignore; - if (!transformed.intersect(sampledArea, &ignore)) return; + if (!transformed.intersect(sampledBounds, &ignore)) return; // If the layer doesn't intersect a sampling area, skip capturing it bool intersectsAnyArea = false; @@ -411,22 +434,24 @@ void RegionSamplingThread::captureSample() { bounds.top, bounds.right, bounds.bottom); visitor(layer); }; - mFlinger.traverseLayersInDisplay(device, filterVisitor); + mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor); }; sp<GraphicBuffer> buffer = nullptr; - if (mCachedBuffer && mCachedBuffer->getWidth() == sampledArea.getWidth() && - mCachedBuffer->getHeight() == sampledArea.getHeight()) { + if (mCachedBuffer && mCachedBuffer->getWidth() == sampledBounds.getWidth() && + mCachedBuffer->getHeight() == sampledBounds.getHeight()) { buffer = mCachedBuffer; } else { - const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; - buffer = new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(), + const uint32_t usage = + 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"); } - bool ignored; - mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false /* identityTransform */, - true /* regionSampling */, ignored); + const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, + true /* regionSampling */, captureListener); + ScreenCaptureResults captureResults = captureListener->waitForResults(); std::vector<Descriptor> activeDescriptors; for (const auto& descriptor : descriptors) { @@ -437,7 +462,7 @@ void RegionSamplingThread::captureSample() { ALOGV("Sampling %zu descriptors", activeDescriptors.size()); std::vector<float> lumas = - sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation); + sampleBuffer(buffer, sampledBounds.leftTop(), activeDescriptors, orientation); if (lumas.size() != activeDescriptors.size()) { ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(), activeDescriptors.size()); diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h index b9b7a3c436..0defdb3fcb 100644 --- a/services/surfaceflinger/RegionSamplingThread.h +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -43,10 +43,10 @@ float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t st class RegionSamplingThread : public IBinder::DeathRecipient { public: struct TimingTunables { - // debug.sf.sampling_offset_ns - // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline - // at which the sampling should start. - std::chrono::nanoseconds mSamplingOffset; + // debug.sf.sampling_duration_ns + // When asynchronously collecting sample, the duration, at which the sampling should start + // before a vsync + std::chrono::nanoseconds mSamplingDuration; // debug.sf.sampling_period_ns // This is the maximum amount of time the luma recieving client // should have to wait for a new luma value after a frame is updated. The inverse of this is diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h index 6b0455ae87..c9f7f46953 100644 --- a/services/surfaceflinger/RenderArea.h +++ b/services/surfaceflinger/RenderArea.h @@ -23,15 +23,15 @@ public: static float getCaptureFillValue(CaptureFill captureFill); - RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill, - ui::Dataspace reqDataSpace, const Rect& displayViewport, + RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace, + const Rect& layerStackRect, bool allowSecureLayers = false, RotationFlags rotation = ui::Transform::ROT_0) - : mReqWidth(reqWidth), - mReqHeight(reqHeight), + : mAllowSecureLayers(allowSecureLayers), + mReqSize(reqSize), mReqDataSpace(reqDataSpace), mCaptureFill(captureFill), mRotationFlags(rotation), - mDisplayViewport(displayViewport) {} + mLayerStackSpaceRect(layerStackRect) {} virtual ~RenderArea() = default; @@ -70,8 +70,8 @@ public: RotationFlags getRotationFlags() const { return mRotationFlags; } // Returns the size of the physical render area. - int getReqWidth() const { return static_cast<int>(mReqWidth); } - int getReqHeight() const { return static_cast<int>(mReqHeight); } + int getReqWidth() const { return mReqSize.width; } + int getReqHeight() const { return mReqSize.height; } // Returns the composition data space of the render area. ui::Dataspace getReqDataSpace() const { return mReqDataSpace; } @@ -83,15 +83,17 @@ public: virtual sp<const DisplayDevice> getDisplayDevice() const = 0; // Returns the source display viewport. - const Rect& getDisplayViewport() const { return mDisplayViewport; } + const Rect& getLayerStackSpaceRect() const { return mLayerStackSpaceRect; } + +protected: + const bool mAllowSecureLayers; private: - const uint32_t mReqWidth; - const uint32_t mReqHeight; + const ui::Size mReqSize; const ui::Dataspace mReqDataSpace; const CaptureFill mCaptureFill; const RotationFlags mRotationFlags; - const Rect mDisplayViewport; + const Rect mLayerStackSpaceRect; }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp deleted file mode 100644 index 46112f5cf4..0000000000 --- a/services/surfaceflinger/Scheduler/DispSync.cpp +++ /dev/null @@ -1,877 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS -//#define LOG_NDEBUG 0 - -// This is needed for stdint.h to define INT64_MAX in C++ -#define __STDC_LIMIT_MACROS - -#include <math.h> - -#include <algorithm> - -#include <android-base/stringprintf.h> -#include <cutils/properties.h> -#include <log/log.h> -#include <utils/Thread.h> -#include <utils/Trace.h> - -#include <ui/FenceTime.h> - -#include "DispSync.h" -#include "EventLog/EventLog.h" -#include "SurfaceFlinger.h" - -using android::base::StringAppendF; -using std::max; -using std::min; - -namespace android { - -DispSync::~DispSync() = default; -DispSync::Callback::~Callback() = default; - -namespace impl { - -// Setting this to true adds a zero-phase tracer for correlating with hardware -// vsync events -static const bool kEnableZeroPhaseTracer = false; - -// This is the threshold used to determine when hardware vsync events are -// needed to re-synchronize the software vsync model with the hardware. The -// error metric used is the mean of the squared difference between each -// present time and the nearest software-predicted vsync. -static const nsecs_t kErrorThreshold = 160000000000; // 400 usec squared - -#undef LOG_TAG -#define LOG_TAG "DispSyncThread" -class DispSyncThread : public Thread { -public: - DispSyncThread(const char* name, bool showTraceDetailedInfo) - : mName(name), - mStop(false), - mModelLocked("DispSync:ModelLocked", false), - mPeriod(0), - mPhase(0), - mReferenceTime(0), - mWakeupLatency(0), - mFrameNumber(0), - mTraceDetailedInfo(showTraceDetailedInfo) {} - - virtual ~DispSyncThread() {} - - void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) { - if (mTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - - mPhase = phase; - const bool referenceTimeChanged = mReferenceTime != referenceTime; - mReferenceTime = referenceTime; - if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) { - // Inflate the reference time to be the most recent predicted - // vsync before the current time. - const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - const nsecs_t baseTime = now - mReferenceTime; - const nsecs_t numOldPeriods = baseTime / mPeriod; - mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod; - } - mPeriod = period; - if (!mModelLocked && referenceTimeChanged) { - for (auto& eventListener : mEventListeners) { - eventListener.mLastEventTime = mReferenceTime + mPhase + eventListener.mPhase; - // If mLastEventTime is after mReferenceTime (can happen when positive phase offsets - // are used) we treat it as like it happened in previous period. - if (eventListener.mLastEventTime > mReferenceTime) { - eventListener.mLastEventTime -= mPeriod; - } - } - } - if (mTraceDetailedInfo) { - ATRACE_INT64("DispSync:Period", mPeriod); - ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2); - ATRACE_INT64("DispSync:Reference Time", mReferenceTime); - } - ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64 - " mReferenceTime = %" PRId64, - mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime)); - mCond.signal(); - } - - void stop() { - if (mTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - mStop = true; - mCond.signal(); - } - - void lockModel() { - Mutex::Autolock lock(mMutex); - mModelLocked = true; - } - - void unlockModel() { - Mutex::Autolock lock(mMutex); - mModelLocked = false; - } - - virtual bool threadLoop() { - status_t err; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - - while (true) { - std::vector<CallbackInvocation> callbackInvocations; - - nsecs_t targetTime = 0; - - { // Scope for lock - Mutex::Autolock lock(mMutex); - - if (mTraceDetailedInfo) { - ATRACE_INT64("DispSync:Frame", mFrameNumber); - } - ALOGV("[%s] Frame %" PRId64, mName, mFrameNumber); - ++mFrameNumber; - - if (mStop) { - return false; - } - - if (mPeriod == 0) { - err = mCond.wait(mMutex); - if (err != NO_ERROR) { - ALOGE("error waiting for new events: %s (%d)", strerror(-err), err); - return false; - } - continue; - } - - targetTime = computeNextEventTimeLocked(now); - - bool isWakeup = false; - - if (now < targetTime) { - if (mTraceDetailedInfo) ATRACE_NAME("DispSync waiting"); - - if (targetTime == INT64_MAX) { - ALOGV("[%s] Waiting forever", mName); - err = mCond.wait(mMutex); - } else { - ALOGV("[%s] Waiting until %" PRId64, mName, ns2us(targetTime)); - err = mCond.waitRelative(mMutex, targetTime - now); - } - - if (err == TIMED_OUT) { - isWakeup = true; - } else if (err != NO_ERROR) { - ALOGE("error waiting for next event: %s (%d)", strerror(-err), err); - return false; - } - } - - now = systemTime(SYSTEM_TIME_MONOTONIC); - - // Don't correct by more than 1.5 ms - static const nsecs_t kMaxWakeupLatency = us2ns(1500); - - if (isWakeup) { - mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64; - mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency); - if (mTraceDetailedInfo) { - ATRACE_INT64("DispSync:WakeupLat", now - targetTime); - ATRACE_INT64("DispSync:AvgWakeupLat", mWakeupLatency); - } - } - - callbackInvocations = - gatherCallbackInvocationsLocked(now, computeNextRefreshLocked(0, now)); - } - - if (callbackInvocations.size() > 0) { - fireCallbackInvocations(callbackInvocations); - } - } - - return false; - } - - status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback, - nsecs_t lastCallbackTime) { - if (mTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - - for (size_t i = 0; i < mEventListeners.size(); i++) { - if (mEventListeners[i].mCallback == callback) { - return BAD_VALUE; - } - } - - EventListener listener; - listener.mName = name; - listener.mPhase = phase; - listener.mCallback = callback; - - // We want to allow the firstmost future event to fire without - // allowing any past events to fire. To do this extrapolate from - // mReferenceTime the most recent hardware vsync, and pin the - // last event time there. - const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (mPeriod != 0) { - const nsecs_t baseTime = now - mReferenceTime; - const nsecs_t numPeriodsSinceReference = baseTime / mPeriod; - const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod; - const nsecs_t phaseCorrection = mPhase + listener.mPhase; - const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection; - if (predictedLastEventTime >= now) { - // Make sure that the last event time does not exceed the current time. - // If it would, then back the last event time by a period. - listener.mLastEventTime = predictedLastEventTime - mPeriod; - } else { - listener.mLastEventTime = predictedLastEventTime; - } - } else { - listener.mLastEventTime = now + mPhase - mWakeupLatency; - } - - if (lastCallbackTime <= 0) { - // If there is no prior callback time, try to infer one based on the - // logical last event time. - listener.mLastCallbackTime = listener.mLastEventTime + mWakeupLatency; - } else { - listener.mLastCallbackTime = lastCallbackTime; - } - - mEventListeners.push_back(listener); - - mCond.signal(); - - return NO_ERROR; - } - - status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) { - if (mTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - - for (std::vector<EventListener>::iterator it = mEventListeners.begin(); - it != mEventListeners.end(); ++it) { - if (it->mCallback == callback) { - *outLastCallback = it->mLastCallbackTime; - mEventListeners.erase(it); - mCond.signal(); - return NO_ERROR; - } - } - - return BAD_VALUE; - } - - status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) { - if (mTraceDetailedInfo) ATRACE_CALL(); - Mutex::Autolock lock(mMutex); - - for (auto& eventListener : mEventListeners) { - if (eventListener.mCallback == callback) { - const nsecs_t oldPhase = eventListener.mPhase; - eventListener.mPhase = phase; - - // Pretend that the last time this event was handled at the same frame but with the - // new offset to allow for a seamless offset change without double-firing or - // skipping. - nsecs_t diff = oldPhase - phase; - eventListener.mLastEventTime -= diff; - eventListener.mLastCallbackTime -= diff; - mCond.signal(); - return NO_ERROR; - } - } - return BAD_VALUE; - } - - nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const { - Mutex::Autolock lock(mMutex); - return computeNextRefreshLocked(periodOffset, now); - } - -private: - struct EventListener { - const char* mName; - nsecs_t mPhase; - nsecs_t mLastEventTime; - nsecs_t mLastCallbackTime; - DispSync::Callback* mCallback; - }; - - struct CallbackInvocation { - DispSync::Callback* mCallback; - nsecs_t mEventTime; - nsecs_t mExpectedVSyncTime; - }; - - nsecs_t computeNextEventTimeLocked(nsecs_t now) { - if (mTraceDetailedInfo) ATRACE_CALL(); - ALOGV("[%s] computeNextEventTimeLocked", mName); - nsecs_t nextEventTime = INT64_MAX; - for (size_t i = 0; i < mEventListeners.size(); i++) { - nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], now); - - if (t < nextEventTime) { - nextEventTime = t; - } - } - - ALOGV("[%s] nextEventTime = %" PRId64, mName, ns2us(nextEventTime)); - return nextEventTime; - } - - // Check that the duration is close enough in length to a period without - // falling into double-rate vsyncs. - bool isCloseToPeriod(nsecs_t duration) { - // Ratio of 3/5 is arbitrary, but it must be greater than 1/2. - return duration < (3 * mPeriod) / 5; - } - - std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now, - nsecs_t expectedVSyncTime) { - if (mTraceDetailedInfo) ATRACE_CALL(); - ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now)); - - std::vector<CallbackInvocation> callbackInvocations; - nsecs_t onePeriodAgo = now - mPeriod; - - for (auto& eventListener : mEventListeners) { - nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo); - - if (t < now) { - if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) { - eventListener.mLastEventTime = t; - ALOGV("[%s] [%s] Skipping event due to model error", mName, - eventListener.mName); - continue; - } - - CallbackInvocation ci; - ci.mCallback = eventListener.mCallback; - ci.mEventTime = t; - ci.mExpectedVSyncTime = expectedVSyncTime; - if (eventListener.mPhase < 0) { - ci.mExpectedVSyncTime += mPeriod; - } - ALOGV("[%s] [%s] Preparing to fire, latency: %" PRId64, mName, eventListener.mName, - t - eventListener.mLastEventTime); - callbackInvocations.push_back(ci); - eventListener.mLastEventTime = t; - eventListener.mLastCallbackTime = now; - } - } - - return callbackInvocations; - } - - nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) { - if (mTraceDetailedInfo) ATRACE_CALL(); - ALOGV("[%s] [%s] computeListenerNextEventTimeLocked(%" PRId64 ")", mName, listener.mName, - ns2us(baseTime)); - - nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency; - ALOGV("[%s] lastEventTime: %" PRId64, mName, ns2us(lastEventTime)); - if (baseTime < lastEventTime) { - baseTime = lastEventTime; - ALOGV("[%s] Clamping baseTime to lastEventTime -> %" PRId64, mName, ns2us(baseTime)); - } - - baseTime -= mReferenceTime; - ALOGV("[%s] Relative baseTime = %" PRId64, mName, ns2us(baseTime)); - nsecs_t phase = mPhase + listener.mPhase; - ALOGV("[%s] Phase = %" PRId64, mName, ns2us(phase)); - baseTime -= phase; - ALOGV("[%s] baseTime - phase = %" PRId64, mName, ns2us(baseTime)); - - // If our previous time is before the reference (because the reference - // has since been updated), the division by mPeriod will truncate - // towards zero instead of computing the floor. Since in all cases - // before the reference we want the next time to be effectively now, we - // set baseTime to -mPeriod so that numPeriods will be -1. - // When we add 1 and the phase, we will be at the correct event time for - // this period. - if (baseTime < 0) { - ALOGV("[%s] Correcting negative baseTime", mName); - baseTime = -mPeriod; - } - - nsecs_t numPeriods = baseTime / mPeriod; - ALOGV("[%s] numPeriods = %" PRId64, mName, numPeriods); - nsecs_t t = (numPeriods + 1) * mPeriod + phase; - ALOGV("[%s] t = %" PRId64, mName, ns2us(t)); - t += mReferenceTime; - ALOGV("[%s] Absolute t = %" PRId64, mName, ns2us(t)); - - // Check that it's been slightly more than half a period since the last - // event so that we don't accidentally fall into double-rate vsyncs - if (isCloseToPeriod(t - listener.mLastEventTime)) { - t += mPeriod; - ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t)); - } - - t -= mWakeupLatency; - ALOGV("[%s] Corrected for wakeup latency -> %" PRId64, mName, ns2us(t)); - - return t; - } - - void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) { - if (mTraceDetailedInfo) ATRACE_CALL(); - for (size_t i = 0; i < callbacks.size(); i++) { - callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime, - callbacks[i].mExpectedVSyncTime); - } - } - - nsecs_t computeNextRefreshLocked(int periodOffset, nsecs_t now) const { - nsecs_t phase = mReferenceTime + mPhase; - if (mPeriod == 0) { - return 0; - } - return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase; - } - - const char* const mName; - - bool mStop; - TracedOrdinal<bool> mModelLocked; - - nsecs_t mPeriod; - nsecs_t mPhase; - nsecs_t mReferenceTime; - nsecs_t mWakeupLatency; - - int64_t mFrameNumber; - - std::vector<EventListener> mEventListeners; - - mutable Mutex mMutex; - Condition mCond; - - // Flag to turn on logging in systrace. - const bool mTraceDetailedInfo; -}; - -#undef LOG_TAG -#define LOG_TAG "DispSync" - -class ZeroPhaseTracer : public DispSync::Callback { -public: - ZeroPhaseTracer() : mParity("ZERO_PHASE_VSYNC", false) {} - - virtual void onDispSyncEvent(nsecs_t /*when*/, nsecs_t /*expectedVSyncTimestamp*/) { - mParity = !mParity; - } - -private: - TracedOrdinal<bool> mParity; -}; - -DispSync::DispSync(const char* name, bool hasSyncFramework) - : mName(name), mIgnorePresentFences(!hasSyncFramework) { - // This flag offers the ability to turn on systrace logging from the shell. - char value[PROPERTY_VALUE_MAX]; - property_get("debug.sf.dispsync_trace_detailed_info", value, "0"); - mTraceDetailedInfo = atoi(value); - - mThread = new DispSyncThread(name, mTraceDetailedInfo); - mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); - - // set DispSync to SCHED_FIFO to minimize jitter - struct sched_param param = {0}; - param.sched_priority = 2; - if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, ¶m) != 0) { - ALOGE("Couldn't set SCHED_FIFO for DispSyncThread"); - } - - beginResync(); - - if (mTraceDetailedInfo && kEnableZeroPhaseTracer) { - mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>(); - addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0); - } -} - -DispSync::~DispSync() { - mThread->stop(); - mThread->requestExitAndWait(); -} - -void DispSync::reset() { - Mutex::Autolock lock(mMutex); - resetLocked(); -} - -void DispSync::resetLocked() { - mPhase = 0; - const size_t lastSampleIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES; - // Keep the most recent sample, when we resync to hardware we'll overwrite this - // with a more accurate signal - if (mResyncSamples[lastSampleIdx] != 0) { - mReferenceTime = mResyncSamples[lastSampleIdx]; - } - mModelUpdated = false; - for (size_t i = 0; i < MAX_RESYNC_SAMPLES; i++) { - mResyncSamples[i] = 0; - } - mNumResyncSamples = 0; - mFirstResyncSample = 0; - mNumResyncSamplesSincePresent = 0; - mThread->unlockModel(); - resetErrorLocked(); -} - -bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { - Mutex::Autolock lock(mMutex); - - if (mIgnorePresentFences) { - return true; - } - - mPresentFences[mPresentSampleOffset] = fenceTime; - mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES; - mNumResyncSamplesSincePresent = 0; - - updateErrorLocked(); - - return !mModelUpdated || mError > kErrorThreshold; -} - -void DispSync::beginResync() { - Mutex::Autolock lock(mMutex); - ALOGV("[%s] beginResync", mName); - resetLocked(); -} - -bool DispSync::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> /*hwcVsyncPeriod*/, - bool* periodFlushed) { - Mutex::Autolock lock(mMutex); - - ALOGV("[%s] addResyncSample(%" PRId64 ")", mName, ns2us(timestamp)); - - *periodFlushed = false; - const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES; - mResyncSamples[idx] = timestamp; - if (mNumResyncSamples == 0) { - mPhase = 0; - ALOGV("[%s] First resync sample: mPeriod = %" PRId64 ", mPhase = 0, " - "mReferenceTime = %" PRId64, - mName, ns2us(mPeriod), ns2us(timestamp)); - } else if (mPendingPeriod > 0) { - // mNumResyncSamples > 0, so priorIdx won't overflow - const size_t priorIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES; - const nsecs_t lastTimestamp = mResyncSamples[priorIdx]; - - const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp); - if (std::abs(observedVsync - mPendingPeriod) <= std::abs(observedVsync - mIntendedPeriod)) { - // Either the observed vsync is closer to the pending period, (and - // thus we detected a period change), or the period change will - // no-op. In either case, reset the model and flush the pending - // period. - resetLocked(); - mIntendedPeriod = mPendingPeriod; - mPeriod = mPendingPeriod; - mPendingPeriod = 0; - if (mTraceDetailedInfo) { - ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod); - ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod); - } - *periodFlushed = true; - } - } - // Always update the reference time with the most recent timestamp. - mReferenceTime = timestamp; - mThread->updateModel(mPeriod, mPhase, mReferenceTime); - - if (mNumResyncSamples < MAX_RESYNC_SAMPLES) { - mNumResyncSamples++; - } else { - mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES; - } - - updateModelLocked(); - - if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) { - resetErrorLocked(); - } - - if (mIgnorePresentFences) { - // If we're ignoring the present fences we have no way to know whether - // or not we're synchronized with the HW vsyncs, so we just request - // that the HW vsync events be turned on. - return true; - } - - // Check against kErrorThreshold / 2 to add some hysteresis before having to - // resync again - bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0; - ALOGV("[%s] addResyncSample returning %s", mName, modelLocked ? "locked" : "unlocked"); - if (modelLocked) { - *periodFlushed = true; - mThread->lockModel(); - } - return !modelLocked; -} - -void DispSync::endResync() { - mThread->lockModel(); -} - -status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback, - nsecs_t lastCallbackTime) { - Mutex::Autolock lock(mMutex); - return mThread->addEventListener(name, phase, callback, lastCallbackTime); -} - -status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) { - Mutex::Autolock lock(mMutex); - return mThread->removeEventListener(callback, outLastCallbackTime); -} - -status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { - Mutex::Autolock lock(mMutex); - return mThread->changePhaseOffset(callback, phase); -} - -void DispSync::setPeriod(nsecs_t period) { - Mutex::Autolock lock(mMutex); - - const bool pendingPeriodShouldChange = - period != mIntendedPeriod || (period == mIntendedPeriod && mPendingPeriod != 0); - - if (pendingPeriodShouldChange) { - mPendingPeriod = period; - } - if (mTraceDetailedInfo) { - ATRACE_INT("DispSync:IntendedPeriod", mIntendedPeriod); - ATRACE_INT("DispSync:PendingPeriod", mPendingPeriod); - } -} - -nsecs_t DispSync::getPeriod() { - // lock mutex as mPeriod changes multiple times in updateModelLocked - Mutex::Autolock lock(mMutex); - return mPeriod; -} - -void DispSync::updateModelLocked() { - ALOGV("[%s] updateModelLocked %zu", mName, mNumResyncSamples); - if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) { - ALOGV("[%s] Computing...", mName); - nsecs_t durationSum = 0; - nsecs_t minDuration = INT64_MAX; - nsecs_t maxDuration = 0; - // We skip the first 2 samples because the first vsync duration on some - // devices may be much more inaccurate than on other devices, e.g. due - // to delays in ramping up from a power collapse. By doing so this - // actually increases the accuracy of the DispSync model even though - // we're effectively relying on fewer sample points. - static constexpr size_t numSamplesSkipped = 2; - for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) { - size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; - size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES; - nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev]; - durationSum += duration; - minDuration = min(minDuration, duration); - maxDuration = max(maxDuration, duration); - } - - // Exclude the min and max from the average - durationSum -= minDuration + maxDuration; - mPeriod = durationSum / (mNumResyncSamples - numSamplesSkipped - 2); - - ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod)); - - double sampleAvgX = 0; - double sampleAvgY = 0; - double scale = 2.0 * M_PI / double(mPeriod); - for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) { - size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; - nsecs_t sample = mResyncSamples[idx] - mReferenceTime; - double samplePhase = double(sample % mPeriod) * scale; - sampleAvgX += cos(samplePhase); - sampleAvgY += sin(samplePhase); - } - - sampleAvgX /= double(mNumResyncSamples - numSamplesSkipped); - sampleAvgY /= double(mNumResyncSamples - numSamplesSkipped); - - mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale); - - ALOGV("[%s] mPhase = %" PRId64, mName, ns2us(mPhase)); - - if (mPhase < -(mPeriod / 2)) { - mPhase += mPeriod; - ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase)); - } - - mThread->updateModel(mPeriod, mPhase, mReferenceTime); - mModelUpdated = true; - } -} - -void DispSync::updateErrorLocked() { - if (!mModelUpdated) { - return; - } - - int numErrSamples = 0; - nsecs_t sqErrSum = 0; - - for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - // Only check for the cached value of signal time to avoid unecessary - // syscalls. It is the responsibility of the DispSync owner to - // call getSignalTime() periodically so the cache is updated when the - // fence signals. - nsecs_t time = mPresentFences[i]->getCachedSignalTime(); - if (time == Fence::SIGNAL_TIME_PENDING || time == Fence::SIGNAL_TIME_INVALID) { - continue; - } - - nsecs_t sample = time - mReferenceTime; - if (sample <= mPhase) { - continue; - } - - nsecs_t sampleErr = (sample - mPhase) % mPeriod; - if (sampleErr > mPeriod / 2) { - sampleErr -= mPeriod; - } - sqErrSum += sampleErr * sampleErr; - numErrSamples++; - } - - if (numErrSamples > 0) { - mError = sqErrSum / numErrSamples; - mZeroErrSamplesCount = 0; - } else { - mError = 0; - // Use mod ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT to avoid log spam. - mZeroErrSamplesCount++; - ALOGE_IF((mZeroErrSamplesCount % ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT) == 0, - "No present times for model error."); - } - - if (mTraceDetailedInfo) { - ATRACE_INT64("DispSync:Error", mError); - } -} - -void DispSync::resetErrorLocked() { - mPresentSampleOffset = 0; - mError = 0; - mZeroErrSamplesCount = 0; - if (mTraceDetailedInfo) { - ATRACE_INT64("DispSync:Error", mError); - } - for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - mPresentFences[i] = FenceTime::NO_FENCE; - } -} - -nsecs_t DispSync::computeNextRefresh(int periodOffset, nsecs_t now) const { - Mutex::Autolock lock(mMutex); - nsecs_t phase = mReferenceTime + mPhase; - if (mPeriod == 0) { - return 0; - } - return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase; -} - -void DispSync::setIgnorePresentFences(bool ignore) { - Mutex::Autolock lock(mMutex); - if (mIgnorePresentFences != ignore) { - mIgnorePresentFences = ignore; - resetLocked(); - } -} - -void DispSync::dump(std::string& result) const { - Mutex::Autolock lock(mMutex); - StringAppendF(&result, "present fences are %s\n", mIgnorePresentFences ? "ignored" : "used"); - StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps)\n", mPeriod, 1000000000.0 / mPeriod); - StringAppendF(&result, "mPhase: %" PRId64 " ns\n", mPhase); - StringAppendF(&result, "mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError)); - StringAppendF(&result, "mNumResyncSamplesSincePresent: %d (limit %d)\n", - mNumResyncSamplesSincePresent, MAX_RESYNC_SAMPLES_WITHOUT_PRESENT); - StringAppendF(&result, "mNumResyncSamples: %zd (max %d)\n", mNumResyncSamples, - MAX_RESYNC_SAMPLES); - - result.append("mResyncSamples:\n"); - nsecs_t previous = -1; - for (size_t i = 0; i < mNumResyncSamples; i++) { - size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; - nsecs_t sampleTime = mResyncSamples[idx]; - if (i == 0) { - StringAppendF(&result, " %" PRId64 "\n", sampleTime); - } else { - StringAppendF(&result, " %" PRId64 " (+%" PRId64 ")\n", sampleTime, - sampleTime - previous); - } - previous = sampleTime; - } - - StringAppendF(&result, "mPresentFences [%d]:\n", NUM_PRESENT_SAMPLES); - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - previous = Fence::SIGNAL_TIME_INVALID; - for (size_t i = 0; i < NUM_PRESENT_SAMPLES; i++) { - size_t idx = (i + mPresentSampleOffset) % NUM_PRESENT_SAMPLES; - nsecs_t presentTime = mPresentFences[idx]->getSignalTime(); - if (presentTime == Fence::SIGNAL_TIME_PENDING) { - StringAppendF(&result, " [unsignaled fence]\n"); - } else if (presentTime == Fence::SIGNAL_TIME_INVALID) { - StringAppendF(&result, " [invalid fence]\n"); - } else if (previous == Fence::SIGNAL_TIME_PENDING || - previous == Fence::SIGNAL_TIME_INVALID) { - StringAppendF(&result, " %" PRId64 " (%.3f ms ago)\n", presentTime, - (now - presentTime) / 1000000.0); - } else { - StringAppendF(&result, " %" PRId64 " (+%" PRId64 " / %.3f) (%.3f ms ago)\n", - presentTime, presentTime - previous, - (presentTime - previous) / (double)mPeriod, - (now - presentTime) / 1000000.0); - } - previous = presentTime; - } - - StringAppendF(&result, "current monotonic time: %" PRId64 "\n", now); -} - -nsecs_t DispSync::expectedPresentTime(nsecs_t now) { - // The HWC doesn't currently have a way to report additional latency. - // Assume that whatever we submit now will appear right after the flip. - // For a smart panel this might be 1. This is expressed in frames, - // rather than time, because we expect to have a constant frame delay - // regardless of the refresh rate. - const uint32_t hwcLatency = 0; - - // Ask DispSync when the next refresh will be (CLOCK_MONOTONIC). - return mThread->computeNextRefresh(hwcLatency, now); -} - -} // namespace impl - -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h deleted file mode 100644 index 6fb5654b8a..0000000000 --- a/services/surfaceflinger/Scheduler/DispSync.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 <stddef.h> - -#include <utils/Mutex.h> -#include <utils/RefBase.h> -#include <utils/Timers.h> - -#include <ui/FenceTime.h> - -#include <memory> - -namespace android { - -class FenceTime; - -class DispSync { -public: - class Callback { - public: - Callback() = default; - virtual ~Callback(); - virtual void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0; - - protected: - Callback(Callback const&) = delete; - Callback& operator=(Callback const&) = delete; - }; - - DispSync() = default; - virtual ~DispSync(); - - virtual void reset() = 0; - virtual bool addPresentFence(const std::shared_ptr<FenceTime>&) = 0; - virtual void beginResync() = 0; - virtual bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, - bool* periodFlushed) = 0; - virtual void endResync() = 0; - virtual void setPeriod(nsecs_t period) = 0; - virtual nsecs_t getPeriod() = 0; - virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, - nsecs_t lastCallbackTime) = 0; - virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0; - virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0; - virtual nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const = 0; - virtual void setIgnorePresentFences(bool ignore) = 0; - virtual nsecs_t expectedPresentTime(nsecs_t now) = 0; - - virtual void dump(std::string& result) const = 0; - -protected: - DispSync(DispSync const&) = delete; - DispSync& operator=(DispSync const&) = delete; -}; - -namespace impl { - -class DispSyncThread; - -// DispSync maintains a model of the periodic hardware-based vsync events of a -// display and uses that model to execute period callbacks at specific phase -// offsets from the hardware vsync events. The model is constructed by -// feeding consecutive hardware event timestamps to the DispSync object via -// the addResyncSample method. -// -// The model is validated using timestamps from Fence objects that are passed -// to the DispSync object via the addPresentFence method. These fence -// timestamps should correspond to a hardware vsync event, but they need not -// be consecutive hardware vsync times. If this method determines that the -// current model accurately represents the hardware event times it will return -// false to indicate that a resynchronization (via addResyncSample) is not -// needed. -class DispSync : public android::DispSync { -public: - // hasSyncFramework specifies whether the platform supports present fences. - DispSync(const char* name, bool hasSyncFramework); - ~DispSync() override; - - // reset clears the resync samples and error value. - void reset() override; - - // addPresentFence adds a fence for use in validating the current vsync - // event model. The fence need not be signaled at the time - // addPresentFence is called. When the fence does signal, its timestamp - // should correspond to a hardware vsync event. Unlike the - // addResyncSample method, the timestamps of consecutive fences need not - // correspond to consecutive hardware vsync events. - // - // This method should be called with the retire fence from each HWComposer - // set call that affects the display. - bool addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) override; - - // The beginResync, addResyncSample, and endResync methods are used to re- - // synchronize the DispSync's model to the hardware vsync events. The re- - // synchronization process involves first calling beginResync, then - // calling addResyncSample with a sequence of consecutive hardware vsync - // event timestamps, and finally calling endResync when addResyncSample - // indicates that no more samples are needed by returning false. - // - // This resynchronization process should be performed whenever the display - // is turned on (i.e. once immediately after it's turned on) and whenever - // addPresentFence returns true indicating that the model has drifted away - // from the hardware vsync events. - void beginResync() override; - // Adds a vsync sample to the dispsync model. The timestamp is the time - // of the vsync event that fired. periodFlushed will return true if the - // vsync period was detected to have changed to mPendingPeriod. - // - // This method will return true if more vsync samples are needed to lock - // down the DispSync model, and false otherwise. - // periodFlushed will be set to true if mPendingPeriod is flushed to - // mIntendedPeriod, and false otherwise. - bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, - bool* periodFlushed) override; - void endResync() override; - - // The setPeriod method sets the vsync event model's period to a specific - // value. This should be used to prime the model when a display is first - // turned on, or when a refresh rate change is requested. - void setPeriod(nsecs_t period) override; - - // The getPeriod method returns the current vsync period. - nsecs_t getPeriod() override; - - // addEventListener registers a callback to be called repeatedly at the - // given phase offset from the hardware vsync events. The callback is - // called from a separate thread and it should return reasonably quickly - // (i.e. within a few hundred microseconds). - // If the callback was previously registered, and the last clock time the - // callback was invoked was known to the caller (e.g. via removeEventListener), - // then the caller may pass that through to lastCallbackTime, so that - // callbacks do not accidentally double-fire if they are unregistered and - // reregistered in rapid succession. - status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, - nsecs_t lastCallbackTime) override; - - // removeEventListener removes an already-registered event callback. Once - // this method returns that callback will no longer be called by the - // DispSync object. - // outLastCallbackTime will contain the last time that the callback was invoked. - // If the caller wishes to reregister the same callback, they should pass the - // callback time back into lastCallbackTime (see addEventListener). - status_t removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) override; - - // changePhaseOffset changes the phase offset of an already-registered event callback. The - // method will make sure that there is no skipping or double-firing on the listener per frame, - // even when changing the offsets multiple times. - status_t changePhaseOffset(Callback* callback, nsecs_t phase) override; - - // computeNextRefresh computes when the next refresh is expected to begin. - // The periodOffset value can be used to move forward or backward; an - // offset of zero is the next refresh, -1 is the previous refresh, 1 is - // the refresh after next. etc. - nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const override; - - // In certain situations the present fences aren't a good indicator of vsync - // time, e.g. when vr flinger is active, or simply aren't available, - // e.g. when the sync framework isn't present. Use this method to toggle - // whether or not DispSync ignores present fences. If present fences are - // ignored, DispSync will always ask for hardware vsync events by returning - // true from addPresentFence() and addResyncSample(). - void setIgnorePresentFences(bool ignore) override; - - // Determine the expected present time when a buffer acquired now will be displayed. - nsecs_t expectedPresentTime(nsecs_t now); - - // dump appends human-readable debug info to the result string. - void dump(std::string& result) const override; - -private: - void updateModelLocked(); - void updateErrorLocked(); - void resetLocked(); - void resetErrorLocked(); - - enum { MAX_RESYNC_SAMPLES = 32 }; - enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 }; - enum { NUM_PRESENT_SAMPLES = 8 }; - enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 }; - enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 }; - - const char* const mName; - - // mPeriod is the computed period of the modeled vsync events in - // nanoseconds. - nsecs_t mPeriod; - - // mIntendedPeriod is the intended period of the modeled vsync events in - // nanoseconds. Under ideal conditions this should be similar if not the - // same as mPeriod, plus or minus an observed error. - nsecs_t mIntendedPeriod = 0; - - // mPendingPeriod is the proposed period change in nanoseconds. - // If mPendingPeriod differs from mPeriod and is nonzero, it will - // be flushed to mPeriod when we detect that the hardware switched - // vsync frequency. - nsecs_t mPendingPeriod = 0; - - // mPhase is the phase offset of the modeled vsync events. It is the - // number of nanoseconds from time 0 to the first vsync event. - nsecs_t mPhase; - - // mReferenceTime is the reference time of the modeled vsync events. - // It is the nanosecond timestamp of the first vsync event after a resync. - nsecs_t mReferenceTime; - - // mError is the computed model error. It is based on the difference - // between the estimated vsync event times and those observed in the - // mPresentFences array. - nsecs_t mError; - - // mZeroErrSamplesCount keeps track of how many times in a row there were - // zero timestamps available in the mPresentFences array. - // Used to check that we are able to calculate the model error. - size_t mZeroErrSamplesCount; - - // Whether we have updated the vsync event model since the last resync. - bool mModelUpdated; - - // These member variables are the state used during the resynchronization - // process to store information about the hardware vsync event times used - // to compute the model. - nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0}; - size_t mFirstResyncSample = 0; - size_t mNumResyncSamples = 0; - int mNumResyncSamplesSincePresent; - - // These member variables store information about the present fences used - // to validate the currently computed model. - std::shared_ptr<FenceTime> mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE}; - size_t mPresentSampleOffset; - - // mThread is the thread from which all the callbacks are called. - sp<DispSyncThread> mThread; - - // mMutex is used to protect access to all member variables. - mutable Mutex mMutex; - - // Ignore present (retire) fences if the device doesn't have support for the - // sync framework - bool mIgnorePresentFences; - - std::unique_ptr<Callback> mZeroPhaseTracer; - - // Flag to turn on logging in systrace. - bool mTraceDetailedInfo = false; -}; - -} // namespace impl - -} // namespace android diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp index 8752b6600e..ce5c31ac19 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "DispSyncSource.h" @@ -26,37 +22,129 @@ #include <utils/Trace.h> #include <mutex> -#include "DispSync.h" #include "EventThread.h" +#include "VsyncController.h" -namespace android { +namespace android::scheduler { using base::StringAppendF; +using namespace std::chrono_literals; + +class CallbackRepeater { +public: + CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name, + std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, + std::chrono::nanoseconds notBefore) + : mName(name), + mCallback(cb), + mRegistration(dispatch, + std::bind(&CallbackRepeater::callback, this, std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3), + mName), + mStarted(false), + mWorkDuration(workDuration), + mReadyDuration(readyDuration), + mLastCallTime(notBefore) {} + + ~CallbackRepeater() { + std::lock_guard lock(mMutex); + mRegistration.cancel(); + } + + void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { + std::lock_guard lock(mMutex); + mStarted = true; + mWorkDuration = workDuration; + mReadyDuration = readyDuration; + + auto const scheduleResult = + mRegistration.schedule({.workDuration = mWorkDuration.count(), + .readyDuration = mReadyDuration.count(), + .earliestVsync = mLastCallTime.count()}); + LOG_ALWAYS_FATAL_IF((scheduleResult != scheduler::ScheduleResult::Scheduled), + "Error scheduling callback: rc %X", scheduleResult); + } + + void stop() { + std::lock_guard lock(mMutex); + LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped"); + mStarted = false; + mRegistration.cancel(); + } + + void dump(std::string& result) const { + std::lock_guard lock(mMutex); + const auto relativeLastCallTime = + mLastCallTime - std::chrono::steady_clock::now().time_since_epoch(); + StringAppendF(&result, "\t%s: ", mName.c_str()); + StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ", + mWorkDuration.count() / 1e6f, mReadyDuration.count() / 1e6f); + StringAppendF(&result, "%.2fms relative to now (%s)\n", relativeLastCallTime.count() / 1e6f, + mStarted ? "running" : "stopped"); + } + +private: + void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { + { + std::lock_guard lock(mMutex); + mLastCallTime = std::chrono::nanoseconds(vsyncTime); + } + + mCallback(vsyncTime, wakeupTime, readyTime); + + { + std::lock_guard lock(mMutex); + if (!mStarted) { + return; + } + auto const scheduleResult = + mRegistration.schedule({.workDuration = mWorkDuration.count(), + .readyDuration = mReadyDuration.count(), + .earliestVsync = vsyncTime}); + LOG_ALWAYS_FATAL_IF((scheduleResult != ScheduleResult::Scheduled), + "Error rescheduling callback: rc %X", scheduleResult); + } + } -DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, + const std::string mName; + scheduler::VSyncDispatch::Callback mCallback; + + mutable std::mutex mMutex; + VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex); + bool mStarted GUARDED_BY(mMutex) = false; + std::chrono::nanoseconds mWorkDuration GUARDED_BY(mMutex) = 0ns; + std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex) = 0ns; + std::chrono::nanoseconds mLastCallTime GUARDED_BY(mMutex) = 0ns; +}; + +DispSyncSource::DispSyncSource(scheduler::VSyncDispatch& vSyncDispatch, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration, bool traceVsync, const char* name) : mName(name), mValue(base::StringPrintf("VSYNC-%s", name), 0), mTraceVsync(traceVsync), mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)), - mDispSync(dispSync), - mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {} + mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration), + mReadyDuration(readyDuration) { + mCallbackRepeater = + std::make_unique<CallbackRepeater>(vSyncDispatch, + std::bind(&DispSyncSource::onVsyncCallback, this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3), + name, workDuration, readyDuration, + std::chrono::steady_clock::now().time_since_epoch()); +} + +DispSyncSource::~DispSyncSource() = default; void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); if (enable) { - status_t err = mDispSync->addEventListener(mName, mPhaseOffset, - static_cast<DispSync::Callback*>(this), - mLastCallbackTime); - if (err != NO_ERROR) { - ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err); - } + mCallbackRepeater->start(mWorkDuration, mReadyDuration); // ATRACE_INT(mVsyncOnLabel.c_str(), 1); } else { - status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this), - &mLastCallbackTime); - if (err != NO_ERROR) { - ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err); - } + mCallbackRepeater->stop(); // ATRACE_INT(mVsyncOnLabel.c_str(), 0); } mEnabled = enable; @@ -67,32 +155,22 @@ void DispSyncSource::setCallback(VSyncSource::Callback* callback) { mCallback = callback; } -void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) { +void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) { std::lock_guard lock(mVsyncMutex); - const nsecs_t period = mDispSync->getPeriod(); - - // Normalize phaseOffset to [-period, period) - const int numPeriods = phaseOffset / period; - phaseOffset -= numPeriods * period; - if (mPhaseOffset == phaseOffset) { - return; - } - - mPhaseOffset = phaseOffset; + mWorkDuration = workDuration; + mReadyDuration = readyDuration; // If we're not enabled, we don't need to mess with the listeners if (!mEnabled) { return; } - status_t err = - mDispSync->changePhaseOffset(static_cast<DispSync::Callback*>(this), mPhaseOffset); - if (err != NO_ERROR) { - ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err); - } + mCallbackRepeater->start(mWorkDuration, mReadyDuration); } -void DispSyncSource::onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) { +void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, + nsecs_t readyTime) { VSyncSource::Callback* callback; { std::lock_guard lock(mCallbackMutex); @@ -104,17 +182,13 @@ void DispSyncSource::onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestam } if (callback != nullptr) { - callback->onVSyncEvent(when, expectedVSyncTimestamp); + callback->onVSyncEvent(targetWakeupTime, vsyncTime, readyTime); } } void DispSyncSource::dump(std::string& result) const { std::lock_guard lock(mVsyncMutex); StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled"); - mDispSync->dump(result); } -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h index 2aee3f6321..2fce235546 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.h +++ b/services/surfaceflinger/Scheduler/DispSyncSource.h @@ -18,45 +18,47 @@ #include <mutex> #include <string> -#include "DispSync.h" #include "EventThread.h" #include "TracedOrdinal.h" +#include "VSyncDispatch.h" -namespace android { +namespace android::scheduler { +class CallbackRepeater; -class DispSyncSource final : public VSyncSource, private DispSync::Callback { +class DispSyncSource final : public VSyncSource { public: - DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name); + DispSyncSource(VSyncDispatch& vSyncDispatch, std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration, bool traceVsync, const char* name); - ~DispSyncSource() override = default; + ~DispSyncSource() override; // The following methods are implementation of VSyncSource. const char* getName() const override { return mName; } void setVSyncEnabled(bool enable) override; void setCallback(VSyncSource::Callback* callback) override; - void setPhaseOffset(nsecs_t phaseOffset) override; + void setDuration(std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) override; void dump(std::string&) const override; private: - // The following method is the implementation of the DispSync::Callback. - void onDispSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override; + void onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime); const char* const mName; TracedOrdinal<int> mValue; const bool mTraceVsync; const std::string mVsyncOnLabel; - nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0; - DispSync* mDispSync; + std::unique_ptr<CallbackRepeater> mCallbackRepeater; std::mutex mCallbackMutex; VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr; mutable std::mutex mVsyncMutex; - TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex); + TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mVsyncMutex); + std::chrono::nanoseconds mReadyDuration GUARDED_BY(mVsyncMutex); bool mEnabled GUARDED_BY(mVsyncMutex) = false; }; -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp deleted file mode 100644 index 7f9db9ca2d..0000000000 --- a/services/surfaceflinger/Scheduler/EventControlThread.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#include <pthread.h> -#include <sched.h> -#include <sys/resource.h> - -#include <cutils/sched_policy.h> -#include <log/log.h> -#include <system/thread_defs.h> - -#include "EventControlThread.h" - -namespace android { - -EventControlThread::~EventControlThread() = default; - -namespace impl { - -EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function) - : mSetVSyncEnabled(std::move(function)) { - pthread_setname_np(mThread.native_handle(), "EventControlThread"); - - pid_t tid = pthread_gettid_np(mThread.native_handle()); - setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY); - set_sched_policy(tid, SP_FOREGROUND); -} - -EventControlThread::~EventControlThread() { - { - std::lock_guard<std::mutex> lock(mMutex); - mKeepRunning = false; - mCondition.notify_all(); - } - mThread.join(); -} - -void EventControlThread::setVsyncEnabled(bool enabled) { - std::lock_guard<std::mutex> lock(mMutex); - mVsyncEnabled = enabled; - mCondition.notify_all(); -} - -// Unfortunately std::unique_lock gives warnings with -Wthread-safety -void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS { - auto keepRunning = true; - auto currentVsyncEnabled = false; - - while (keepRunning) { - mSetVSyncEnabled(currentVsyncEnabled); - - std::unique_lock<std::mutex> lock(mMutex); - mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS { - return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning; - }); - currentVsyncEnabled = mVsyncEnabled; - keepRunning = mKeepRunning; - } -} - -} // namespace impl -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/Scheduler/EventControlThread.h b/services/surfaceflinger/Scheduler/EventControlThread.h deleted file mode 100644 index cafae53400..0000000000 --- a/services/surfaceflinger/Scheduler/EventControlThread.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <condition_variable> -#include <cstddef> -#include <functional> -#include <mutex> -#include <thread> - -#include <android-base/thread_annotations.h> - -namespace android { - -class EventControlThread { -public: - virtual ~EventControlThread(); - - virtual void setVsyncEnabled(bool enabled) = 0; -}; - -namespace impl { - -class EventControlThread final : public android::EventControlThread { -public: - using SetVSyncEnabledFunction = std::function<void(bool)>; - - explicit EventControlThread(SetVSyncEnabledFunction function); - ~EventControlThread(); - - // EventControlThread implementation - void setVsyncEnabled(bool enabled) override; - -private: - void threadMain(); - - std::mutex mMutex; - std::condition_variable mCondition; - - const SetVSyncEnabledFunction mSetVSyncEnabled; - bool mVsyncEnabled GUARDED_BY(mMutex) = false; - bool mKeepRunning GUARDED_BY(mMutex) = true; - - // Must be last so that everything is initialized before the thread starts. - std::thread mThread{&EventControlThread::threadMain, this}; -}; - -} // namespace impl -} // namespace android diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 845bf50ad3..bf5be4790d 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -31,6 +31,8 @@ #include <android-base/stringprintf.h> +#include <binder/IPCThreadState.h> + #include <cutils/compiler.h> #include <cutils/sched_policy.h> @@ -40,6 +42,7 @@ #include <utils/Trace.h> #include "EventThread.h" +#include "FrameTimeline.h" #include "HwcStrongTypes.h" using namespace std::chrono_literals; @@ -61,6 +64,8 @@ std::string toString(VSyncRequest request) { return "VSyncRequest::None"; case VSyncRequest::Single: return "VSyncRequest::Single"; + case VSyncRequest::SingleSuppressCallback: + return "VSyncRequest::SingleSuppressCallback"; default: return StringPrintf("VSyncRequest::Periodic{period=%d}", vsyncPeriod(request)); } @@ -74,18 +79,16 @@ std::string toString(const EventThreadConnection& connection) { std::string toString(const DisplayEventReceiver::Event& event) { switch (event.header.type) { case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: - return StringPrintf("Hotplug{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", %s}", - event.header.displayId, + return StringPrintf("Hotplug{displayId=%s, %s}", + to_string(event.header.displayId).c_str(), event.hotplug.connected ? "connected" : "disconnected"); case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: - return StringPrintf("VSync{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT - ", count=%u, expectedVSyncTimestamp=%" PRId64 "}", - event.header.displayId, event.vsync.count, + return StringPrintf("VSync{displayId=%s, count=%u, expectedVSyncTimestamp=%" PRId64 "}", + to_string(event.header.displayId).c_str(), event.vsync.count, event.vsync.expectedVSyncTimestamp); case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: - return StringPrintf("ConfigChanged{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT - ", configId=%u}", - event.header.displayId, event.config.configId); + return StringPrintf("ConfigChanged{displayId=%s, configId=%u}", + to_string(event.header.displayId).c_str(), event.config.configId); default: return "Event{}"; } @@ -100,11 +103,14 @@ DisplayEventReceiver::Event makeHotplug(PhysicalDisplayId displayId, nsecs_t tim } DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t timestamp, - uint32_t count, nsecs_t expectedVSyncTimestamp) { + uint32_t count, nsecs_t expectedVSyncTimestamp, + nsecs_t deadlineTimestamp, int64_t vsyncId) { DisplayEventReceiver::Event event; event.header = {DisplayEventReceiver::DISPLAY_EVENT_VSYNC, displayId, timestamp}; event.vsync.count = count; event.vsync.expectedVSyncTimestamp = expectedVSyncTimestamp; + event.vsync.deadlineTimestamp = deadlineTimestamp; + event.vsync.vsyncId = vsyncId; return event; } @@ -119,11 +125,12 @@ DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, } // namespace -EventThreadConnection::EventThreadConnection(EventThread* eventThread, +EventThreadConnection::EventThreadConnection(EventThread* eventThread, uid_t callingUid, ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) : resyncCallback(std::move(resyncCallback)), mConfigChanged(configChanged), + mOwnerUid(callingUid), mEventThread(eventThread), mChannel(gui::BitTube::DefaultSize) {} @@ -165,9 +172,13 @@ EventThread::~EventThread() = default; namespace impl { EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource, - InterceptVSyncsCallback interceptVSyncsCallback) + android::frametimeline::TokenManager* tokenManager, + InterceptVSyncsCallback interceptVSyncsCallback, + ThrottleVsyncCallback throttleVsyncCallback) : mVSyncSource(std::move(vsyncSource)), + mTokenManager(tokenManager), mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)), + mThrottleVsyncCallback(std::move(throttleVsyncCallback)), mThreadName(mVSyncSource->getName()) { mVSyncSource->setCallback(this); @@ -202,15 +213,17 @@ EventThread::~EventThread() { mThread.join(); } -void EventThread::setPhaseOffset(nsecs_t phaseOffset) { +void EventThread::setDuration(std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) { std::lock_guard<std::mutex> lock(mMutex); - mVSyncSource->setPhaseOffset(phaseOffset); + mVSyncSource->setDuration(workDuration, readyDuration); } sp<EventThreadConnection> EventThread::createEventConnection( ResyncCallback resyncCallback, ISurfaceComposer::ConfigChanged configChanged) const { - return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback), - configChanged); + return new EventThreadConnection(const_cast<EventThread*>(this), + IPCThreadState::self()->getCallingUid(), + std::move(resyncCallback), configChanged); } status_t EventThread::registerDisplayEventConnection(const sp<EventThreadConnection>& connection) { @@ -262,6 +275,8 @@ void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) if (connection->vsyncRequest == VSyncRequest::None) { connection->vsyncRequest = VSyncRequest::Single; mCondition.notify_all(); + } else if (connection->vsyncRequest == VSyncRequest::SingleSuppressCallback) { + connection->vsyncRequest = VSyncRequest::Single; } } @@ -285,12 +300,21 @@ void EventThread::onScreenAcquired() { mCondition.notify_all(); } -void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) { +void EventThread::onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp, + nsecs_t deadlineTimestamp) { std::lock_guard<std::mutex> lock(mMutex); LOG_FATAL_IF(!mVSyncState); + const int64_t vsyncId = [&] { + if (mTokenManager != nullptr) { + return mTokenManager->generateTokenForPredictions( + {timestamp, deadlineTimestamp, expectedVSyncTimestamp}); + } + return static_cast<int64_t>(0); + }(); + mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count, - expectedVSyncTimestamp)); + expectedVSyncTimestamp, deadlineTimestamp, vsyncId)); mCondition.notify_all(); } @@ -412,9 +436,12 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { LOG_FATAL_IF(!mVSyncState); const auto now = systemTime(SYSTEM_TIME_MONOTONIC); - const auto expectedVSyncTime = now + timeout.count(); + const auto deadlineTimestamp = now + timeout.count(); + const auto expectedVSyncTime = deadlineTimestamp + timeout.count(); + // TODO(b/162890590): use TokenManager to populate vsyncId mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now, - ++mVSyncState->count, expectedVSyncTime)); + ++mVSyncState->count, expectedVSyncTime, + deadlineTimestamp, /*vsyncId=*/0)); } } } @@ -422,6 +449,11 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, const sp<EventThreadConnection>& connection) const { + const auto throttleVsync = [&] { + return mThrottleVsyncCallback && + mThrottleVsyncCallback(event.vsync.expectedVSyncTimestamp, connection->mOwnerUid); + }; + switch (event.header.type) { case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: return true; @@ -434,12 +466,25 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, switch (connection->vsyncRequest) { case VSyncRequest::None: return false; - case VSyncRequest::Single: + case VSyncRequest::SingleSuppressCallback: connection->vsyncRequest = VSyncRequest::None; + return false; + case VSyncRequest::Single: { + if (throttleVsync()) { + return false; + } + connection->vsyncRequest = VSyncRequest::SingleSuppressCallback; return true; + } case VSyncRequest::Periodic: + if (throttleVsync()) { + return false; + } return true; default: + // We don't throttle vsync if the app set a vsync request rate + // since there is no easy way to do that and this is a very + // rare case return event.vsync.count % vsyncPeriod(connection->vsyncRequest) == 0; } @@ -473,8 +518,8 @@ void EventThread::dump(std::string& result) const { StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState)); if (mVSyncState) { - StringAppendF(&result, "{displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%u%s}\n", - mVSyncState->displayId, mVSyncState->count, + StringAppendF(&result, "{displayId=%s, count=%u%s}\n", + to_string(mVSyncState->displayId).c_str(), mVSyncState->count, mVSyncState->synthetic ? ", synthetic" : ""); } else { StringAppendF(&result, "none\n"); diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 49f624c35e..2e2d98950e 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -41,13 +41,20 @@ class EventThread; class EventThreadTest; class SurfaceFlinger; +namespace frametimeline { +class TokenManager; +} // namespace frametimeline + // --------------------------------------------------------------------------- using ResyncCallback = std::function<void()>; enum class VSyncRequest { - None = -1, - Single = 0, + None = -2, + // Single wakes up for the next two frames to avoid scheduler overhead + Single = -1, + // SingleSuppressCallback only wakes up for the next frame + SingleSuppressCallback = 0, Periodic = 1, // Subsequent values are periods. }; @@ -57,7 +64,8 @@ public: class Callback { public: virtual ~Callback() {} - virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) = 0; + virtual void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp, + nsecs_t deadlineTimestamp) = 0; }; virtual ~VSyncSource() {} @@ -65,14 +73,15 @@ public: virtual const char* getName() const = 0; virtual void setVSyncEnabled(bool enable) = 0; virtual void setCallback(Callback* callback) = 0; - virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; + virtual void setDuration(std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) = 0; virtual void dump(std::string& result) const = 0; }; class EventThreadConnection : public BnDisplayEventConnection { public: - EventThreadConnection(EventThread*, ResyncCallback, + EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback, ISurfaceComposer::ConfigChanged configChanged); virtual ~EventThreadConnection(); @@ -89,6 +98,8 @@ public: const ISurfaceComposer::ConfigChanged mConfigChanged = ISurfaceComposer::ConfigChanged::eConfigChangedSuppress; + const uid_t mOwnerUid; + private: virtual void onFirstRef(); EventThread* const mEventThread; @@ -116,7 +127,8 @@ public: virtual void dump(std::string& result) const = 0; - virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; + virtual void setDuration(std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) = 0; virtual status_t registerDisplayEventConnection( const sp<EventThreadConnection>& connection) = 0; @@ -133,8 +145,10 @@ namespace impl { class EventThread : public android::EventThread, private VSyncSource::Callback { public: using InterceptVSyncsCallback = std::function<void(nsecs_t)>; + using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>; - EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback); + EventThread(std::unique_ptr<VSyncSource>, frametimeline::TokenManager*, InterceptVSyncsCallback, + ThrottleVsyncCallback); ~EventThread(); sp<EventThreadConnection> createEventConnection( @@ -157,7 +171,8 @@ public: void dump(std::string& result) const override; - void setPhaseOffset(nsecs_t phaseOffset) override; + void setDuration(std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) override; size_t getEventThreadConnectionCount() override; @@ -177,11 +192,14 @@ private: REQUIRES(mMutex); // Implements VSyncSource::Callback - void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp) override; + void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp, + nsecs_t deadlineTimestamp) override; const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex); + frametimeline::TokenManager* const mTokenManager; const InterceptVSyncsCallback mInterceptVSyncsCallback; + const ThrottleVsyncCallback mThrottleVsyncCallback; const char* const mThreadName; std::thread mThread; diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h index 975c9db41a..016b076444 100644 --- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h +++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h @@ -35,16 +35,17 @@ public: mCallback = callback; } - void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) { + void onInjectSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp, + nsecs_t deadlineTimestamp) { std::lock_guard<std::mutex> lock(mCallbackMutex); if (mCallback) { - mCallback->onVSyncEvent(when, expectedVSyncTimestamp); + mCallback->onVSyncEvent(when, expectedVSyncTimestamp, deadlineTimestamp); } } const char* getName() const override { return "inject"; } void setVSyncEnabled(bool) override {} - void setPhaseOffset(nsecs_t) override {} + void setDuration(std::chrono::nanoseconds, std::chrono::nanoseconds) override {} void dump(std::string&) const override {} private: diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index ecf2597b8c..36433c20ed 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -20,6 +20,7 @@ #include "LayerHistory.h" +#include <android-base/stringprintf.h> #include <cutils/properties.h> #include <utils/Log.h> #include <utils/Timers.h> @@ -183,4 +184,11 @@ void LayerHistory::clear() { mActiveLayersEnd = 0; } + +std::string LayerHistory::dump() const { + std::lock_guard lock(mLock); + return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(), + mActiveLayersEnd); +} + } // namespace android::scheduler::impl diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index 228b8a06af..128699b5a7 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -22,6 +22,7 @@ #include <memory> #include <mutex> +#include <string> #include <utility> #include <vector> @@ -71,6 +72,7 @@ public: virtual Summary summarize(nsecs_t now) = 0; virtual void clear() = 0; + virtual std::string dump() const = 0; }; namespace impl { @@ -97,6 +99,7 @@ public: android::scheduler::LayerHistory::Summary summarize(nsecs_t now) override; void clear() override; + std::string dump() const override; private: friend class android::scheduler::LayerHistoryTest; @@ -155,6 +158,7 @@ public: android::scheduler::LayerHistory::Summary summarize(nsecs_t /*now*/) override; void clear() override; + std::string dump() const override; private: friend android::scheduler::LayerHistoryTestV2; diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp index aa04bd7a58..37e67e17fe 100644 --- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp @@ -20,6 +20,7 @@ #include "LayerHistory.h" +#include <android-base/stringprintf.h> #include <cutils/properties.h> #include <utils/Log.h> #include <utils/Timers.h> @@ -31,9 +32,8 @@ #include <utility> #include "../Layer.h" -#include "SchedulerUtils.h" - #include "LayerInfoV2.h" +#include "SchedulerUtils.h" namespace android::scheduler::impl { @@ -214,4 +214,10 @@ void LayerHistoryV2::clear() { } } +std::string LayerHistoryV2::dump() const { + std::lock_guard lock(mLock); + return base::StringPrintf("LayerHistoryV2{size=%zu, active=%zu}", mLayerInfos.size(), + mActiveLayersEnd); +} + } // namespace android::scheduler::impl diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index 6067e69ea0..134337588b 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -14,9 +14,7 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <binder/IPCThreadState.h> @@ -28,6 +26,7 @@ #include <gui/IDisplayEventConnection.h> #include "EventThread.h" +#include "FrameTimeline.h" #include "MessageQueue.h" #include "SurfaceFlinger.h" @@ -39,8 +38,9 @@ void MessageQueue::Handler::dispatchRefresh() { } } -void MessageQueue::Handler::dispatchInvalidate(nsecs_t expectedVSyncTimestamp) { +void MessageQueue::Handler::dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp) { if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) { + mVsyncId = vsyncId; mExpectedVSyncTime = expectedVSyncTimestamp; mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE)); } @@ -50,11 +50,11 @@ void MessageQueue::Handler::handleMessage(const Message& message) { switch (message.what) { case INVALIDATE: android_atomic_and(~eventMaskInvalidate, &mEventMask); - mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime); + mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime); break; case REFRESH: android_atomic_and(~eventMaskRefresh, &mEventMask); - mQueue.mFlinger->onMessageReceived(message.what, mExpectedVSyncTime); + mQueue.mFlinger->onMessageReceived(message.what, mVsyncId, mExpectedVSyncTime); break; } } @@ -67,15 +67,53 @@ void MessageQueue::init(const sp<SurfaceFlinger>& flinger) { mHandler = new Handler(*this); } +// TODO(b/169865816): refactor VSyncInjections to use MessageQueue directly +// and remove the EventThread from MessageQueue void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) { if (mEventTube.getFd() >= 0) { mLooper->removeFd(mEventTube.getFd()); } mEvents = connection; - mEvents->stealReceiveChannel(&mEventTube); - mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver, - this); + if (mEvents) { + mEvents->stealReceiveChannel(&mEventTube); + mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver, + this); + } +} + +void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) { + ATRACE_CALL(); + // Trace VSYNC-sf + mVsync.value = (mVsync.value + 1) % 2; + + { + std::lock_guard lock(mVsync.mutex); + mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime); + } + mHandler->dispatchInvalidate(mVsync.tokenManager->generateTokenForPredictions( + {targetWakeupTime, readyTime, vsyncTime}), + vsyncTime); +} + +void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch, + frametimeline::TokenManager& tokenManager, + std::chrono::nanoseconds workDuration) { + setDuration(workDuration); + mVsync.tokenManager = &tokenManager; + mVsync.registration = std::make_unique< + scheduler::VSyncCallbackRegistration>(dispatch, + std::bind(&MessageQueue::vsyncCallback, this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3), + "sf"); +} + +void MessageQueue::setDuration(std::chrono::nanoseconds workDuration) { + ATRACE_CALL(); + std::lock_guard lock(mVsync.mutex); + mVsync.workDuration = workDuration; } void MessageQueue::waitMessage() { @@ -105,7 +143,18 @@ void MessageQueue::postMessage(sp<MessageHandler>&& handler) { } void MessageQueue::invalidate() { - mEvents->requestNextVsync(); + ATRACE_CALL(); + if (mEvents) { + mEvents->requestNextVsync(); + } else { + const auto [workDuration, lastVsyncCallback] = [&] { + std::lock_guard lock(mVsync.mutex); + std::chrono::nanoseconds mWorkDurationNanos = mVsync.workDuration; + return std::make_pair(mWorkDurationNanos.count(), mVsync.lastCallbackTime.count()); + }(); + + mVsync.registration->schedule({workDuration, /*readyDuration=*/0, lastVsyncCallback}); + } } void MessageQueue::refresh() { @@ -123,7 +172,8 @@ int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) { while ((n = DisplayEventReceiver::getEvents(&mEventTube, buffer, 8)) > 0) { for (int i = 0; i < n; i++) { if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { - mHandler->dispatchInvalidate(buffer[i].vsync.expectedVSyncTimestamp); + mHandler->dispatchInvalidate(buffer[i].vsync.vsyncId, + buffer[i].vsync.expectedVSyncTimestamp); break; } } @@ -133,5 +183,3 @@ int MessageQueue::eventReceiver(int /*fd*/, int /*events*/) { } // namespace android::impl -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 132b416c88..139b38e32c 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -29,6 +29,8 @@ #include <private/gui/BitTube.h> #include "EventThread.h" +#include "TracedOrdinal.h" +#include "VSyncDispatch.h" namespace android { @@ -63,6 +65,9 @@ public: virtual ~MessageQueue() = default; virtual void init(const sp<SurfaceFlinger>& flinger) = 0; + virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&, + std::chrono::nanoseconds workDuration) = 0; + virtual void setDuration(std::chrono::nanoseconds workDuration) = 0; virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0; virtual void waitMessage() = 0; virtual void postMessage(sp<MessageHandler>&&) = 0; @@ -74,18 +79,20 @@ public: namespace impl { -class MessageQueue final : public android::MessageQueue { +class MessageQueue : public android::MessageQueue { +protected: class Handler : public MessageHandler { enum { eventMaskInvalidate = 0x1, eventMaskRefresh = 0x2, eventMaskTransaction = 0x4 }; MessageQueue& mQueue; int32_t mEventMask; + std::atomic<int64_t> mVsyncId; std::atomic<nsecs_t> mExpectedVSyncTime; public: explicit Handler(MessageQueue& queue) : mQueue(queue), mEventMask(0) {} - virtual void handleMessage(const Message& message); - void dispatchRefresh(); - void dispatchInvalidate(nsecs_t expectedVSyncTimestamp); + void handleMessage(const Message& message) override; + virtual void dispatchRefresh(); + virtual void dispatchInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTimestamp); }; friend class Handler; @@ -93,15 +100,33 @@ class MessageQueue final : public android::MessageQueue { sp<SurfaceFlinger> mFlinger; sp<Looper> mLooper; sp<EventThreadConnection> mEvents; + + struct Vsync { + frametimeline::TokenManager* tokenManager = nullptr; + std::unique_ptr<scheduler::VSyncCallbackRegistration> registration; + + std::mutex mutex; + TracedOrdinal<std::chrono::nanoseconds> workDuration + GUARDED_BY(mutex) = {"VsyncWorkDuration-sf", std::chrono::nanoseconds(0)}; + std::chrono::nanoseconds lastCallbackTime GUARDED_BY(mutex) = std::chrono::nanoseconds{0}; + TracedOrdinal<int> value = {"VSYNC-sf", 0}; + }; + + Vsync mVsync; + gui::BitTube mEventTube; sp<Handler> mHandler; static int cb_eventReceiver(int fd, int events, void* data); int eventReceiver(int fd, int events); + void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime); public: ~MessageQueue() override = default; void init(const sp<SurfaceFlinger>& flinger) override; + void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&, + std::chrono::nanoseconds workDuration) override; + void setDuration(std::chrono::nanoseconds workDuration) override; void setEventConnection(const sp<EventThreadConnection>& connection) override; void waitMessage() override; diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp index a90d05e17c..27838003c4 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp +++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp @@ -20,6 +20,21 @@ #include <sstream> #include <thread> +namespace { +using namespace std::chrono_literals; + +constexpr int64_t kNsToSeconds = std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); + +// The syscall interface uses a pair of integers for the timestamp. The first +// (tv_sec) is the whole count of seconds. The second (tv_nsec) is the +// nanosecond part of the count. This function takes care of translation. +void calculateTimeoutTime(std::chrono::nanoseconds timestamp, timespec* spec) { + clock_gettime(CLOCK_MONOTONIC, spec); + spec->tv_sec += static_cast<__kernel_time_t>(timestamp.count() / kNsToSeconds); + spec->tv_nsec += timestamp.count() % kNsToSeconds; +} +} // namespace + namespace android { namespace scheduler { @@ -32,81 +47,95 @@ OneShotTimer::~OneShotTimer() { } void OneShotTimer::start() { - { - std::lock_guard<std::mutex> lock(mMutex); - mState = TimerState::RESET; + sem_init(&mSemaphore, 0, 0); + + if (!mThread.joinable()) { + // Only create thread if it has not been created. + mThread = std::thread(&OneShotTimer::loop, this); } - mThread = std::thread(&OneShotTimer::loop, this); } void OneShotTimer::stop() { - { - std::lock_guard<std::mutex> lock(mMutex); - mState = TimerState::STOPPED; - } - mCondition.notify_all(); + mStopTriggered = true; + sem_post(&mSemaphore); + if (mThread.joinable()) { mThread.join(); + sem_destroy(&mSemaphore); } } void OneShotTimer::loop() { + TimerState state = TimerState::RESET; while (true) { bool triggerReset = false; bool triggerTimeout = false; - { - std::lock_guard<std::mutex> lock(mMutex); - if (mState == TimerState::STOPPED) { - break; - } - if (mState == TimerState::IDLE) { - mCondition.wait(mMutex); - continue; - } + state = checkForResetAndStop(state); + if (state == TimerState::STOPPED) { + break; + } - if (mState == TimerState::RESET) { - triggerReset = true; - } + if (state == TimerState::IDLE) { + sem_wait(&mSemaphore); + continue; } + + if (state == TimerState::RESET) { + triggerReset = true; + } + if (triggerReset && mResetCallback) { mResetCallback(); } - { // lock the mutex again. someone might have called stop meanwhile - std::lock_guard<std::mutex> lock(mMutex); - if (mState == TimerState::STOPPED) { - break; - } + state = checkForResetAndStop(state); + if (state == TimerState::STOPPED) { + break; + } + + auto triggerTime = std::chrono::steady_clock::now() + mInterval; + state = TimerState::WAITING; + while (state == TimerState::WAITING) { + constexpr auto zero = std::chrono::steady_clock::duration::zero(); + // Wait for mInterval time for semaphore signal. + struct timespec ts; + calculateTimeoutTime(std::chrono::nanoseconds(mInterval), &ts); + sem_clockwait(&mSemaphore, CLOCK_MONOTONIC, &ts); - auto triggerTime = std::chrono::steady_clock::now() + mInterval; - mState = TimerState::WAITING; - while (mState == TimerState::WAITING) { - constexpr auto zero = std::chrono::steady_clock::duration::zero(); - auto waitTime = triggerTime - std::chrono::steady_clock::now(); - if (waitTime > zero) mCondition.wait_for(mMutex, waitTime); - if (mState == TimerState::RESET) { - triggerTime = std::chrono::steady_clock::now() + mInterval; - mState = TimerState::WAITING; - } else if (mState == TimerState::WAITING && - (triggerTime - std::chrono::steady_clock::now()) <= zero) { - triggerTimeout = true; - mState = TimerState::IDLE; - } + state = checkForResetAndStop(state); + if (state == TimerState::RESET) { + triggerTime = std::chrono::steady_clock::now() + mInterval; + state = TimerState::WAITING; + } else if (state == TimerState::WAITING && + (triggerTime - std::chrono::steady_clock::now()) <= zero) { + triggerTimeout = true; + state = TimerState::IDLE; } } + if (triggerTimeout && mTimeoutCallback) { mTimeoutCallback(); } } } -void OneShotTimer::reset() { - { - std::lock_guard<std::mutex> lock(mMutex); - mState = TimerState::RESET; +OneShotTimer::TimerState OneShotTimer::checkForResetAndStop(TimerState state) { + // Stop takes precedence of the reset. + if (mStopTriggered.exchange(false)) { + return TimerState::STOPPED; + } + // If the state was stopped, the thread was joined, and we cannot reset + // the timer anymore. + if (state != TimerState::STOPPED && mResetTriggered.exchange(false)) { + return TimerState::RESET; } - mCondition.notify_all(); + return state; +} + +void OneShotTimer::reset() { + mResetTriggered = true; + sem_post(&mSemaphore); } std::string OneShotTimer::dump() const { diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h index b005754c1a..8bbd4f5983 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.h +++ b/services/surfaceflinger/Scheduler/OneShotTimer.h @@ -16,6 +16,7 @@ #pragma once +#include <semaphore.h> #include <chrono> #include <condition_variable> #include <thread> @@ -70,17 +71,15 @@ private: // Function that loops until the condition for stopping is met. void loop(); + // Checks whether mResetTriggered and mStopTriggered were set and updates + // mState if so. + TimerState checkForResetAndStop(TimerState state); + // Thread waiting for timer to expire. std::thread mThread; - // Condition used to notify mThread. - std::condition_variable_any mCondition; - - // Lock used for synchronizing the waiting thread with the application thread. - std::mutex mMutex; - - // Current timer state - TimerState mState GUARDED_BY(mMutex) = TimerState::RESET; + // Semaphore to keep mThread synchronized. + sem_t mSemaphore; // Interval after which timer expires. const Interval mInterval; @@ -90,6 +89,12 @@ private: // Callback that happens when timer expires. const TimeoutCallback mTimeoutCallback; + + // After removing lock guarding mState, the state can be now accessed at + // any time. Keep a bool if the reset or stop were requested, and occasionally + // check in the main loop if they were. + std::atomic<bool> mResetTriggered = false; + std::atomic<bool> mStopTriggered = false; }; } // namespace scheduler diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp deleted file mode 100644 index fe2e406775..0000000000 --- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "PhaseOffsets.h" - -#include <cutils/properties.h> - -#include <optional> - -#include "SurfaceFlingerProperties.h" - -namespace { - -std::optional<nsecs_t> getProperty(const char* name) { - char value[PROPERTY_VALUE_MAX]; - property_get(name, value, "-1"); - if (const int i = atoi(value); i != -1) return i; - return std::nullopt; -} - -bool fpsEqualsWithMargin(float fpsA, float fpsB) { - static constexpr float MARGIN = 0.01f; - return std::abs(fpsA - fpsB) <= MARGIN; -} - -std::vector<float> getRefreshRatesFromConfigs( - const android::scheduler::RefreshRateConfigs& refreshRateConfigs) { - const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates(); - std::vector<float> refreshRates; - refreshRates.reserve(allRefreshRates.size()); - - for (const auto& [ignored, refreshRate] : allRefreshRates) { - refreshRates.emplace_back(refreshRate->getFps()); - } - - return refreshRates; -} - -} // namespace - -namespace android::scheduler { - -PhaseConfiguration::~PhaseConfiguration() = default; - -namespace impl { - -PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs) - : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs), - refreshRateConfigs.getCurrentRefreshRate().getFps(), - sysprop::vsync_event_phase_offset_ns(1000000), - sysprop::vsync_sf_event_phase_offset_ns(1000000), - getProperty("debug.sf.early_phase_offset_ns"), - getProperty("debug.sf.early_gl_phase_offset_ns"), - getProperty("debug.sf.early_app_phase_offset_ns"), - getProperty("debug.sf.early_gl_app_phase_offset_ns"), - // Below defines the threshold when an offset is considered to be negative, - // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset - // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For - // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW - // vsync. - getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns") - .value_or(std::numeric_limits<nsecs_t>::max())) {} - -PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps, - nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs, - std::optional<nsecs_t> earlySfOffsetNs, - std::optional<nsecs_t> earlyGlSfOffsetNs, - std::optional<nsecs_t> earlyAppOffsetNs, - std::optional<nsecs_t> earlyGlAppOffsetNs, nsecs_t thresholdForNextVsync) - : mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs), - mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs), - mEarlySfOffsetNs(earlySfOffsetNs), - mEarlyGlSfOffsetNs(earlyGlSfOffsetNs), - mEarlyAppOffsetNs(earlyAppOffsetNs), - mEarlyGlAppOffsetNs(earlyGlAppOffsetNs), - mThresholdForNextVsync(thresholdForNextVsync), - mOffsets(initializeOffsets(refreshRates)), - mRefreshRateFps(currentFps) {} - -void PhaseOffsets::dump(std::string& result) const { - const auto [early, earlyGl, late] = getCurrentOffsets(); - using base::StringAppendF; - StringAppendF(&result, - " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n" - " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n" - " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n" - "next VSYNC threshold: %9" PRId64 " ns\n", - late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, - mThresholdForNextVsync); -} - -std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets( - const std::vector<float>& refreshRates) const { - std::unordered_map<float, Offsets> offsets; - - for (const auto& refreshRate : refreshRates) { - offsets.emplace(refreshRate, - getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate))); - } - return offsets; -} - -PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const { - if (fps > 65.0f) { - return getHighFpsOffsets(vsyncPeriod); - } else { - return getDefaultOffsets(vsyncPeriod); - } -} - -PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const { - return { - { - mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync - ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration, - - mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs), - }, - { - mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync - ? mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - : mEarlyGlSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration, - - mEarlyGlAppOffsetNs.value_or(mVSyncPhaseOffsetNs), - }, - { - mSfVSyncPhaseOffsetNs < mThresholdForNextVsync - ? mSfVSyncPhaseOffsetNs - : mSfVSyncPhaseOffsetNs - vsyncDuration, - - mVSyncPhaseOffsetNs, - }, - }; -} - -PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const { - const auto highFpsLateAppOffsetNs = - getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000); - const auto highFpsLateSfOffsetNs = - getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000); - - const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns"); - const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"); - const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns"); - const auto highFpsEarlyGlAppOffsetNs = - getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"); - - return { - { - highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync - ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) - : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) - - vsyncDuration, - - highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs), - }, - { - highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) < - mThresholdForNextVsync - ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) - : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) - - vsyncDuration, - - highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs), - }, - { - highFpsLateSfOffsetNs < mThresholdForNextVsync - ? highFpsLateSfOffsetNs - : highFpsLateSfOffsetNs - vsyncDuration, - - highFpsLateAppOffsetNs, - }, - }; -} - -PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const { - const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), - [&fps](const std::pair<float, Offsets>& candidateFps) { - return fpsEqualsWithMargin(fps, candidateFps.first); - }); - - if (iter != mOffsets.end()) { - return iter->second; - } - - // Unknown refresh rate. This might happen if we get a hotplug event for an external display. - // In this case just construct the offset. - ALOGW("Can't find offset for %.2f fps", fps); - return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps)); -} - -static void validateSysprops() { - const auto validatePropertyBool = [](const char* prop) { - LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop); - }; - - validatePropertyBool("debug.sf.use_phase_offsets_as_durations"); - - LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1, - "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting " - "duration"); - - LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1, - "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting " - "duration"); - - const auto validateProperty = [](const char* prop) { - LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(), - "%s is set to %" PRId64 " but expecting duration", prop, - getProperty(prop).value_or(-1)); - }; - - validateProperty("debug.sf.early_phase_offset_ns"); - validateProperty("debug.sf.early_gl_phase_offset_ns"); - validateProperty("debug.sf.early_app_phase_offset_ns"); - validateProperty("debug.sf.early_gl_app_phase_offset_ns"); - validateProperty("debug.sf.high_fps_late_app_phase_offset_ns"); - validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns"); - validateProperty("debug.sf.high_fps_early_phase_offset_ns"); - validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns"); - validateProperty("debug.sf.high_fps_early_app_phase_offset_ns"); - validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"); -} - -static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) { - return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration; -} - -static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) { - return sfDuration == -1 ? 1'000'000 - : vsyncDuration - (appDuration + sfDuration) % vsyncDuration; -} - -PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) const { - return Offsets{ - { - mSfEarlyDuration < vsyncDuration - ? sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - : sfDurationToOffset(mSfEarlyDuration, vsyncDuration) - vsyncDuration, - - appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration, vsyncDuration), - }, - { - mSfEarlyGlDuration < vsyncDuration - ? sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - : sfDurationToOffset(mSfEarlyGlDuration, vsyncDuration) - vsyncDuration, - - appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration, vsyncDuration), - }, - { - mSfDuration < vsyncDuration - ? sfDurationToOffset(mSfDuration, vsyncDuration) - : sfDurationToOffset(mSfDuration, vsyncDuration) - vsyncDuration, - - appDurationToOffset(mAppDuration, mSfDuration, vsyncDuration), - }, - }; -} - -std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets( - const std::vector<float>& refreshRates) const { - std::unordered_map<float, Offsets> offsets; - - for (const auto fps : refreshRates) { - offsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps))); - } - return offsets; -} - -PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs) - : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs), - refreshRateConfigs.getCurrentRefreshRate().getFps(), - getProperty("debug.sf.late.sf.duration").value_or(-1), - getProperty("debug.sf.late.app.duration").value_or(-1), - getProperty("debug.sf.early.sf.duration").value_or(mSfDuration), - getProperty("debug.sf.early.app.duration").value_or(mAppDuration), - getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration), - getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) { - validateSysprops(); -} - -PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps, - nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration, - nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration, - nsecs_t appEarlyGlDuration) - : mSfDuration(sfDuration), - mAppDuration(appDuration), - mSfEarlyDuration(sfEarlyDuration), - mAppEarlyDuration(appEarlyDuration), - mSfEarlyGlDuration(sfEarlyGlDuration), - mAppEarlyGlDuration(appEarlyGlDuration), - mOffsets(initializeOffsets(refreshRates)), - mRefreshRateFps(currentFps) {} - -PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const { - const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), [=](const auto& candidateFps) { - return fpsEqualsWithMargin(fps, candidateFps.first); - }); - - if (iter != mOffsets.end()) { - return iter->second; - } - - // Unknown refresh rate. This might happen if we get a hotplug event for an external display. - // In this case just construct the offset. - ALOGW("Can't find offset for %.2f fps", fps); - return constructOffsets(static_cast<nsecs_t>(1e9f / fps)); -} - -void PhaseDurations::dump(std::string& result) const { - const auto [early, earlyGl, late] = getCurrentOffsets(); - using base::StringAppendF; - StringAppendF(&result, - " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 - " ns\n" - " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64 - " ns\n" - " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 - " ns\n" - " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64 - " ns\n" - " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 - " ns\n" - " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64 - " ns\n", - late.app, - - late.sf, - - mAppDuration, mSfDuration, - - early.app, early.sf, - - mAppEarlyDuration, mSfEarlyDuration, - - earlyGl.app, - - earlyGl.sf, - - mAppEarlyGlDuration, mSfEarlyGlDuration); -} - -} // namespace impl -} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h deleted file mode 100644 index 9ec6d56fdf..0000000000 --- a/services/surfaceflinger/Scheduler/PhaseOffsets.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <unordered_map> - -#include "RefreshRateConfigs.h" -#include "VSyncModulator.h" - -namespace android::scheduler { - -/* - * This class encapsulates offsets for different refresh rates. Depending - * on what refresh rate we are using, and wheter we are composing in GL, - * different offsets will help us with latency. This class keeps track of - * which mode the device is on, and returns approprate offsets when needed. - */ -class PhaseConfiguration { -public: - using Offsets = VSyncModulator::OffsetsConfig; - - virtual ~PhaseConfiguration(); - - virtual Offsets getCurrentOffsets() const = 0; - virtual Offsets getOffsetsForRefreshRate(float fps) const = 0; - - virtual void setRefreshRateFps(float fps) = 0; - - virtual void dump(std::string& result) const = 0; -}; - -namespace impl { - -/* - * This is the old implementation of phase offsets and considered as deprecated. - * PhaseDurations is the new implementation. - */ -class PhaseOffsets : public scheduler::PhaseConfiguration { -public: - PhaseOffsets(const scheduler::RefreshRateConfigs&); - - // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate. - Offsets getOffsetsForRefreshRate(float fps) const override; - - // Returns early, early GL, and late offsets for Apps and SF. - Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); } - - // This function should be called when the device is switching between different - // refresh rates, to properly update the offsets. - void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; } - - // Returns current offsets in human friendly format. - void dump(std::string& result) const override; - -protected: - // Used for unit tests - PhaseOffsets(const std::vector<float>& refreshRates, float currentFps, - nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs, - std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGlSfOffsetNs, - std::optional<nsecs_t> earlyAppOffsetNs, std::optional<nsecs_t> earlyGlAppOffsetNs, - nsecs_t thresholdForNextVsync); - std::unordered_map<float, Offsets> initializeOffsets( - const std::vector<float>& refreshRates) const; - Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const; - Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const; - Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const; - - const nsecs_t mVSyncPhaseOffsetNs; - const nsecs_t mSfVSyncPhaseOffsetNs; - const std::optional<nsecs_t> mEarlySfOffsetNs; - const std::optional<nsecs_t> mEarlyGlSfOffsetNs; - const std::optional<nsecs_t> mEarlyAppOffsetNs; - const std::optional<nsecs_t> mEarlyGlAppOffsetNs; - const nsecs_t mThresholdForNextVsync; - const std::unordered_map<float, Offsets> mOffsets; - - std::atomic<float> mRefreshRateFps; -}; - -/* - * Class that encapsulates the phase offsets for SurfaceFlinger and App. - * The offsets are calculated from durations for each one of the (late, early, earlyGL) - * offset types. - */ -class PhaseDurations : public scheduler::PhaseConfiguration { -public: - PhaseDurations(const scheduler::RefreshRateConfigs&); - - // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate. - Offsets getOffsetsForRefreshRate(float fps) const override; - - // Returns early, early GL, and late offsets for Apps and SF. - Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); } - - // This function should be called when the device is switching between different - // refresh rates, to properly update the offsets. - void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; } - - // Returns current offsets in human friendly format. - void dump(std::string& result) const override; - -protected: - // Used for unit tests - PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration, - nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, - nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration); - -private: - std::unordered_map<float, Offsets> initializeOffsets(const std::vector<float>&) const; - PhaseDurations::Offsets constructOffsets(nsecs_t vsyncDuration) const; - - const nsecs_t mSfDuration; - const nsecs_t mAppDuration; - - const nsecs_t mSfEarlyDuration; - const nsecs_t mAppEarlyDuration; - - const nsecs_t mSfEarlyGlDuration; - const nsecs_t mAppEarlyGlDuration; - - const std::unordered_map<float, Offsets> mOffsets; - - std::atomic<float> mRefreshRateFps; -}; - -} // namespace impl -} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 8661b6ee0a..7d97e72fbb 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -48,6 +48,13 @@ std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) { } } +std::string RefreshRateConfigs::Policy::toString() { + return base::StringPrintf("default config ID: %d, allowGroupSwitching = %d" + ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]", + defaultConfig.value(), allowGroupSwitching, primaryRange.min, + primaryRange.max, appRequestRange.min, appRequestRange.max); +} + const RefreshRate& RefreshRateConfigs::getRefreshRateForContent( const std::vector<LayerRequirement>& layers) const { std::lock_guard lock(mLock); @@ -618,4 +625,36 @@ RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction return RefreshRateConfigs::KernelIdleTimerAction::TurnOn; } +void RefreshRateConfigs::setPreferredRefreshRateForUid(uid_t uid, float refreshRateHz) { + if (refreshRateHz > 0 && refreshRateHz < 1) { + return; + } + + std::lock_guard lock(mLock); + if (refreshRateHz != 0) { + mPreferredRefreshRateForUid[uid] = refreshRateHz; + } else { + mPreferredRefreshRateForUid.erase(uid); + } +} + +int RefreshRateConfigs::getRefreshRateDividerForUid(uid_t uid) const { + constexpr float kThreshold = 0.1f; + std::lock_guard lock(mLock); + + const auto iter = mPreferredRefreshRateForUid.find(uid); + if (iter == mPreferredRefreshRateForUid.end()) { + return 1; + } + + const auto refreshRateHz = iter->second; + const auto numPeriods = mCurrentRefreshRate->getFps() / refreshRateHz; + const auto numPeriodsRounded = std::round(numPeriods); + if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) { + return 1; + } + + return static_cast<int>(numPeriods); +} + } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 27bf0ecf9f..5cf7d07a38 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -108,6 +108,10 @@ public: std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>; struct Policy { + private: + static constexpr int kAllowGroupSwitchingDefault = false; + + public: struct Range { float min = 0; float max = std::numeric_limits<float>::max(); @@ -122,6 +126,8 @@ public: // The default config, used to ensure we only initiate display config switches within the // same config group as defaultConfigId's group. HwcConfigIndexType defaultConfig; + // Whether or not we switch config groups to get the best frame rate. + bool allowGroupSwitching = kAllowGroupSwitchingDefault; // The primary refresh rate range represents display manager's general guidance on the // display configs we'll consider when switching refresh rates. Unless we get an explicit // signal from an app, we should stay within this range. @@ -133,15 +139,23 @@ public: // app request range. The app request range will be greater than or equal to the primary // refresh rate range, never smaller. Range appRequestRange; - // Whether or not we switch config groups to get the best frame rate. Only used by tests. - bool allowGroupSwitching = false; Policy() = default; + Policy(HwcConfigIndexType defaultConfig, const Range& range) - : Policy(defaultConfig, range, range) {} + : Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {} + + Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const Range& range) + : Policy(defaultConfig, allowGroupSwitching, range, range) {} + Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange, const Range& appRequestRange) + : Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {} + + Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, + const Range& primaryRange, const Range& appRequestRange) : defaultConfig(defaultConfig), + allowGroupSwitching(allowGroupSwitching), primaryRange(primaryRange), appRequestRange(appRequestRange) {} @@ -152,6 +166,7 @@ public: } bool operator!=(const Policy& other) const { return !(*this == other); } + std::string toString(); }; // Return code set*Policy() to indicate the current policy is unchanged. @@ -280,6 +295,11 @@ public: RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, HwcConfigIndexType currentConfigId); + // Returns whether switching configs (refresh rate or resolution) is possible. + // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only + // differ in resolution. + bool canSwitch() const { return mRefreshRates.size() > 1; } + // Class to enumerate options around toggling the kernel timer on and off. We have an option // for no change to avoid extra calls to kernel. enum class KernelIdleTimerAction { @@ -291,6 +311,13 @@ public: // refresh rates. KernelIdleTimerAction getIdleTimerAction() const; + // Stores the preferred refresh rate that an app should run at. + // refreshRate == 0 means no preference. + void setPreferredRefreshRateForUid(uid_t, float refreshRateHz) EXCLUDES(mLock); + + // Returns a divider for the current refresh rate + int getRefreshRateDividerForUid(uid_t) const EXCLUDES(mLock); + private: friend class RefreshRateConfigsTest; @@ -348,6 +375,8 @@ private: Policy mDisplayManagerPolicy GUARDED_BY(mLock); std::optional<Policy> mOverridePolicy GUARDED_BY(mLock); + std::unordered_map<uid_t, float> mPreferredRefreshRateForUid GUARDED_BY(mLock); + // The min and max refresh rates supported by the device. // This will not change at runtime. const RefreshRate* mMinSupportedRefreshRate; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 5c0ba01cd3..a14019eeb5 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -20,17 +20,18 @@ #include "Scheduler.h" +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> -#include <cutils/properties.h> #include <input/InputWindow.h> #include <system/window.h> #include <ui/DisplayStatInfo.h> #include <utils/Timers.h> #include <utils/Trace.h> +#include <FrameTimeline/FrameTimeline.h> #include <algorithm> #include <cinttypes> #include <cstdint> @@ -39,9 +40,7 @@ #include <numeric> #include "../Layer.h" -#include "DispSync.h" #include "DispSyncSource.h" -#include "EventControlThread.h" #include "EventThread.h" #include "InjectVSyncSource.h" #include "OneShotTimer.h" @@ -51,6 +50,7 @@ #include "VSyncDispatchTimerQueue.h" #include "VSyncPredictor.h" #include "VSyncReactor.h" +#include "VsyncController.h" #define RETURN_IF_INVALID_HANDLE(handle, ...) \ do { \ @@ -60,67 +60,80 @@ } \ } while (false) +using namespace std::string_literals; + namespace android { -std::unique_ptr<DispSync> createDispSync(bool supportKernelTimer) { - // TODO (140302863) remove this and use the vsync_reactor system. - if (property_get_bool("debug.sf.vsync_reactor", true)) { - // TODO (144707443) tune Predictor tunables. - static constexpr int defaultRate = 60; - static constexpr auto initialPeriod = - std::chrono::duration<nsecs_t, std::ratio<1, defaultRate>>(1); - static constexpr size_t vsyncTimestampHistorySize = 20; - static constexpr size_t minimumSamplesForPrediction = 6; - static constexpr uint32_t discardOutlierPercent = 20; - auto tracker = std::make_unique< - scheduler::VSyncPredictor>(std::chrono::duration_cast<std::chrono::nanoseconds>( - initialPeriod) - .count(), - vsyncTimestampHistorySize, minimumSamplesForPrediction, - discardOutlierPercent); - - static constexpr auto vsyncMoveThreshold = - std::chrono::duration_cast<std::chrono::nanoseconds>(3ms); - static constexpr auto timerSlack = - std::chrono::duration_cast<std::chrono::nanoseconds>(500us); - auto dispatch = std::make_unique< - scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), *tracker, - timerSlack.count(), vsyncMoveThreshold.count()); - - static constexpr size_t pendingFenceLimit = 20; - return std::make_unique<scheduler::VSyncReactor>(std::make_unique<scheduler::SystemClock>(), - std::move(dispatch), std::move(tracker), - pendingFenceLimit, supportKernelTimer); - } else { - return std::make_unique<impl::DispSync>("SchedulerDispSync", - sysprop::running_without_sync_framework(true)); - } +namespace { + +std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() { + // TODO(b/144707443): Tune constants. + constexpr int kDefaultRate = 60; + constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1); + constexpr nsecs_t idealPeriod = + std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count(); + constexpr size_t vsyncTimestampHistorySize = 20; + constexpr size_t minimumSamplesForPrediction = 6; + constexpr uint32_t discardOutlierPercent = 20; + return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize, + minimumSamplesForPrediction, + discardOutlierPercent); } -Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, - const scheduler::RefreshRateConfigs& refreshRateConfig, - ISchedulerCallback& schedulerCallback, bool useContentDetectionV2, - bool useContentDetection) - : mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)), - mPrimaryDispSync(createDispSync(mSupportKernelTimer)), - mEventControlThread(new impl::EventControlThread(std::move(function))), - mSchedulerCallback(schedulerCallback), - mRefreshRateConfigs(refreshRateConfig), - mUseContentDetection(useContentDetection), - mUseContentDetectionV2(useContentDetectionV2) { - using namespace sysprop; +std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) { + // TODO(b/144707443): Tune constants. + constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms; + constexpr std::chrono::nanoseconds timerSlack = 500us; + return std::make_unique< + scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker, + timerSlack.count(), vsyncMoveThreshold.count()); +} - if (mUseContentDetectionV2) { - mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(refreshRateConfig); - } else { - mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>(); +const char* toContentDetectionString(bool useContentDetection, bool useContentDetectionV2) { + if (!useContentDetection) return "off"; + return useContentDetectionV2 ? "V2" : "V1"; +} + +} // namespace + +class PredictedVsyncTracer { +public: + PredictedVsyncTracer(scheduler::VSyncDispatch& dispatch) + : mRegistration(dispatch, std::bind(&PredictedVsyncTracer::callback, this), + "PredictedVsyncTracer") { + scheduleRegistration(); + } + +private: + TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0}; + scheduler::VSyncCallbackRegistration mRegistration; + + void scheduleRegistration() { mRegistration.schedule({0, 0, 0}); } + + void callback() { + mParity = !mParity; + scheduleRegistration(); } +}; - const int setIdleTimerMs = property_get_int32("debug.sf.set_idle_timer_ms", 0); +Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) + : Scheduler(configs, callback, + {.supportKernelTimer = sysprop::support_kernel_idle_timer(false), + .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false), + .useContentDetectionV2 = + base::GetBoolProperty("debug.sf.use_content_detection_v2"s, true)}) {} + +Scheduler::Scheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback, + Options options) + : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback, + createLayerHistory(configs, options.useContentDetectionV2), options) { + using namespace sysprop; + + const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0); if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) { - const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback - : &Scheduler::idleTimerCallback; + const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback + : &Scheduler::idleTimerCallback; mIdleTimer.emplace( std::chrono::milliseconds(millis), [this, callback] { std::invoke(callback, this, TimerState::Reset); }, @@ -146,18 +159,20 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, } } -Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync, - std::unique_ptr<EventControlThread> eventControlThread, - const scheduler::RefreshRateConfigs& configs, - ISchedulerCallback& schedulerCallback, bool useContentDetectionV2, - bool useContentDetection) - : mSupportKernelTimer(false), - mPrimaryDispSync(std::move(primaryDispSync)), - mEventControlThread(std::move(eventControlThread)), +Scheduler::Scheduler(VsyncSchedule schedule, const scheduler::RefreshRateConfigs& configs, + ISchedulerCallback& schedulerCallback, + std::unique_ptr<LayerHistory> layerHistory, Options options) + : mOptions(options), + mVsyncSchedule(std::move(schedule)), + mLayerHistory(std::move(layerHistory)), mSchedulerCallback(schedulerCallback), mRefreshRateConfigs(configs), - mUseContentDetection(useContentDetection), - mUseContentDetectionV2(useContentDetectionV2) {} + mPredictedVsyncTracer( + base::GetBoolProperty("debug.sf.show_predicted_vsync", false) + ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch) + : nullptr) { + mSchedulerCallback.setVsyncEnabled(false); +} Scheduler::~Scheduler() { // Ensure the OneShotTimer threads are joined before we start destroying state. @@ -166,22 +181,57 @@ Scheduler::~Scheduler() { mIdleTimer.reset(); } -DispSync& Scheduler::getPrimaryDispSync() { - return *mPrimaryDispSync; +Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) { + auto clock = std::make_unique<scheduler::SystemClock>(); + auto tracker = createVSyncTracker(); + auto dispatch = createVSyncDispatch(*tracker); + + // TODO(b/144707443): Tune constants. + constexpr size_t pendingFenceLimit = 20; + auto controller = + std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit, + supportKernelTimer); + return {std::move(controller), std::move(tracker), std::move(dispatch)}; +} + +std::unique_ptr<LayerHistory> Scheduler::createLayerHistory( + const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) { + if (!configs.canSwitch()) return nullptr; + + if (useContentDetectionV2) { + return std::make_unique<scheduler::impl::LayerHistoryV2>(configs); + } + + return std::make_unique<scheduler::impl::LayerHistory>(); +} + +std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( + const char* name, std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration, bool traceVsync) { + return std::make_unique<scheduler::DispSyncSource>(*mVsyncSchedule.dispatch, workDuration, + readyDuration, traceVsync, name); } -std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name, - nsecs_t phaseOffsetNs) { - return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs, - true /* traceVsync */, name); +bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const { + const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid); + if (divider <= 1) { + return true; + } + + return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, divider); } Scheduler::ConnectionHandle Scheduler::createConnection( - const char* connectionName, nsecs_t phaseOffsetNs, + const char* connectionName, frametimeline::TokenManager* tokenManager, + std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration, impl::EventThread::InterceptVSyncsCallback interceptCallback) { - auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs); - auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), - std::move(interceptCallback)); + auto vsyncSource = makePrimaryDispSyncSource(connectionName, workDuration, readyDuration); + auto throttleVsync = [this](nsecs_t expectedVsyncTimestamp, uid_t uid) { + return !isVsyncValid(expectedVsyncTimestamp, uid); + }; + auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), tokenManager, + std::move(interceptCallback), + std::move(throttleVsync)); return createConnection(std::move(eventThread)); } @@ -192,6 +242,7 @@ Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThr auto connection = createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress); + std::lock_guard<std::mutex> lock(mConnectionsLock); mConnections.emplace(handle, Connection{connection, std::move(eventThread)}); return handle; } @@ -203,29 +254,47 @@ sp<EventThreadConnection> Scheduler::createConnectionInternal( sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) { + std::lock_guard<std::mutex> lock(mConnectionsLock); RETURN_IF_INVALID_HANDLE(handle, nullptr); return createConnectionInternal(mConnections[handle].thread.get(), configChanged); } sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) { + std::lock_guard<std::mutex> lock(mConnectionsLock); RETURN_IF_INVALID_HANDLE(handle, nullptr); return mConnections[handle].connection; } void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId, bool connected) { - RETURN_IF_INVALID_HANDLE(handle); - mConnections[handle].thread->onHotplugReceived(displayId, connected); + android::EventThread* thread; + { + std::lock_guard<std::mutex> lock(mConnectionsLock); + RETURN_IF_INVALID_HANDLE(handle); + thread = mConnections[handle].thread.get(); + } + + thread->onHotplugReceived(displayId, connected); } void Scheduler::onScreenAcquired(ConnectionHandle handle) { - RETURN_IF_INVALID_HANDLE(handle); - mConnections[handle].thread->onScreenAcquired(); + android::EventThread* thread; + { + std::lock_guard<std::mutex> lock(mConnectionsLock); + RETURN_IF_INVALID_HANDLE(handle); + thread = mConnections[handle].thread.get(); + } + thread->onScreenAcquired(); } void Scheduler::onScreenReleased(ConnectionHandle handle) { - RETURN_IF_INVALID_HANDLE(handle); - mConnections[handle].thread->onScreenReleased(); + android::EventThread* thread; + { + std::lock_guard<std::mutex> lock(mConnectionsLock); + RETURN_IF_INVALID_HANDLE(handle); + thread = mConnections[handle].thread.get(); + } + thread->onScreenReleased(); } void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, @@ -237,6 +306,16 @@ void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalD } void Scheduler::dispatchCachedReportedConfig() { + // Check optional fields first. + if (!mFeatures.configId.has_value()) { + ALOGW("No config ID found, not dispatching cached config."); + return; + } + if (!mFeatures.cachedConfigChangedParams.has_value()) { + ALOGW("No config changed params found, not dispatching cached config."); + return; + } + const auto configId = *mFeatures.configId; const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod(); @@ -258,28 +337,45 @@ void Scheduler::dispatchCachedReportedConfig() { void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, HwcConfigIndexType configId, nsecs_t vsyncPeriod) { - RETURN_IF_INVALID_HANDLE(handle); - mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod); + android::EventThread* thread; + { + std::lock_guard<std::mutex> lock(mConnectionsLock); + RETURN_IF_INVALID_HANDLE(handle); + thread = mConnections[handle].thread.get(); + } + thread->onConfigChanged(displayId, configId, vsyncPeriod); } size_t Scheduler::getEventThreadConnectionCount(ConnectionHandle handle) { + std::lock_guard<std::mutex> lock(mConnectionsLock); RETURN_IF_INVALID_HANDLE(handle, 0); return mConnections[handle].thread->getEventThreadConnectionCount(); } void Scheduler::dump(ConnectionHandle handle, std::string& result) const { - RETURN_IF_INVALID_HANDLE(handle); - mConnections.at(handle).thread->dump(result); + android::EventThread* thread; + { + std::lock_guard<std::mutex> lock(mConnectionsLock); + RETURN_IF_INVALID_HANDLE(handle); + thread = mConnections.at(handle).thread.get(); + } + thread->dump(result); } -void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) { - RETURN_IF_INVALID_HANDLE(handle); - mConnections[handle].thread->setPhaseOffset(phaseOffset); +void Scheduler::setDuration(ConnectionHandle handle, std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration) { + android::EventThread* thread; + { + std::lock_guard<std::mutex> lock(mConnectionsLock); + RETURN_IF_INVALID_HANDLE(handle); + thread = mConnections[handle].thread.get(); + } + thread->setDuration(workDuration, readyDuration); } -void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) { - stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0, systemTime()); - stats->vsyncPeriod = mPrimaryDispSync->getPeriod(); +void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now) { + stats->vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now); + stats->vsyncPeriod = mVsyncSchedule.tracker->currentPeriod(); } Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) { @@ -295,7 +391,9 @@ Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) { auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), - impl::EventThread::InterceptVSyncsCallback()); + /*tokenManager=*/nullptr, + impl::EventThread::InterceptVSyncsCallback(), + impl::EventThread::ThrottleVsyncCallback()); mInjectorConnectionHandle = createConnection(std::move(eventThread)); } @@ -304,20 +402,20 @@ Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) { return mInjectorConnectionHandle; } -bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime) { +bool Scheduler::injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp) { if (!mInjectVSyncs || !mVSyncInjector) { return false; } - mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime); + mVSyncInjector->onInjectSyncEvent(when, expectedVSyncTime, deadlineTimestamp); return true; } void Scheduler::enableHardwareVsync() { std::lock_guard<std::mutex> lock(mHWVsyncLock); if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { - mPrimaryDispSync->beginResync(); - mEventControlThread->setVsyncEnabled(true); + mVsyncSchedule.tracker->resetModel(); + mSchedulerCallback.setVsyncEnabled(true); mPrimaryHWVsyncEnabled = true; } } @@ -325,8 +423,7 @@ void Scheduler::enableHardwareVsync() { void Scheduler::disableHardwareVsync(bool makeUnavailable) { std::lock_guard<std::mutex> lock(mHWVsyncLock); if (mPrimaryHWVsyncEnabled) { - mEventControlThread->setVsyncEnabled(false); - mPrimaryDispSync->endResync(); + mSchedulerCallback.setVsyncEnabled(false); mPrimaryHWVsyncEnabled = false; } if (makeUnavailable) { @@ -366,11 +463,11 @@ void Scheduler::resync() { void Scheduler::setVsyncPeriod(nsecs_t period) { std::lock_guard<std::mutex> lock(mHWVsyncLock); - mPrimaryDispSync->setPeriod(period); + mVsyncSchedule.controller->startPeriodTransition(period); if (!mPrimaryHWVsyncEnabled) { - mPrimaryDispSync->beginResync(); - mEventControlThread->setVsyncEnabled(true); + mVsyncSchedule.tracker->resetModel(); + mSchedulerCallback.setVsyncEnabled(true); mPrimaryHWVsyncEnabled = true; } } @@ -382,8 +479,8 @@ void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsy { // Scope for the lock std::lock_guard<std::mutex> lock(mHWVsyncLock); if (mPrimaryHWVsyncEnabled) { - needsHwVsync = - mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed); + needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod, + periodFlushed); } } @@ -395,7 +492,7 @@ void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsy } void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { - if (mPrimaryDispSync->addPresentFence(fenceTime)) { + if (mVsyncSchedule.controller->addPresentFence(fenceTime)) { enableHardwareVsync(); } else { disableHardwareVsync(false); @@ -403,11 +500,7 @@ void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) { } void Scheduler::setIgnorePresentFences(bool ignore) { - mPrimaryDispSync->setIgnorePresentFences(ignore); -} - -nsecs_t Scheduler::getDispSyncExpectedPresentTime(nsecs_t now) { - return mPrimaryDispSync->expectedPresentTime(now); + mVsyncSchedule.controller->setIgnorePresentFences(ignore); } void Scheduler::registerLayer(Layer* layer) { @@ -416,25 +509,25 @@ void Scheduler::registerLayer(Layer* layer) { const auto minFps = mRefreshRateConfigs.getMinRefreshRate().getFps(); const auto maxFps = mRefreshRateConfigs.getMaxRefreshRate().getFps(); - if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) { + if (layer->getWindowType() == InputWindowInfo::Type::STATUS_BAR) { mLayerHistory->registerLayer(layer, minFps, maxFps, scheduler::LayerHistory::LayerVoteType::NoVote); - } else if (!mUseContentDetection) { + } else if (!mOptions.useContentDetection) { // If the content detection feature is off, all layers are registered at Max. We still keep // the layer history, since we use it for other features (like Frame Rate API), so layers // still need to be registered. mLayerHistory->registerLayer(layer, minFps, maxFps, scheduler::LayerHistory::LayerVoteType::Max); - } else if (!mUseContentDetectionV2) { + } else if (!mOptions.useContentDetectionV2) { // In V1 of content detection, all layers are registered as Heuristic (unless it's // wallpaper). const auto highFps = - layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER ? minFps : maxFps; + layer->getWindowType() == InputWindowInfo::Type::WALLPAPER ? minFps : maxFps; mLayerHistory->registerLayer(layer, minFps, highFps, scheduler::LayerHistory::LayerVoteType::Heuristic); } else { - if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) { + if (layer->getWindowType() == InputWindowInfo::Type::WALLPAPER) { // Running Wallpaper at Min is considered as part of content detection. mLayerHistory->registerLayer(layer, minFps, maxFps, scheduler::LayerHistory::LayerVoteType::Min); @@ -499,19 +592,10 @@ void Scheduler::resetIdleTimer() { } void Scheduler::notifyTouchEvent() { - if (!mTouchTimer) return; - - // Touch event will boost the refresh rate to performance. - // Clear Layer History to get fresh FPS detection. - // NOTE: Instead of checking all the layers, we should be checking the layer - // that is currently on top. b/142507166 will give us this capability. - std::lock_guard<std::mutex> lock(mFeatureStateLock); - if (mLayerHistory) { - // Layer History will be cleared based on RefreshRateConfigs::getBestRefreshRate - + if (mTouchTimer) { mTouchTimer->reset(); - if (mSupportKernelTimer && mIdleTimer) { + if (mOptions.supportKernelTimer && mIdleTimer) { mIdleTimer->reset(); } } @@ -550,7 +634,7 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { refreshRate.getFps() <= FPS_THRESHOLD_FOR_KERNEL_TIMER) { // Disable HW VSYNC if the timer expired, as we don't need it enabled if // we're not pushing frames, and if we're in PERFORMANCE mode then we'll - // need to update the DispSync model anyway. + // need to update the VsyncController model anyway. disableHardwareVsync(false /* makeUnavailable */); } @@ -564,8 +648,14 @@ void Scheduler::idleTimerCallback(TimerState state) { void Scheduler::touchTimerCallback(TimerState state) { const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; + // Touch event will boost the refresh rate to performance. + // Clear layer history to get fresh FPS detection. + // NOTE: Instead of checking all the layers, we should be checking the layer + // that is currently on top. b/142507166 will give us this capability. if (handleTimerStateChanged(&mFeatures.touch, touch)) { - mLayerHistory->clear(); + if (mLayerHistory) { + mLayerHistory->clear(); + } } ATRACE_INT("TouchState", static_cast<int>(touch)); } @@ -577,14 +667,23 @@ void Scheduler::displayPowerTimerCallback(TimerState state) { void Scheduler::dump(std::string& result) const { using base::StringAppendF; - const char* const states[] = {"off", "on"}; - StringAppendF(&result, "+ Idle timer: %s\n", - mIdleTimer ? mIdleTimer->dump().c_str() : states[0]); + StringAppendF(&result, "+ Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off"); StringAppendF(&result, "+ Touch timer: %s\n", - mTouchTimer ? mTouchTimer->dump().c_str() : states[0]); - StringAppendF(&result, "+ Use content detection: %s\n\n", - sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off"); + mTouchTimer ? mTouchTimer->dump().c_str() : "off"); + StringAppendF(&result, "+ Content detection: %s %s\n\n", + toContentDetectionString(mOptions.useContentDetection, + mOptions.useContentDetectionV2), + mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)"); +} + +void Scheduler::dumpVsync(std::string& s) const { + using base::StringAppendF; + + StringAppendF(&s, "VSyncReactor:\n"); + mVsyncSchedule.controller->dump(s); + StringAppendF(&s, "VSyncDispatch:\n"); + mVsyncSchedule.dispatch->dump(s); } template <class T> @@ -631,7 +730,7 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType( const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active; const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired; - if (!mUseContentDetectionV2) { + if (!mOptions.useContentDetectionV2) { // As long as touch is active we want to be in performance mode. if (touchActive) { return mRefreshRateConfigs.getMaxRefreshRateByPolicy().getConfigId(); diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 730ea8f0c8..4c86d26a01 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -29,7 +29,6 @@ #include <ui/GraphicTypes.h> #pragma clang diagnostic pop -#include "EventControlThread.h" #include "EventThread.h" #include "LayerHistory.h" #include "OneShotTimer.h" @@ -41,49 +40,43 @@ namespace android { using namespace std::chrono_literals; using scheduler::LayerHistory; -class DispSync; class FenceTime; class InjectVSyncSource; -struct DisplayStateInfo; +class PredictedVsyncTracer; -class ISchedulerCallback { -public: - virtual ~ISchedulerCallback() = default; +namespace scheduler { +class VsyncController; +class VSyncDispatch; +class VSyncTracker; +} // namespace scheduler + +namespace frametimeline { +class TokenManager; +} // namespace frametimeline + +struct ISchedulerCallback { + virtual void setVsyncEnabled(bool) = 0; virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&, scheduler::RefreshRateConfigEvent) = 0; virtual void repaintEverythingForHWC() = 0; virtual void kernelTimerChanged(bool expired) = 0; -}; -class IPhaseOffsetControl { -public: - virtual ~IPhaseOffsetControl() = default; - virtual void setPhaseOffset(scheduler::ConnectionHandle, nsecs_t phaseOffset) = 0; +protected: + ~ISchedulerCallback() = default; }; -class Scheduler : public IPhaseOffsetControl { +class Scheduler { public: using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; using ConfigEvent = scheduler::RefreshRateConfigEvent; - // Indicates whether to start the transaction early, or at vsync time. - enum class TransactionStart { - Early, // DEPRECATED. Start the transaction early. Times out on its own - EarlyStart, // Start the transaction early and keep this config until EarlyEnd - EarlyEnd, // End the early config started at EarlyStart - Normal // Start the transaction at the normal time - }; - - Scheduler(impl::EventControlThread::SetVSyncEnabledFunction, - const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback, - bool useContentDetectionV2, bool useContentDetection); - - virtual ~Scheduler(); - - DispSync& getPrimaryDispSync(); + Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&); + ~Scheduler(); using ConnectionHandle = scheduler::ConnectionHandle; - ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs, + ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration, impl::EventThread::InterceptVSyncsCallback); sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle, @@ -100,17 +93,16 @@ public: void onScreenAcquired(ConnectionHandle); void onScreenReleased(ConnectionHandle); - // Modifies phase offset in the event thread. - void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset) override; + // Modifies work duration in the event thread. + void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration); - void getDisplayStatInfo(DisplayStatInfo* stats); + void getDisplayStatInfo(DisplayStatInfo* stats, nsecs_t now); // Returns injector handle if injection has toggled, or an invalid handle otherwise. ConnectionHandle enableVSyncInjection(bool enable); - // Returns false if injection is disabled. - bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime); - + bool injectVSync(nsecs_t when, nsecs_t expectedVSyncTime, nsecs_t deadlineTimestamp); void enableHardwareVsync(); void disableHardwareVsync(bool makeUnavailable); @@ -122,13 +114,12 @@ public: void resyncToHardwareVsync(bool makeAvailable, nsecs_t period); void resync(); - // Passes a vsync sample to DispSync. periodFlushed will be true if - // DispSync detected that the vsync period changed, and false otherwise. + // Passes a vsync sample to VsyncController. periodFlushed will be true if + // VsyncController detected that the vsync period changed, and false otherwise. void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, bool* periodFlushed); void addPresentFence(const std::shared_ptr<FenceTime>&); void setIgnorePresentFences(bool ignore); - nsecs_t getDispSyncExpectedPresentTime(nsecs_t now); // Layers are registered on creation, and unregistered when the weak reference expires. void registerLayer(Layer*); @@ -146,8 +137,15 @@ public: void setDisplayPowerState(bool normal); + scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; } + + // Returns true if a given vsync timestamp is considered valid vsync + // for a given uid + bool isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const; + void dump(std::string&) const; void dump(ConnectionHandle, std::string&) const; + void dumpVsync(std::string&) const; // Get the appropriate refresh for current conditions. std::optional<HwcConfigIndexType> getPreferredConfigId(); @@ -163,6 +161,11 @@ public: size_t getEventThreadConnectionCount(ConnectionHandle handle); + std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, + std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration, + bool traceVsync = true); + private: friend class TestableScheduler; @@ -172,12 +175,31 @@ private: enum class TimerState { Reset, Expired }; enum class TouchState { Inactive, Active }; + struct Options { + // Whether to use idle timer callbacks that support the kernel timer. + bool supportKernelTimer; + // Whether to use content detection at all. + bool useContentDetection; + // Whether to use improved content detection. + bool useContentDetectionV2; + }; + + struct VsyncSchedule { + std::unique_ptr<scheduler::VsyncController> controller; + std::unique_ptr<scheduler::VSyncTracker> tracker; + std::unique_ptr<scheduler::VSyncDispatch> dispatch; + }; + + // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers. + Scheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&, Options); + // Used by tests to inject mocks. - Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>, - const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback, - bool useContentDetectionV2, bool useContentDetection); + Scheduler(VsyncSchedule, const scheduler::RefreshRateConfigs&, ISchedulerCallback&, + std::unique_ptr<LayerHistory>, Options); - std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs); + static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer); + static std::unique_ptr<LayerHistory> createLayerHistory(const scheduler::RefreshRateConfigs&, + bool useContentDetectionV2); // Create a connection on the given EventThread. ConnectionHandle createConnection(std::unique_ptr<EventThread>); @@ -212,7 +234,8 @@ private: }; ConnectionHandle::Id mNextConnectionHandleId = 0; - std::unordered_map<ConnectionHandle, Connection> mConnections; + mutable std::mutex mConnectionsLock; + std::unordered_map<ConnectionHandle, Connection> mConnections GUARDED_BY(mConnectionsLock); bool mInjectVSyncs = false; InjectVSyncSource* mVSyncInjector = nullptr; @@ -224,14 +247,11 @@ private: std::atomic<nsecs_t> mLastResyncTime = 0; - // Whether to use idle timer callbacks that support the kernel timer. - const bool mSupportKernelTimer; - - std::unique_ptr<DispSync> mPrimaryDispSync; - std::unique_ptr<EventControlThread> mEventControlThread; + const Options mOptions; + VsyncSchedule mVsyncSchedule; // Used to choose refresh rate if content detection is enabled. - std::unique_ptr<LayerHistory> mLayerHistory; + const std::unique_ptr<LayerHistory> mLayerHistory; // Timer that records time between requests for next vsync. std::optional<scheduler::OneShotTimer> mIdleTimer; @@ -275,10 +295,7 @@ private: GUARDED_BY(mVsyncTimelineLock); static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms; - // This variable indicates whether to use the content detection feature at all. - const bool mUseContentDetection; - // This variable indicates whether to use V2 version of the content detection. - const bool mUseContentDetectionV2; + const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer; }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp index 59c336ab10..c9c2d84a2a 100644 --- a/services/surfaceflinger/Scheduler/Timer.cpp +++ b/services/surfaceflinger/Scheduler/Timer.cpp @@ -89,7 +89,7 @@ nsecs_t Timer::now() const { } void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); using namespace std::literals; static constexpr int ns_per_s = std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); @@ -109,7 +109,7 @@ void Timer::alarmAt(std::function<void()> const& cb, nsecs_t time) { } void Timer::alarmCancel() { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); struct itimerspec old_timer; struct itimerspec new_timer { @@ -192,7 +192,7 @@ bool Timer::dispatch() { setDebugState(DebugState::Running); std::function<void()> cb; { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); cb = mCallback; } if (cb) { @@ -211,7 +211,7 @@ bool Timer::dispatch() { } void Timer::setDebugState(DebugState state) { - std::lock_guard lk(mMutex); + std::lock_guard lock(mMutex); mDebugState = state; } @@ -233,7 +233,7 @@ const char* Timer::strDebugState(DebugState state) const { } void Timer::dump(std::string& result) const { - std::lock_guard lk(mMutex); + std::lock_guard lock(mMutex); StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState)); } diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index 2a2d7c5279..9d7110327b 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -40,11 +40,13 @@ public: /* * A callback that can be registered to be awoken at a given time relative to a vsync event. - * \param [in] vsyncTime The timestamp of the vsync the callback is for. - * \param [in] targetWakeupTime The timestamp of intended wakeup time of the cb. - * + * \param [in] vsyncTime: The timestamp of the vsync the callback is for. + * \param [in] targetWakeupTime: The timestamp of intended wakeup time of the cb. + * \param [in] readyTime: The timestamp of intended time where client needs to finish + * its work by. */ - using Callback = std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime)>; + using Callback = + std::function<void(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime)>; /* * Registers a callback that will be called at designated points on the vsync timeline. @@ -71,33 +73,61 @@ public: virtual void unregisterCallback(CallbackToken token) = 0; /* + * Timing information about a scheduled callback + * + * @workDuration: The time needed for the client to perform its work + * @readyDuration: The time needed for the client to be ready before a vsync event. + * For external (non-SF) clients, not only do we need to account for their + * workDuration, but we also need to account for the time SF will take to + * process their buffer/transaction. In this case, readyDuration will be set + * to the SF duration in order to provide enough end-to-end time, and to be + * able to provide the ready-by time (deadline) on the callback. + * For internal clients, we don't need to add additional padding, so + * readyDuration will typically be 0. + * @earliestVsync: The targeted display time. This will be snapped to the closest + * predicted vsync time after earliestVsync. + * + * callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync + * event. + */ + struct ScheduleTiming { + nsecs_t workDuration = 0; + nsecs_t readyDuration = 0; + nsecs_t earliestVsync = 0; + + bool operator==(const ScheduleTiming& other) const { + return workDuration == other.workDuration && readyDuration == other.readyDuration && + earliestVsync == other.earliestVsync; + } + + bool operator!=(const ScheduleTiming& other) const { return !(*this == other); } + }; + + /* * Schedules the registered callback to be dispatched. * - * The callback will be dispatched at 'workDuration' nanoseconds before a vsync event. + * The callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync + * event. * * The caller designates the earliest vsync event that should be targeted by the earliestVsync * parameter. - * The callback will be scheduled at (workDuration - predictedVsync), where predictedVsync - * is the first vsync event time where ( predictedVsync >= earliestVsync ). + * The callback will be scheduled at (workDuration + readyDuration - predictedVsync), where + * predictedVsync is the first vsync event time where ( predictedVsync >= earliestVsync ). * - * If (workDuration - earliestVsync) is in the past, or if a callback has already been - * dispatched for the predictedVsync, an error will be returned. + * If (workDuration + readyDuration - earliestVsync) is in the past, or if a callback has + * already been dispatched for the predictedVsync, an error will be returned. * * It is valid to reschedule a callback to a different time. * * \param [in] token The callback to schedule. - * \param [in] workDuration The time before the actual vsync time to invoke the callback - * associated with token. - * \param [in] earliestVsync The targeted display time. This will be snapped to the closest - * predicted vsync time after earliestVsync. + * \param [in] scheduleTiming The timing information for this schedule call * \return A ScheduleResult::Scheduled if callback was scheduled. * A ScheduleResult::CannotSchedule - * if (workDuration - earliestVsync) is in the past, or - * if a callback was dispatched for the predictedVsync already. - * A ScheduleResult::Error if there was another error. + * if (workDuration + readyDuration - earliestVsync) is in the past, + * or if a callback was dispatched for the predictedVsync already. A ScheduleResult::Error if + * there was another error. */ - virtual ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, - nsecs_t earliestVsync) = 0; + virtual ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) = 0; /* Cancels a scheduled callback, if possible. * @@ -129,7 +159,7 @@ public: ~VSyncCallbackRegistration(); // See documentation for VSyncDispatch::schedule. - ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync); + ScheduleResult schedule(VSyncDispatch::ScheduleTiming scheduleTiming); // See documentation for VSyncDispatch::cancel. CancelResult cancel(); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index ef9268015c..ca6ea27d14 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -35,8 +35,6 @@ VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& na nsecs_t minVsyncDistance) : mName(name), mCallback(cb), - mWorkDuration(0), - mEarliestVsync(0), mMinVsyncDistance(minVsyncDistance) {} std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const { @@ -54,6 +52,13 @@ std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::wakeupTime() const { return {mArmedInfo->mActualWakeupTime}; } +std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::readyTime() const { + if (!mArmedInfo) { + return {}; + } + return {mArmedInfo->mActualReadyTime}; +} + std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const { if (!mArmedInfo) { return {}; @@ -61,10 +66,10 @@ std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::targetVsync() const { return {mArmedInfo->mActualVsyncTime}; } -ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync, +ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker, nsecs_t now) { - auto nextVsyncTime = - tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration)); + auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom( + std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration)); bool const wouldSkipAVsyncTarget = mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance)); @@ -80,16 +85,15 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsec tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance); } - auto const nextWakeupTime = nextVsyncTime - workDuration; - mWorkDuration = workDuration; - mEarliestVsync = earliestVsync; - mArmedInfo = {nextWakeupTime, nextVsyncTime}; + auto const nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; + auto const nextReadyTime = nextVsyncTime - timing.readyDuration; + mScheduleTiming = timing; + mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime}; return ScheduleResult::Scheduled; } -void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(nsecs_t workDuration, - nsecs_t earliestVsync) { - mWorkloadUpdateInfo = {.earliestVsync = earliestVsync, .duration = workDuration}; +void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) { + mWorkloadUpdateInfo = timing; } bool VSyncDispatchTimerQueueEntry::hasPendingWorkloadUpdate() const { @@ -102,14 +106,18 @@ void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) { } if (mWorkloadUpdateInfo) { - mEarliestVsync = mWorkloadUpdateInfo->earliestVsync; - mWorkDuration = mWorkloadUpdateInfo->duration; + mScheduleTiming = *mWorkloadUpdateInfo; mWorkloadUpdateInfo.reset(); } - auto const nextVsyncTime = - tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration)); - mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime}; + const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration; + const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync); + + const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(earliestVsync); + const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration; + const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration; + + mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime}; } void VSyncDispatchTimerQueueEntry::disarm() { @@ -122,13 +130,14 @@ nsecs_t VSyncDispatchTimerQueueEntry::executing() { return *mLastDispatchTime; } -void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp) { +void VSyncDispatchTimerQueueEntry::callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp, + nsecs_t deadlineTimestamp) { { std::lock_guard<std::mutex> lk(mRunningMutex); mRunning = true; } - mCallback(vsyncTimestamp, wakeupTimestamp); + mCallback(vsyncTimestamp, wakeupTimestamp, deadlineTimestamp); std::lock_guard<std::mutex> lk(mRunningMutex); mRunning = false; @@ -144,15 +153,20 @@ void VSyncDispatchTimerQueueEntry::dump(std::string& result) const { std::lock_guard<std::mutex> lk(mRunningMutex); std::string armedInfo; if (mArmedInfo) { - StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]", + StringAppendF(&armedInfo, + "[wake up in %.2fms deadline in %.2fms for vsync %.2fms from now]", (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f, + (mArmedInfo->mActualReadyTime - systemTime()) / 1e6f, (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f); } StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(), mRunning ? "(in callback function)" : "", armedInfo.c_str()); - StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n", - mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f); + StringAppendF(&result, + "\t\t\tworkDuration: %.2fms readyDuration: %.2fms earliestVsync: %.2fms relative " + "to now\n", + mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f, + (mScheduleTiming.earliestVsync - systemTime()) / 1e6f); if (mLastDispatchTime) { StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n", @@ -171,7 +185,7 @@ VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, mMinVsyncDistance(minVsyncDistance) {} VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); cancelTimer(); } @@ -239,10 +253,11 @@ void VSyncDispatchTimerQueue::timerCallback() { std::shared_ptr<VSyncDispatchTimerQueueEntry> callback; nsecs_t vsyncTimestamp; nsecs_t wakeupTimestamp; + nsecs_t deadlineTimestamp; }; std::vector<Invocation> invocations; { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); auto const now = mTimeKeeper->now(); mLastTimerCallback = now; for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { @@ -252,11 +267,13 @@ void VSyncDispatchTimerQueue::timerCallback() { continue; } + auto const readyTime = callback->readyTime(); + auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0)); if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) { callback->executing(); - invocations.emplace_back( - Invocation{callback, *callback->lastExecutedVsyncTarget(), *wakeupTime}); + invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(), + *wakeupTime, *readyTime}); } } @@ -265,13 +282,14 @@ void VSyncDispatchTimerQueue::timerCallback() { } for (auto const& invocation : invocations) { - invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp); + invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp, + invocation.deadlineTimestamp); } } VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback( Callback const& callbackFn, std::string callbackName) { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); return CallbackToken{ mCallbacks .emplace(++mCallbackToken, @@ -284,7 +302,7 @@ VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { std::shared_ptr<VSyncDispatchTimerQueueEntry> entry = nullptr; { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); auto it = mCallbacks.find(token); if (it != mCallbacks.end()) { entry = it->second; @@ -297,11 +315,11 @@ void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { } } -ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration, - nsecs_t earliestVsync) { +ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, + ScheduleTiming scheduleTiming) { auto result = ScheduleResult::Error; { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); auto it = mCallbacks.find(token); if (it == mCallbacks.end()) { @@ -314,11 +332,11 @@ ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t wo * timer recalculation to avoid cancelling a callback that is about to fire. */ auto const rearmImminent = now > mIntendedWakeupTime; if (CC_UNLIKELY(rearmImminent)) { - callback->addPendingWorkloadUpdate(workDuration, earliestVsync); + callback->addPendingWorkloadUpdate(scheduleTiming); return ScheduleResult::Scheduled; } - result = callback->schedule(workDuration, earliestVsync, mTracker, now); + result = callback->schedule(scheduleTiming, mTracker, now); if (result == ScheduleResult::CannotSchedule) { return result; } @@ -332,7 +350,7 @@ ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t wo } CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); auto it = mCallbacks.find(token); if (it == mCallbacks.end()) { @@ -354,7 +372,7 @@ CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) { } void VSyncDispatchTimerQueue::dump(std::string& result) const { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); StringAppendF(&result, "\tTimer:\n"); mTimeKeeper->dump(result); StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f, @@ -396,11 +414,11 @@ VSyncCallbackRegistration::~VSyncCallbackRegistration() { if (mValidToken) mDispatch.get().unregisterCallback(mToken); } -ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) { +ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) { if (!mValidToken) { return ScheduleResult::Error; } - return mDispatch.get().schedule(mToken, workDuration, earliestVsync); + return mDispatch.get().schedule(mToken, scheduleTiming); } CancelResult VSyncCallbackRegistration::cancel() { diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h index 957c0d1eca..26237b63a3 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -47,7 +47,7 @@ public: std::optional<nsecs_t> lastExecutedVsyncTarget() const; // This moves the state from disarmed->armed and will calculate the wakeupTime. - ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker, + ScheduleResult schedule(VSyncDispatch::ScheduleTiming timing, VSyncTracker& tracker, nsecs_t now); // This will update armed entries with the latest vsync information. Entry remains armed. void update(VSyncTracker& tracker, nsecs_t now); @@ -56,6 +56,8 @@ public: // It will not update the wakeupTime. std::optional<nsecs_t> wakeupTime() const; + std::optional<nsecs_t> readyTime() const; + std::optional<nsecs_t> targetVsync() const; // This moves state from armed->disarmed. @@ -67,14 +69,14 @@ public: // Adds a pending upload of the earliestVSync and workDuration that will be applied on the next // call to update() - void addPendingWorkloadUpdate(nsecs_t workDuration, nsecs_t earliestVsync); + void addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming); // Checks if there is a pending update to the workload, returning true if so. bool hasPendingWorkloadUpdate() const; // End: functions that are not threadsafe. // Invoke the callback with the two given timestamps, moving the state from running->disarmed. - void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp); + void callback(nsecs_t vsyncTimestamp, nsecs_t wakeupTimestamp, nsecs_t deadlineTimestamp); // Block calling thread while the callback is executing. void ensureNotRunning(); @@ -84,22 +86,18 @@ private: std::string const mName; VSyncDispatch::Callback const mCallback; - nsecs_t mWorkDuration; - nsecs_t mEarliestVsync; + VSyncDispatch::ScheduleTiming mScheduleTiming; nsecs_t const mMinVsyncDistance; struct ArmingInfo { nsecs_t mActualWakeupTime; nsecs_t mActualVsyncTime; + nsecs_t mActualReadyTime; }; std::optional<ArmingInfo> mArmedInfo; std::optional<nsecs_t> mLastDispatchTime; - struct WorkloadUpdateInfo { - nsecs_t duration; - nsecs_t earliestVsync; - }; - std::optional<WorkloadUpdateInfo> mWorkloadUpdateInfo; + std::optional<VSyncDispatch::ScheduleTiming> mWorkloadUpdateInfo; mutable std::mutex mRunningMutex; std::condition_variable mCv; @@ -125,7 +123,7 @@ public: CallbackToken registerCallback(Callback const& callbackFn, std::string callbackName) final; void unregisterCallback(CallbackToken token) final; - ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final; + ScheduleResult schedule(CallbackToken token, ScheduleTiming scheduleTiming) final; CancelResult cancel(CallbackToken token) final; void dump(std::string& result) const final; diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp deleted file mode 100644 index 2567c0430d..0000000000 --- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "VSyncModulator.h" - -#include <cutils/properties.h> -#include <utils/Trace.h> - -#include <chrono> -#include <cinttypes> -#include <mutex> - -namespace android::scheduler { - -VSyncModulator::VSyncModulator(IPhaseOffsetControl& phaseOffsetControl, - Scheduler::ConnectionHandle appConnectionHandle, - Scheduler::ConnectionHandle sfConnectionHandle, - const OffsetsConfig& config) - : mPhaseOffsetControl(phaseOffsetControl), - mAppConnectionHandle(appConnectionHandle), - mSfConnectionHandle(sfConnectionHandle), - mOffsetsConfig(config) { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.sf.vsync_trace_detailed_info", value, "0"); - mTraceDetailedInfo = atoi(value); -} - -void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) { - std::lock_guard<std::mutex> lock(mMutex); - mOffsetsConfig = config; - updateOffsetsLocked(); -} - -void VSyncModulator::setTransactionStart(Scheduler::TransactionStart transactionStart) { - switch (transactionStart) { - case Scheduler::TransactionStart::EarlyStart: - ALOGW_IF(mExplicitEarlyWakeup, "Already in TransactionStart::EarlyStart"); - mExplicitEarlyWakeup = true; - break; - case Scheduler::TransactionStart::EarlyEnd: - ALOGW_IF(!mExplicitEarlyWakeup, "Not in TransactionStart::EarlyStart"); - mExplicitEarlyWakeup = false; - break; - case Scheduler::TransactionStart::Normal: - case Scheduler::TransactionStart::Early: - // Non explicit don't change the explicit early wakeup state - break; - } - - if (mTraceDetailedInfo) { - ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup); - } - - if (!mExplicitEarlyWakeup && - (transactionStart == Scheduler::TransactionStart::Early || - transactionStart == Scheduler::TransactionStart::EarlyEnd)) { - mRemainingEarlyFrameCount = MIN_EARLY_FRAME_COUNT_TRANSACTION; - mEarlyTxnStartTime = std::chrono::steady_clock::now(); - } - - // An early transaction stays an early transaction. - if (transactionStart == mTransactionStart || - mTransactionStart == Scheduler::TransactionStart::EarlyEnd) { - return; - } - mTransactionStart = transactionStart; - updateOffsets(); -} - -void VSyncModulator::onTransactionHandled() { - mTxnAppliedTime = std::chrono::steady_clock::now(); - if (mTransactionStart == Scheduler::TransactionStart::Normal) return; - mTransactionStart = Scheduler::TransactionStart::Normal; - updateOffsets(); -} - -void VSyncModulator::onRefreshRateChangeInitiated() { - if (mRefreshRateChangePending) { - return; - } - mRefreshRateChangePending = true; - updateOffsets(); -} - -void VSyncModulator::onRefreshRateChangeCompleted() { - if (!mRefreshRateChangePending) { - return; - } - mRefreshRateChangePending = false; - updateOffsets(); -} - -void VSyncModulator::onRefreshed(bool usedRenderEngine) { - bool updateOffsetsNeeded = false; - - // Apply a margin to account for potential data races - // This might make us stay in early offsets for one - // additional frame but it's better to be conservative here. - if ((mEarlyTxnStartTime.load() + MARGIN_FOR_TX_APPLY) < mTxnAppliedTime.load()) { - if (mRemainingEarlyFrameCount > 0) { - mRemainingEarlyFrameCount--; - updateOffsetsNeeded = true; - } - } - if (usedRenderEngine) { - mRemainingRenderEngineUsageCount = MIN_EARLY_GL_FRAME_COUNT_TRANSACTION; - updateOffsetsNeeded = true; - } else if (mRemainingRenderEngineUsageCount > 0) { - mRemainingRenderEngineUsageCount--; - updateOffsetsNeeded = true; - } - if (updateOffsetsNeeded) { - updateOffsets(); - } -} - -VSyncModulator::Offsets VSyncModulator::getOffsets() const { - std::lock_guard<std::mutex> lock(mMutex); - return mOffsets; -} - -const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const { - // Early offsets are used if we're in the middle of a refresh rate - // change, or if we recently begin a transaction. - if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd || - mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) { - return mOffsetsConfig.early; - } else if (mRemainingRenderEngineUsageCount > 0) { - return mOffsetsConfig.earlyGl; - } else { - return mOffsetsConfig.late; - } -} - -void VSyncModulator::updateOffsets() { - std::lock_guard<std::mutex> lock(mMutex); - updateOffsetsLocked(); -} - -void VSyncModulator::updateOffsetsLocked() { - const Offsets& offsets = getNextOffsets(); - - mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf); - mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app); - - mOffsets = offsets; - - if (!mTraceDetailedInfo) { - return; - } - - const bool isEarly = &offsets == &mOffsetsConfig.early; - const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl; - const bool isLate = &offsets == &mOffsetsConfig.late; - - ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly); - ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl); - ATRACE_INT("Vsync-LateOffsetsOn", isLate); -} - -} // namespace android::scheduler - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h deleted file mode 100644 index ab678c98c5..0000000000 --- a/services/surfaceflinger/Scheduler/VSyncModulator.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <chrono> -#include <mutex> - -#include "Scheduler.h" - -namespace android::scheduler { - -/* - * Modulates the vsync-offsets depending on current SurfaceFlinger state. - */ -class VSyncModulator { -private: - // Number of frames we'll keep the early phase offsets once they are activated for a - // transaction. This acts as a low-pass filter in case the client isn't quick enough in - // sending new transactions. - static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2; - - // Number of frames we'll keep the early gl phase offsets once they are activated. - // This acts as a low-pass filter to avoid scenarios where we rapidly - // switch in and out of gl composition. - static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2; - - // Margin used to account for potential data races - static const constexpr std::chrono::nanoseconds MARGIN_FOR_TX_APPLY = 1ms; - -public: - // Wrapper for a collection of surfaceflinger/app offsets for a particular - // configuration. - struct Offsets { - nsecs_t sf; - nsecs_t app; - - bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; } - - bool operator!=(const Offsets& other) const { return !(*this == other); } - }; - - struct OffsetsConfig { - Offsets early; // For transactions with the eEarlyWakeup flag. - Offsets earlyGl; // As above but while compositing with GL. - Offsets late; // Default. - - bool operator==(const OffsetsConfig& other) const { - return early == other.early && earlyGl == other.earlyGl && late == other.late; - } - - bool operator!=(const OffsetsConfig& other) const { return !(*this == other); } - }; - - VSyncModulator(IPhaseOffsetControl&, ConnectionHandle appConnectionHandle, - ConnectionHandle sfConnectionHandle, const OffsetsConfig&); - - void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex); - - // Signals that a transaction has started, and changes offsets accordingly. - void setTransactionStart(Scheduler::TransactionStart transactionStart); - - // Signals that a transaction has been completed, so that we can finish - // special handling for a transaction. - void onTransactionHandled(); - - // Called when we send a refresh rate change to hardware composer, so that - // we can move into early offsets. - void onRefreshRateChangeInitiated(); - - // Called when we detect from vsync signals that the refresh rate changed. - // This way we can move out of early offsets if no longer necessary. - void onRefreshRateChangeCompleted(); - - // Called when the display is presenting a new frame. usedRenderEngine - // should be set to true if RenderEngine was involved with composing the new - // frame. - void onRefreshed(bool usedRenderEngine); - - // Returns the offsets that we are currently using - Offsets getOffsets() const EXCLUDES(mMutex); - -private: - friend class VSyncModulatorTest; - // Returns the next offsets that we should be using - const Offsets& getNextOffsets() const REQUIRES(mMutex); - // Updates offsets and persists them into the scheduler framework. - void updateOffsets() EXCLUDES(mMutex); - void updateOffsetsLocked() REQUIRES(mMutex); - - IPhaseOffsetControl& mPhaseOffsetControl; - const ConnectionHandle mAppConnectionHandle; - const ConnectionHandle mSfConnectionHandle; - - mutable std::mutex mMutex; - OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex); - - Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late}; - - std::atomic<Scheduler::TransactionStart> mTransactionStart = - Scheduler::TransactionStart::Normal; - std::atomic<bool> mRefreshRateChangePending = false; - std::atomic<bool> mExplicitEarlyWakeup = false; - std::atomic<int> mRemainingEarlyFrameCount = 0; - std::atomic<int> mRemainingRenderEngineUsageCount = 0; - std::atomic<std::chrono::steady_clock::time_point> mEarlyTxnStartTime = {}; - std::atomic<std::chrono::steady_clock::time_point> mTxnAppliedTime = {}; - - bool mTraceDetailedInfo = false; -}; - -} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 61f3fbbdf1..75d1e6f132 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #define ATRACE_TAG ATRACE_TAG_GRAPHICS //#define LOG_NDEBUG 0 #include "VSyncPredictor.h" @@ -31,6 +27,9 @@ #include <chrono> #include <sstream> +#undef LOG_TAG +#define LOG_TAG "VSyncPredictor" + namespace android::scheduler { using base::StringAppendF; @@ -54,7 +53,7 @@ inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const } } -inline size_t VSyncPredictor::next(int i) const { +inline size_t VSyncPredictor::next(size_t i) const { return (i + 1) % mTimestamps.size(); } @@ -69,12 +68,12 @@ bool VSyncPredictor::validate(nsecs_t timestamp) const { } nsecs_t VSyncPredictor::currentPeriod() const { - std::lock_guard<std::mutex> lk(mMutex); - return std::get<0>(mRateMap.find(mIdealPeriod)->second); + std::lock_guard lock(mMutex); + return mRateMap.find(mIdealPeriod)->second.slope; } bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { - std::lock_guard<std::mutex> lk(mMutex); + std::lock_guard lock(mMutex); if (!validate(timestamp)) { // VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored, @@ -122,7 +121,7 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { // normalizing to the oldest timestamp cuts down on error in calculating the intercept. auto const oldest_ts = *std::min_element(mTimestamps.begin(), mTimestamps.end()); auto it = mRateMap.find(mIdealPeriod); - auto const currentPeriod = std::get<0>(it->second); + auto const currentPeriod = it->second.slope; // TODO (b/144707443): its important that there's some precision in the mean of the ordinals // for the intercept calculation, so scale the ordinals by 1000 to continue // fixed point calculation. Explore expanding @@ -138,14 +137,14 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { auto meanTS = scheduler::calculate_mean(vsyncTS); auto meanOrdinal = scheduler::calculate_mean(ordinals); - for (auto i = 0; i < vsyncTS.size(); i++) { + for (size_t i = 0; i < vsyncTS.size(); i++) { vsyncTS[i] -= meanTS; ordinals[i] -= meanOrdinal; } auto top = 0ll; auto bottom = 0ll; - for (auto i = 0; i < vsyncTS.size(); i++) { + for (size_t i = 0; i < vsyncTS.size(); i++) { top += vsyncTS[i] * ordinals[i]; bottom += ordinals[i] * ordinals[i]; } @@ -176,10 +175,8 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { return true; } -nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { - std::lock_guard<std::mutex> lk(mMutex); - - auto const [slope, intercept] = getVSyncPredictionModel(lk); +nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const { + auto const [slope, intercept] = getVSyncPredictionModelLocked(); if (mTimestamps.empty()) { traceInt64If("VSP-mode", 1); @@ -214,20 +211,78 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { return prediction; } -std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel() const { - std::lock_guard<std::mutex> lk(mMutex); - return VSyncPredictor::getVSyncPredictionModel(lk); +nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { + std::lock_guard lock(mMutex); + return nextAnticipatedVSyncTimeFromLocked(timePoint); +} + +/* + * Returns whether a given vsync timestamp is in phase with a vsync divider. + * For example, if the vsync timestamps are (0,16,32,48): + * isVSyncInPhase(0, 2) = true + * isVSyncInPhase(16, 2) = false + * isVSyncInPhase(32, 2) = true + */ +bool VSyncPredictor::isVSyncInPhase(nsecs_t timePoint, int divider) const { + struct VsyncError { + nsecs_t vsyncTimestamp; + float error; + + bool operator<(const VsyncError& other) const { return error < other.error; } + }; + + std::lock_guard lock(mMutex); + if (divider <= 1) { + return true; + } + + const nsecs_t period = mRateMap[mIdealPeriod].slope; + const nsecs_t justBeforeTimePoint = timePoint - period / 2; + const nsecs_t dividedPeriod = mIdealPeriod / divider; + + // If this is the first time we have asked about this divider with the + // current vsync period, it is considered in phase and we store the closest + // vsync timestamp + const auto knownTimestampIter = mRateDividerKnownTimestampMap.find(dividedPeriod); + if (knownTimestampIter == mRateDividerKnownTimestampMap.end()) { + const auto vsync = nextAnticipatedVSyncTimeFromLocked(justBeforeTimePoint); + mRateDividerKnownTimestampMap[dividedPeriod] = vsync; + return true; + } + + // Find the next N vsync timestamp where N is the divider. + // One of these vsyncs will be in phase. We return the one which is + // the most aligned with the last known in phase vsync + std::vector<VsyncError> vsyncs(static_cast<size_t>(divider)); + const nsecs_t knownVsync = knownTimestampIter->second; + nsecs_t point = justBeforeTimePoint; + for (size_t i = 0; i < divider; i++) { + const nsecs_t vsync = nextAnticipatedVSyncTimeFromLocked(point); + const auto numPeriods = static_cast<float>(vsync - knownVsync) / (period * divider); + const auto error = std::abs(std::round(numPeriods) - numPeriods); + vsyncs[i] = {vsync, error}; + point = vsync + 1; + } + + const auto minVsyncError = std::min_element(vsyncs.begin(), vsyncs.end()); + mRateDividerKnownTimestampMap[dividedPeriod] = minVsyncError->vsyncTimestamp; + return std::abs(minVsyncError->vsyncTimestamp - timePoint) < period / 2; +} + +VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { + std::lock_guard lock(mMutex); + const auto model = VSyncPredictor::getVSyncPredictionModelLocked(); + return {model.slope, model.intercept}; } -std::tuple<nsecs_t, nsecs_t> VSyncPredictor::getVSyncPredictionModel( - std::lock_guard<std::mutex> const&) const { +VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { return mRateMap.find(mIdealPeriod)->second; } void VSyncPredictor::setPeriod(nsecs_t period) { ATRACE_CALL(); - std::lock_guard<std::mutex> lk(mMutex); + std::lock_guard lock(mMutex); static constexpr size_t kSizeLimit = 30; if (CC_UNLIKELY(mRateMap.size() == kSizeLimit)) { mRateMap.erase(mRateMap.begin()); @@ -256,29 +311,27 @@ void VSyncPredictor::clearTimestamps() { } bool VSyncPredictor::needsMoreSamples() const { - std::lock_guard<std::mutex> lk(mMutex); + std::lock_guard lock(mMutex); return mTimestamps.size() < kMinimumSamplesForPrediction; } void VSyncPredictor::resetModel() { - std::lock_guard<std::mutex> lk(mMutex); + std::lock_guard lock(mMutex); mRateMap[mIdealPeriod] = {mIdealPeriod, 0}; clearTimestamps(); } void VSyncPredictor::dump(std::string& result) const { - std::lock_guard<std::mutex> lk(mMutex); + std::lock_guard lock(mMutex); StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f); StringAppendF(&result, "\tRefresh Rate Map:\n"); for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) { StringAppendF(&result, "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n", - idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f, - std::get<1>(periodInterceptTuple)); + idealPeriod / 1e6f, periodInterceptTuple.slope / 1e6f, + periodInterceptTuple.intercept); } } } // namespace android::scheduler -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 5f3c418fed..381cf81f5c 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -38,10 +38,10 @@ public: uint32_t outlierTolerancePercent); ~VSyncPredictor(); - bool addVsyncTimestamp(nsecs_t timestamp) final; - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final; - nsecs_t currentPeriod() const final; - void resetModel() final; + bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final EXCLUDES(mMutex); + nsecs_t currentPeriod() const final EXCLUDES(mMutex); + void resetModel() final EXCLUDES(mMutex); /* * Inform the model that the period is anticipated to change to a new value. @@ -50,16 +50,23 @@ public: * * \param [in] period The new period that should be used. */ - void setPeriod(nsecs_t period) final; + void setPeriod(nsecs_t period) final EXCLUDES(mMutex); /* Query if the model is in need of more samples to make a prediction. * \return True, if model would benefit from more samples, False if not. */ - bool needsMoreSamples() const final; + bool needsMoreSamples() const final EXCLUDES(mMutex); - std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const; + struct Model { + nsecs_t slope; + nsecs_t intercept; + }; - void dump(std::string& result) const final; + VSyncPredictor::Model getVSyncPredictionModel() const EXCLUDES(mMutex); + + bool isVSyncInPhase(nsecs_t timePoint, int divider) const final EXCLUDES(mMutex); + + void dump(std::string& result) const final EXCLUDES(mMutex); private: VSyncPredictor(VSyncPredictor const&) = delete; @@ -74,17 +81,23 @@ private: size_t const kOutlierTolerancePercent; std::mutex mutable mMutex; - size_t next(int i) const REQUIRES(mMutex); + size_t next(size_t i) const REQUIRES(mMutex); bool validate(nsecs_t timestamp) const REQUIRES(mMutex); - std::tuple<nsecs_t, nsecs_t> getVSyncPredictionModel(std::lock_guard<std::mutex> const&) const - REQUIRES(mMutex); + + Model getVSyncPredictionModelLocked() const REQUIRES(mMutex); + + nsecs_t nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const REQUIRES(mMutex); nsecs_t mIdealPeriod GUARDED_BY(mMutex); std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); - std::unordered_map<nsecs_t, std::tuple<nsecs_t, nsecs_t>> mutable mRateMap GUARDED_BY(mMutex); + // Map between ideal vsync period and the calculated model + std::unordered_map<nsecs_t, Model> mutable mRateMap GUARDED_BY(mMutex); + + // Map between the divided vsync period and the last known vsync timestamp + std::unordered_map<nsecs_t, nsecs_t> mutable mRateDividerKnownTimestampMap GUARDED_BY(mMutex); - int mLastTimestampIndex GUARDED_BY(mMutex) = 0; + size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); }; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index efa8bab8fe..7b5d4626dc 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -30,136 +30,23 @@ namespace android::scheduler { using base::StringAppendF; +VsyncController::~VsyncController() = default; + Clock::~Clock() = default; nsecs_t SystemClock::now() const { return systemTime(SYSTEM_TIME_MONOTONIC); } -class PredictedVsyncTracer { -public: - PredictedVsyncTracer(VSyncDispatch& dispatch) - : mRegistration(dispatch, - std::bind(&PredictedVsyncTracer::callback, this, std::placeholders::_1, - std::placeholders::_2), - "PredictedVsyncTracer") { - mRegistration.schedule(0, 0); - } - -private: - TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0}; - VSyncCallbackRegistration mRegistration; - - void callback(nsecs_t /*vsyncTime*/, nsecs_t /*targetWakeupTim*/) { - mParity = !mParity; - mRegistration.schedule(0, 0); - } -}; - -VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch, - std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit, - bool supportKernelIdleTimer) +VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, + size_t pendingFenceLimit, bool supportKernelIdleTimer) : mClock(std::move(clock)), - mTracker(std::move(tracker)), - mDispatch(std::move(dispatch)), + mTracker(tracker), mPendingLimit(pendingFenceLimit), - mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false) - ? std::make_unique<PredictedVsyncTracer>(*mDispatch) - : nullptr), mSupportKernelIdleTimer(supportKernelIdleTimer) {} VSyncReactor::~VSyncReactor() = default; -// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts -// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic -// for now. -class CallbackRepeater { -public: - CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name, - nsecs_t period, nsecs_t offset, nsecs_t notBefore) - : mName(name), - mCallback(cb), - mRegistration(dispatch, - std::bind(&CallbackRepeater::callback, this, std::placeholders::_1, - std::placeholders::_2), - mName), - mPeriod(period), - mOffset(offset), - mLastCallTime(notBefore) {} - - ~CallbackRepeater() { - std::lock_guard<std::mutex> lk(mMutex); - mRegistration.cancel(); - } - - void start(nsecs_t offset) { - std::lock_guard<std::mutex> lk(mMutex); - mStopped = false; - mOffset = offset; - - auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime); - LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled), - "Error scheduling callback: rc %X", schedule_result); - } - - void setPeriod(nsecs_t period) { - std::lock_guard<std::mutex> lk(mMutex); - if (period == mPeriod) { - return; - } - mPeriod = period; - } - - void stop() { - std::lock_guard<std::mutex> lk(mMutex); - LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped"); - mStopped = true; - mRegistration.cancel(); - } - - void dump(std::string& result) const { - std::lock_guard<std::mutex> lk(mMutex); - StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n", - mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f, - mStopped ? "stopped" : "running"); - } - -private: - void callback(nsecs_t vsynctime, nsecs_t wakeupTime) { - { - std::lock_guard<std::mutex> lk(mMutex); - mLastCallTime = vsynctime; - } - - mCallback->onDispSyncEvent(wakeupTime, vsynctime); - - { - std::lock_guard<std::mutex> lk(mMutex); - if (mStopped) { - return; - } - auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime); - LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled), - "Error rescheduling callback: rc %X", schedule_result); - } - } - - // DispSync offsets are defined as time after the vsync before presentation. - // VSyncReactor workloads are defined as time before the intended presentation vsync. - // Note change in sign between the two defnitions. - nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; } - - const std::string mName; - DispSync::Callback* const mCallback; - - std::mutex mutable mMutex; - VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex); - bool mStopped GUARDED_BY(mMutex) = false; - nsecs_t mPeriod GUARDED_BY(mMutex); - nsecs_t mOffset GUARDED_BY(mMutex); - nsecs_t mLastCallTime GUARDED_BY(mMutex); -}; - -bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) { +bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) { if (!fence) { return false; } @@ -169,7 +56,7 @@ bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) { return true; } - std::lock_guard<std::mutex> lk(mMutex); + std::lock_guard lock(mMutex); if (mExternalIgnoreFences || mInternalIgnoreFences) { return true; } @@ -182,7 +69,7 @@ bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) { } else if (time == Fence::SIGNAL_TIME_INVALID) { it = mUnfiredFences.erase(it); } else { - timestampAccepted &= mTracker->addVsyncTimestamp(time); + timestampAccepted &= mTracker.addVsyncTimestamp(time); it = mUnfiredFences.erase(it); } @@ -194,7 +81,7 @@ bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) { } mUnfiredFences.push_back(fence); } else { - timestampAccepted &= mTracker->addVsyncTimestamp(signalTime); + timestampAccepted &= mTracker.addVsyncTimestamp(signalTime); } if (!timestampAccepted) { @@ -206,14 +93,14 @@ bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) { return mMoreSamplesNeeded; } -void VSyncReactor::setIgnorePresentFences(bool ignoration) { - std::lock_guard<std::mutex> lk(mMutex); - mExternalIgnoreFences = ignoration; +void VSyncReactor::setIgnorePresentFences(bool ignore) { + std::lock_guard lock(mMutex); + mExternalIgnoreFences = ignore; updateIgnorePresentFencesInternal(); } -void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) { - mInternalIgnoreFences = ignoration; +void VSyncReactor::setIgnorePresentFencesInternal(bool ignore) { + mInternalIgnoreFences = ignore; updateIgnorePresentFencesInternal(); } @@ -223,16 +110,7 @@ void VSyncReactor::updateIgnorePresentFencesInternal() { } } -nsecs_t VSyncReactor::computeNextRefresh(int periodOffset, nsecs_t now) const { - auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0; - return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod); -} - -nsecs_t VSyncReactor::expectedPresentTime(nsecs_t now) { - return mTracker->nextAnticipatedVSyncTimeFrom(now); -} - -void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) { +void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) { ATRACE_CALL(); mPeriodConfirmationInProgress = true; mPeriodTransitioningTo = newPeriod; @@ -247,30 +125,20 @@ void VSyncReactor::endPeriodTransition() { mLastHwVsync.reset(); } -void VSyncReactor::setPeriod(nsecs_t period) { +void VSyncReactor::startPeriodTransition(nsecs_t period) { ATRACE_INT64("VSR-setPeriod", period); - std::lock_guard lk(mMutex); + std::lock_guard lock(mMutex); mLastHwVsync.reset(); - if (!mSupportKernelIdleTimer && period == getPeriod()) { + if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) { endPeriodTransition(); setIgnorePresentFencesInternal(false); mMoreSamplesNeeded = false; } else { - startPeriodTransition(period); + startPeriodTransitionInternal(period); } } -nsecs_t VSyncReactor::getPeriod() { - return mTracker->currentPeriod(); -} - -void VSyncReactor::beginResync() { - mTracker->resetModel(); -} - -void VSyncReactor::endResync() {} - bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> HwcVsyncPeriod) { if (!mPeriodConfirmationInProgress) { return false; @@ -281,13 +149,13 @@ bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_ } const bool periodIsChanging = - mPeriodTransitioningTo && (*mPeriodTransitioningTo != getPeriod()); + mPeriodTransitioningTo && (*mPeriodTransitioningTo != mTracker.currentPeriod()); if (mSupportKernelIdleTimer && !periodIsChanging) { // Clear out the Composer-provided period and use the allowance logic below HwcVsyncPeriod = {}; } - auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : getPeriod(); + auto const period = mPeriodTransitioningTo ? *mPeriodTransitioningTo : mTracker.currentPeriod(); static constexpr int allowancePercent = 10; static constexpr std::ratio<allowancePercent, 100> allowancePercentRatio; auto const allowance = period * allowancePercentRatio.num / allowancePercentRatio.den; @@ -299,28 +167,25 @@ bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_ return std::abs(distance - period) < allowance; } -bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, - bool* periodFlushed) { +bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, + bool* periodFlushed) { assert(periodFlushed); - std::lock_guard<std::mutex> lk(mMutex); + std::lock_guard lock(mMutex); if (periodConfirmed(timestamp, hwcVsyncPeriod)) { ATRACE_NAME("VSR: period confirmed"); if (mPeriodTransitioningTo) { - mTracker->setPeriod(*mPeriodTransitioningTo); - for (auto& entry : mCallbacks) { - entry.second->setPeriod(*mPeriodTransitioningTo); - } + mTracker.setPeriod(*mPeriodTransitioningTo); *periodFlushed = true; } if (mLastHwVsync) { - mTracker->addVsyncTimestamp(*mLastHwVsync); + mTracker.addVsyncTimestamp(*mLastHwVsync); } - mTracker->addVsyncTimestamp(timestamp); + mTracker.addVsyncTimestamp(timestamp); endPeriodTransition(); - mMoreSamplesNeeded = mTracker->needsMoreSamples(); + mMoreSamplesNeeded = mTracker.needsMoreSamples(); } else if (mPeriodConfirmationInProgress) { ATRACE_NAME("VSR: still confirming period"); mLastHwVsync = timestamp; @@ -329,8 +194,8 @@ bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwc } else { ATRACE_NAME("VSR: adding sample"); *periodFlushed = false; - mTracker->addVsyncTimestamp(timestamp); - mMoreSamplesNeeded = mTracker->needsMoreSamples(); + mTracker.addVsyncTimestamp(timestamp); + mMoreSamplesNeeded = mTracker.needsMoreSamples(); } if (!mMoreSamplesNeeded) { @@ -339,51 +204,8 @@ bool VSyncReactor::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwc return mMoreSamplesNeeded; } -status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase, - DispSync::Callback* callback, - nsecs_t /* lastCallbackTime */) { - std::lock_guard<std::mutex> lk(mMutex); - auto it = mCallbacks.find(callback); - if (it == mCallbacks.end()) { - // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f. - static auto constexpr maxListeners = 4; - if (mCallbacks.size() >= maxListeners) { - ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name, - maxListeners, mCallbacks.size()); - return NO_MEMORY; - } - - auto const period = mTracker->currentPeriod(); - auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period, - phase, mClock->now()); - it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first; - } - - it->second->start(phase); - return NO_ERROR; -} - -status_t VSyncReactor::removeEventListener(DispSync::Callback* callback, - nsecs_t* /* outLastCallback */) { - std::lock_guard<std::mutex> lk(mMutex); - auto const it = mCallbacks.find(callback); - LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback); - - it->second->stop(); - return NO_ERROR; -} - -status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) { - std::lock_guard<std::mutex> lk(mMutex); - auto const it = mCallbacks.find(callback); - LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback); - - it->second->start(phase); - return NO_ERROR; -} - void VSyncReactor::dump(std::string& result) const { - std::lock_guard<std::mutex> lk(mMutex); + std::lock_guard lock(mMutex); StringAppendF(&result, "VsyncReactor in use\n"); StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size()); StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n", @@ -403,17 +225,8 @@ void VSyncReactor::dump(std::string& result) const { StringAppendF(&result, "No Last HW vsync\n"); } - StringAppendF(&result, "CallbackRepeaters:\n"); - for (const auto& [callback, repeater] : mCallbacks) { - repeater->dump(result); - } - StringAppendF(&result, "VSyncTracker:\n"); - mTracker->dump(result); - StringAppendF(&result, "VSyncDispatch:\n"); - mDispatch->dump(result); + mTracker.dump(result); } -void VSyncReactor::reset() {} - } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h index 265d89c4b4..449d4c3bee 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.h +++ b/services/surfaceflinger/Scheduler/VSyncReactor.h @@ -22,74 +22,53 @@ #include <mutex> #include <unordered_map> #include <vector> -#include "DispSync.h" #include "TimeKeeper.h" +#include "VsyncController.h" namespace android::scheduler { class Clock; class VSyncDispatch; class VSyncTracker; -class CallbackRepeater; -class PredictedVsyncTracer; // TODO (b/145217110): consider renaming. -class VSyncReactor : public android::DispSync { +class VSyncReactor : public VsyncController { public: - VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch, - std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit, + VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, size_t pendingFenceLimit, bool supportKernelIdleTimer); ~VSyncReactor(); - bool addPresentFence(const std::shared_ptr<FenceTime>& fence) final; - void setIgnorePresentFences(bool ignoration) final; + bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final; + void setIgnorePresentFences(bool ignore) final; - nsecs_t computeNextRefresh(int periodOffset, nsecs_t now) const final; - nsecs_t expectedPresentTime(nsecs_t now) final; + void startPeriodTransition(nsecs_t period) final; - void setPeriod(nsecs_t period) final; - nsecs_t getPeriod() final; - - // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible. - void beginResync() final; - bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, - bool* periodFlushed) final; - void endResync() final; - - status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback, - nsecs_t lastCallbackTime) final; - status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) final; - status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) final; + bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, + bool* periodFlushed) final; void dump(std::string& result) const final; - void reset() final; private: - void setIgnorePresentFencesInternal(bool ignoration) REQUIRES(mMutex); + void setIgnorePresentFencesInternal(bool ignore) REQUIRES(mMutex); void updateIgnorePresentFencesInternal() REQUIRES(mMutex); - void startPeriodTransition(nsecs_t newPeriod) REQUIRES(mMutex); + void startPeriodTransitionInternal(nsecs_t newPeriod) REQUIRES(mMutex); void endPeriodTransition() REQUIRES(mMutex); bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod) REQUIRES(mMutex); std::unique_ptr<Clock> const mClock; - std::unique_ptr<VSyncTracker> const mTracker; - std::unique_ptr<VSyncDispatch> const mDispatch; + VSyncTracker& mTracker; size_t const mPendingLimit; mutable std::mutex mMutex; bool mInternalIgnoreFences GUARDED_BY(mMutex) = false; bool mExternalIgnoreFences GUARDED_BY(mMutex) = false; - std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex); + std::vector<std::shared_ptr<android::FenceTime>> mUnfiredFences GUARDED_BY(mMutex); bool mMoreSamplesNeeded GUARDED_BY(mMutex) = false; bool mPeriodConfirmationInProgress GUARDED_BY(mMutex) = false; std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex); std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex); - std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks - GUARDED_BY(mMutex); - - const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer; const bool mSupportKernelIdleTimer = false; }; diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 107c5400fe..2cd9b3df18 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -68,6 +68,14 @@ public: virtual bool needsMoreSamples() const = 0; + /* + * Checks if a vsync timestamp is in phase for a given divider. + * + * \param [in] timePoint A vsync timestamp + * \param [in] divider The divider to check for + */ + virtual bool isVSyncInPhase(nsecs_t timePoint, int divider) const = 0; + virtual void dump(std::string& result) const = 0; protected: diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp new file mode 100644 index 0000000000..aac2569044 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp @@ -0,0 +1,395 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "VsyncConfiguration.h" + +#include <cutils/properties.h> + +#include <optional> + +#include "SurfaceFlingerProperties.h" + +namespace { + +std::optional<nsecs_t> getProperty(const char* name) { + char value[PROPERTY_VALUE_MAX]; + property_get(name, value, "-1"); + if (const int i = atoi(value); i != -1) return i; + return std::nullopt; +} + +bool fpsEqualsWithMargin(float fpsA, float fpsB) { + static constexpr float MARGIN = 0.01f; + return std::abs(fpsA - fpsB) <= MARGIN; +} + +std::vector<float> getRefreshRatesFromConfigs( + const android::scheduler::RefreshRateConfigs& refreshRateConfigs) { + const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates(); + std::vector<float> refreshRates; + refreshRates.reserve(allRefreshRates.size()); + + for (const auto& [ignored, refreshRate] : allRefreshRates) { + refreshRates.emplace_back(refreshRate->getFps()); + } + + return refreshRates; +} + +} // namespace + +namespace android::scheduler::impl { + +VsyncConfiguration::VsyncConfiguration(float currentFps) : mRefreshRateFps(currentFps) {} + +PhaseOffsets::VsyncConfigSet VsyncConfiguration::getConfigsForRefreshRate(float fps) const { + const auto iter = std::find_if(mOffsets.begin(), mOffsets.end(), + [&fps](const std::pair<float, VsyncConfigSet>& candidateFps) { + return fpsEqualsWithMargin(fps, candidateFps.first); + }); + + if (iter != mOffsets.end()) { + return iter->second; + } + + // Unknown refresh rate. This might happen if we get a hotplug event for an external display. + // In this case just construct the offset. + ALOGW("Can't find offset for %.2f fps", fps); + return constructOffsets(static_cast<nsecs_t>(1e9f / fps)); +} + +void VsyncConfiguration::initializeOffsets(const std::vector<float>& refreshRates) { + for (const auto fps : refreshRates) { + mOffsets.emplace(fps, constructOffsets(static_cast<nsecs_t>(1e9f / fps))); + } +} + +void VsyncConfiguration::dump(std::string& result) const { + const auto [early, earlyGpu, late] = getCurrentConfigs(); + using base::StringAppendF; + StringAppendF(&result, + " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 + " ns\n" + " app duration: %9lld ns\t SF duration: %9lld ns\n" + " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 + " ns\n" + " early app duration: %9lld ns\t early SF duration: %9lld ns\n" + " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 + " ns\n" + " GL early app duration: %9lld ns\tGL early SF duration: %9lld ns\n", + late.appOffset, late.sfOffset, + + late.appWorkDuration.count(), late.sfWorkDuration.count(), + + early.appOffset, early.sfOffset, + + early.appWorkDuration.count(), early.sfWorkDuration.count(), + + earlyGpu.appOffset, earlyGpu.sfOffset, + + earlyGpu.appWorkDuration.count(), earlyGpu.sfWorkDuration.count()); +} + +PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs) + : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs), + refreshRateConfigs.getCurrentRefreshRate().getFps(), + sysprop::vsync_event_phase_offset_ns(1000000), + sysprop::vsync_sf_event_phase_offset_ns(1000000), + getProperty("debug.sf.early_phase_offset_ns"), + getProperty("debug.sf.early_gl_phase_offset_ns"), + getProperty("debug.sf.early_app_phase_offset_ns"), + getProperty("debug.sf.early_gl_app_phase_offset_ns"), + getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000), + getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000), + getProperty("debug.sf.high_fps_early_phase_offset_ns"), + getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"), + getProperty("debug.sf.high_fps_early_app_phase_offset_ns"), + getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"), + // Below defines the threshold when an offset is considered to be negative, + // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset + // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For + // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW + // vsync. + getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns") + .value_or(std::numeric_limits<nsecs_t>::max())) {} + +PhaseOffsets::PhaseOffsets( + const std::vector<float>& refreshRates, float currentFps, nsecs_t vsyncPhaseOffsetNs, + nsecs_t sfVSyncPhaseOffsetNs, std::optional<nsecs_t> earlySfOffsetNs, + std::optional<nsecs_t> earlyGpuSfOffsetNs, std::optional<nsecs_t> earlyAppOffsetNs, + std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs, + nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs, + std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs, + std::optional<nsecs_t> highFpsEarlyAppOffsetNs, + std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync) + : VsyncConfiguration(currentFps), + mVSyncPhaseOffsetNs(vsyncPhaseOffsetNs), + mSfVSyncPhaseOffsetNs(sfVSyncPhaseOffsetNs), + mEarlySfOffsetNs(earlySfOffsetNs), + mEarlyGpuSfOffsetNs(earlyGpuSfOffsetNs), + mEarlyAppOffsetNs(earlyAppOffsetNs), + mEarlyGpuAppOffsetNs(earlyGpuAppOffsetNs), + mHighFpsVSyncPhaseOffsetNs(highFpsVsyncPhaseOffsetNs), + mHighFpsSfVSyncPhaseOffsetNs(highFpsSfVSyncPhaseOffsetNs), + mHighFpsEarlySfOffsetNs(highFpsEarlySfOffsetNs), + mHighFpsEarlyGpuSfOffsetNs(highFpsEarlyGpuSfOffsetNs), + mHighFpsEarlyAppOffsetNs(highFpsEarlyAppOffsetNs), + mHighFpsEarlyGpuAppOffsetNs(highFpsEarlyGpuAppOffsetNs), + mThresholdForNextVsync(thresholdForNextVsync) { + initializeOffsets(refreshRates); +} + +PhaseOffsets::VsyncConfigSet PhaseOffsets::constructOffsets(nsecs_t vsyncDuration) const { + if (vsyncDuration < std::chrono::nanoseconds(15ms).count()) { + return getHighFpsOffsets(vsyncDuration); + } else { + return getDefaultOffsets(vsyncDuration); + } +} + +namespace { +std::chrono::nanoseconds sfOffsetToDuration(nsecs_t sfOffset, nsecs_t vsyncDuration) { + return std::chrono::nanoseconds(vsyncDuration - sfOffset); +} + +std::chrono::nanoseconds appOffsetToDuration(nsecs_t appOffset, nsecs_t sfOffset, + nsecs_t vsyncDuration) { + auto duration = vsyncDuration + (sfOffset - appOffset); + if (duration < vsyncDuration) { + duration += vsyncDuration; + } + + return std::chrono::nanoseconds(duration); +} +} // namespace + +PhaseOffsets::VsyncConfigSet PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const { + const auto earlySfOffset = + mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync + + ? mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) + : mEarlySfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration; + const auto earlyAppOffset = mEarlyAppOffsetNs.value_or(mVSyncPhaseOffsetNs); + const auto earlyGpuSfOffset = + mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) < mThresholdForNextVsync + + ? mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) + : mEarlyGpuSfOffsetNs.value_or(mSfVSyncPhaseOffsetNs) - vsyncDuration; + const auto earlyGpuAppOffset = mEarlyGpuAppOffsetNs.value_or(mVSyncPhaseOffsetNs); + const auto lateSfOffset = mSfVSyncPhaseOffsetNs < mThresholdForNextVsync + ? mSfVSyncPhaseOffsetNs + : mSfVSyncPhaseOffsetNs - vsyncDuration; + const auto lateAppOffset = mVSyncPhaseOffsetNs; + + return { + .early = {.sfOffset = earlySfOffset, + .appOffset = earlyAppOffset, + .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration), + .appWorkDuration = + appOffsetToDuration(earlyAppOffset, earlySfOffset, vsyncDuration)}, + .earlyGpu = {.sfOffset = earlyGpuSfOffset, + .appOffset = earlyGpuAppOffset, + .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration), + .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset, earlyGpuSfOffset, + vsyncDuration)}, + .late = {.sfOffset = lateSfOffset, + .appOffset = lateAppOffset, + .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration), + .appWorkDuration = + appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration)}, + }; +} + +PhaseOffsets::VsyncConfigSet PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const { + const auto earlySfOffset = + mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync + ? mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) + : mHighFpsEarlySfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration; + const auto earlyAppOffset = mHighFpsEarlyAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs); + const auto earlyGpuSfOffset = mHighFpsEarlyGpuSfOffsetNs.value_or( + mHighFpsSfVSyncPhaseOffsetNs) < mThresholdForNextVsync + + ? mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) + : mHighFpsEarlyGpuSfOffsetNs.value_or(mHighFpsSfVSyncPhaseOffsetNs) - vsyncDuration; + const auto earlyGpuAppOffset = mHighFpsEarlyGpuAppOffsetNs.value_or(mHighFpsVSyncPhaseOffsetNs); + const auto lateSfOffset = mHighFpsSfVSyncPhaseOffsetNs < mThresholdForNextVsync + ? mHighFpsSfVSyncPhaseOffsetNs + : mHighFpsSfVSyncPhaseOffsetNs - vsyncDuration; + const auto lateAppOffset = mHighFpsVSyncPhaseOffsetNs; + + return { + .early = + { + .sfOffset = earlySfOffset, + .appOffset = earlyAppOffset, + .sfWorkDuration = sfOffsetToDuration(earlySfOffset, vsyncDuration), + .appWorkDuration = appOffsetToDuration(earlyAppOffset, earlySfOffset, + vsyncDuration), + }, + .earlyGpu = + { + .sfOffset = earlyGpuSfOffset, + .appOffset = earlyGpuAppOffset, + .sfWorkDuration = sfOffsetToDuration(earlyGpuSfOffset, vsyncDuration), + .appWorkDuration = appOffsetToDuration(earlyGpuAppOffset, + earlyGpuSfOffset, vsyncDuration), + }, + .late = + { + .sfOffset = lateSfOffset, + .appOffset = lateAppOffset, + .sfWorkDuration = sfOffsetToDuration(lateSfOffset, vsyncDuration), + .appWorkDuration = + appOffsetToDuration(lateAppOffset, lateSfOffset, vsyncDuration), + }, + }; +} + +static void validateSysprops() { + const auto validatePropertyBool = [](const char* prop) { + LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop); + }; + + validatePropertyBool("debug.sf.use_phase_offsets_as_durations"); + + LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1, + "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting " + "duration"); + + LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1, + "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting " + "duration"); + + const auto validateProperty = [](const char* prop) { + LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(), + "%s is set to %" PRId64 " but expecting duration", prop, + getProperty(prop).value_or(-1)); + }; + + validateProperty("debug.sf.early_phase_offset_ns"); + validateProperty("debug.sf.early_gl_phase_offset_ns"); + validateProperty("debug.sf.early_app_phase_offset_ns"); + validateProperty("debug.sf.early_gl_app_phase_offset_ns"); + validateProperty("debug.sf.high_fps_late_app_phase_offset_ns"); + validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns"); + validateProperty("debug.sf.high_fps_early_phase_offset_ns"); + validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns"); + validateProperty("debug.sf.high_fps_early_app_phase_offset_ns"); + validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"); +} + +namespace { +nsecs_t sfDurationToOffset(std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) { + return vsyncDuration - sfDuration.count() % vsyncDuration; +} + +nsecs_t appDurationToOffset(std::chrono::nanoseconds appDuration, + std::chrono::nanoseconds sfDuration, nsecs_t vsyncDuration) { + return vsyncDuration - (appDuration + sfDuration).count() % vsyncDuration; +} +} // namespace + +WorkDuration::VsyncConfigSet WorkDuration::constructOffsets(nsecs_t vsyncDuration) const { + const auto sfDurationFixup = [vsyncDuration](nsecs_t duration) { + return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) - 1ms + : std::chrono::nanoseconds(duration); + }; + + const auto appDurationFixup = [vsyncDuration](nsecs_t duration) { + return duration == -1 ? std::chrono::nanoseconds(vsyncDuration) + : std::chrono::nanoseconds(duration); + }; + + const auto sfEarlyDuration = sfDurationFixup(mSfEarlyDuration); + const auto appEarlyDuration = appDurationFixup(mAppEarlyDuration); + const auto sfEarlyGpuDuration = sfDurationFixup(mSfEarlyGpuDuration); + const auto appEarlyGpuDuration = appDurationFixup(mAppEarlyGpuDuration); + const auto sfDuration = sfDurationFixup(mSfDuration); + const auto appDuration = appDurationFixup(mAppDuration); + + return { + .early = + { + + .sfOffset = sfEarlyDuration.count() < vsyncDuration + ? sfDurationToOffset(sfEarlyDuration, vsyncDuration) + : sfDurationToOffset(sfEarlyDuration, vsyncDuration) - + vsyncDuration, + + .appOffset = appDurationToOffset(appEarlyDuration, sfEarlyDuration, + vsyncDuration), + + .sfWorkDuration = sfEarlyDuration, + .appWorkDuration = appEarlyDuration, + }, + .earlyGpu = + { + + .sfOffset = sfEarlyGpuDuration.count() < vsyncDuration + + ? sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration) + : sfDurationToOffset(sfEarlyGpuDuration, vsyncDuration) - + vsyncDuration, + + .appOffset = appDurationToOffset(appEarlyGpuDuration, + sfEarlyGpuDuration, vsyncDuration), + .sfWorkDuration = sfEarlyGpuDuration, + .appWorkDuration = appEarlyGpuDuration, + }, + .late = + { + + .sfOffset = sfDuration.count() < vsyncDuration + + ? sfDurationToOffset(sfDuration, vsyncDuration) + : sfDurationToOffset(sfDuration, vsyncDuration) - vsyncDuration, + + .appOffset = + appDurationToOffset(appDuration, sfDuration, vsyncDuration), + + .sfWorkDuration = sfDuration, + .appWorkDuration = appDuration, + }, + }; +} + +WorkDuration::WorkDuration(const scheduler::RefreshRateConfigs& refreshRateConfigs) + : WorkDuration(getRefreshRatesFromConfigs(refreshRateConfigs), + refreshRateConfigs.getCurrentRefreshRate().getFps(), + getProperty("debug.sf.late.sf.duration").value_or(-1), + getProperty("debug.sf.late.app.duration").value_or(-1), + getProperty("debug.sf.early.sf.duration").value_or(mSfDuration), + getProperty("debug.sf.early.app.duration").value_or(mAppDuration), + getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration), + getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) { + validateSysprops(); +} + +WorkDuration::WorkDuration(const std::vector<float>& refreshRates, float currentFps, + nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration, + nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration, + nsecs_t appEarlyGpuDuration) + : VsyncConfiguration(currentFps), + mSfDuration(sfDuration), + mAppDuration(appDuration), + mSfEarlyDuration(sfEarlyDuration), + mAppEarlyDuration(appEarlyDuration), + mSfEarlyGpuDuration(sfEarlyGpuDuration), + mAppEarlyGpuDuration(appEarlyGpuDuration) { + initializeOffsets(refreshRates); +} + +} // namespace android::scheduler::impl diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h new file mode 100644 index 0000000000..c27a25d738 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h @@ -0,0 +1,151 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <unordered_map> + +#include "RefreshRateConfigs.h" +#include "VsyncModulator.h" + +namespace android::scheduler { + +/* + * This class encapsulates vsync configurations for different refresh rates. Depending + * on what refresh rate we are using, and wheter we are composing in GL, + * different offsets will help us with latency. This class keeps track of + * which mode the device is on, and returns approprate offsets when needed. + */ +class VsyncConfiguration { +public: + using VsyncConfigSet = VsyncModulator::VsyncConfigSet; + + virtual ~VsyncConfiguration() = default; + virtual VsyncConfigSet getCurrentConfigs() const = 0; + virtual VsyncConfigSet getConfigsForRefreshRate(float fps) const = 0; + + virtual void setRefreshRateFps(float fps) = 0; + + virtual void dump(std::string& result) const = 0; +}; + +namespace impl { + +/* + * This is a common implementation for both phase offsets and durations. + * PhaseOffsets and WorkDuration derive from this class and implement the + * constructOffsets method + */ +class VsyncConfiguration : public scheduler::VsyncConfiguration { +public: + explicit VsyncConfiguration(float currentFps); + + // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate. + VsyncConfigSet getConfigsForRefreshRate(float fps) const override; + + // Returns early, early GL, and late offsets for Apps and SF. + VsyncConfigSet getCurrentConfigs() const override { + return getConfigsForRefreshRate(mRefreshRateFps); + } + + // This function should be called when the device is switching between different + // refresh rates, to properly update the offsets. + void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; } + + // Returns current offsets in human friendly format. + void dump(std::string& result) const override; + +protected: + void initializeOffsets(const std::vector<float>& refreshRates); + virtual VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const = 0; + + std::unordered_map<float, VsyncConfigSet> mOffsets; + std::atomic<float> mRefreshRateFps; +}; + +/* + * This is the old implementation of phase offsets and considered as deprecated. + * WorkDuration is the new implementation. + */ +class PhaseOffsets : public VsyncConfiguration { +public: + explicit PhaseOffsets(const scheduler::RefreshRateConfigs&); + +protected: + // Used for unit tests + PhaseOffsets(const std::vector<float>& refreshRates, float currentFps, + nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs, + std::optional<nsecs_t> earlySfOffsetNs, std::optional<nsecs_t> earlyGpuSfOffsetNs, + std::optional<nsecs_t> earlyAppOffsetNs, + std::optional<nsecs_t> earlyGpuAppOffsetNs, nsecs_t highFpsVsyncPhaseOffsetNs, + nsecs_t highFpsSfVSyncPhaseOffsetNs, std::optional<nsecs_t> highFpsEarlySfOffsetNs, + std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs, + std::optional<nsecs_t> highFpsEarlyAppOffsetNs, + std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, nsecs_t thresholdForNextVsync); + +private: + VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override; + + VsyncConfigSet getDefaultOffsets(nsecs_t vsyncPeriod) const; + VsyncConfigSet getHighFpsOffsets(nsecs_t vsyncPeriod) const; + + const nsecs_t mVSyncPhaseOffsetNs; + const nsecs_t mSfVSyncPhaseOffsetNs; + const std::optional<nsecs_t> mEarlySfOffsetNs; + const std::optional<nsecs_t> mEarlyGpuSfOffsetNs; + const std::optional<nsecs_t> mEarlyAppOffsetNs; + const std::optional<nsecs_t> mEarlyGpuAppOffsetNs; + + const nsecs_t mHighFpsVSyncPhaseOffsetNs; + const nsecs_t mHighFpsSfVSyncPhaseOffsetNs; + const std::optional<nsecs_t> mHighFpsEarlySfOffsetNs; + const std::optional<nsecs_t> mHighFpsEarlyGpuSfOffsetNs; + const std::optional<nsecs_t> mHighFpsEarlyAppOffsetNs; + const std::optional<nsecs_t> mHighFpsEarlyGpuAppOffsetNs; + + const nsecs_t mThresholdForNextVsync; +}; + +/* + * Class that encapsulates the phase offsets for SurfaceFlinger and App. + * The offsets are calculated from durations for each one of the (late, early, earlyGpu) + * offset types. + */ +class WorkDuration : public VsyncConfiguration { +public: + explicit WorkDuration(const scheduler::RefreshRateConfigs&); + +protected: + // Used for unit tests + WorkDuration(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration, + nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, + nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration); + +private: + VsyncConfiguration::VsyncConfigSet constructOffsets(nsecs_t vsyncDuration) const override; + + const nsecs_t mSfDuration; + const nsecs_t mAppDuration; + + const nsecs_t mSfEarlyDuration; + const nsecs_t mAppEarlyDuration; + + const nsecs_t mSfEarlyGpuDuration; + const nsecs_t mAppEarlyGpuDuration; +}; + +} // namespace impl +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h new file mode 100644 index 0000000000..0f0df222f4 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VsyncController.h @@ -0,0 +1,85 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <cstddef> + +#include <utils/Mutex.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> + +#include <ui/FenceTime.h> + +#include <memory> + +namespace android::scheduler { + +class FenceTime; + +class VsyncController { +public: + virtual ~VsyncController(); + + /* + * Adds a present fence to the model. The controller will use the fence time as + * a vsync signal. + * + * \param [in] fence The present fence given from the display + * \return True if the model needs more vsync signals to make + * an accurate prediction, + * False otherwise + */ + virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0; + + /* + * Adds a hw sync timestamp to the model. The controller will use the timestamp + * time as a vsync signal. + * + * \param [in] timestamp The HW Vsync timestamp + * \param [in] hwcVsyncPeriod The Vsync period reported by composer, if available + * \param [out] periodFlushed True if the vsync period changed is completed + * \return True if the model needs more vsync signals to make + * an accurate prediction, + * False otherwise + */ + virtual bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, + bool* periodFlushed) = 0; + + /* + * Inform the controller that the period is changing and the controller needs to recalibrate + * itself. The controller will end the period transition internally. + * + * \param [in] period The period that the system is changing into. + */ + virtual void startPeriodTransition(nsecs_t period) = 0; + + /* + * Tells the tracker to stop using present fences to get a vsync signal. + * + * \param [in] ignore Whether to ignore the present fences or not + */ + virtual void setIgnorePresentFences(bool ignore) = 0; + + virtual void dump(std::string& result) const = 0; + +protected: + VsyncController() = default; + VsyncController(VsyncController const&) = delete; + VsyncController& operator=(VsyncController const&) = delete; +}; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp new file mode 100644 index 0000000000..1f821be504 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp @@ -0,0 +1,164 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#undef LOG_TAG +#define LOG_TAG "VsyncModulator" + +#include "VsyncModulator.h" + +#include <android-base/properties.h> +#include <log/log.h> +#include <utils/Trace.h> + +#include <chrono> +#include <cinttypes> +#include <mutex> + +using namespace std::chrono_literals; + +namespace android::scheduler { + +const std::chrono::nanoseconds VsyncModulator::MIN_EARLY_TRANSACTION_TIME = 1ms; + +VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now) + : mVsyncConfigSet(config), + mNow(now), + mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {} + +VsyncModulator::VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) { + std::lock_guard<std::mutex> lock(mMutex); + mVsyncConfigSet = config; + return updateVsyncConfigLocked(); +} + +VsyncModulator::VsyncConfigOpt VsyncModulator::setTransactionSchedule( + TransactionSchedule schedule) { + switch (schedule) { + case Schedule::EarlyStart: + ALOGW_IF(mExplicitEarlyWakeup, "%s: Duplicate EarlyStart", __FUNCTION__); + mExplicitEarlyWakeup = true; + break; + case Schedule::EarlyEnd: + ALOGW_IF(!mExplicitEarlyWakeup, "%s: Unexpected EarlyEnd", __FUNCTION__); + mExplicitEarlyWakeup = false; + break; + case Schedule::Early: + case Schedule::Late: + // No change to mExplicitEarlyWakeup for non-explicit states. + break; + } + + if (mTraceDetailedInfo) { + ATRACE_INT("mExplicitEarlyWakeup", mExplicitEarlyWakeup); + } + + if (!mExplicitEarlyWakeup && (schedule == Schedule::Early || schedule == Schedule::EarlyEnd)) { + mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES; + mEarlyTransactionStartTime = mNow(); + } + + // An early transaction stays an early transaction. + if (schedule == mTransactionSchedule || mTransactionSchedule == Schedule::EarlyEnd) { + return std::nullopt; + } + mTransactionSchedule = schedule; + return updateVsyncConfig(); +} + +VsyncModulator::VsyncConfigOpt VsyncModulator::onTransactionCommit() { + mLastTransactionCommitTime = mNow(); + if (mTransactionSchedule == Schedule::Late) return std::nullopt; + mTransactionSchedule = Schedule::Late; + return updateVsyncConfig(); +} + +VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeInitiated() { + if (mRefreshRateChangePending) return std::nullopt; + mRefreshRateChangePending = true; + return updateVsyncConfig(); +} + +VsyncModulator::VsyncConfigOpt VsyncModulator::onRefreshRateChangeCompleted() { + if (!mRefreshRateChangePending) return std::nullopt; + mRefreshRateChangePending = false; + return updateVsyncConfig(); +} + +VsyncModulator::VsyncConfigOpt VsyncModulator::onDisplayRefresh(bool usedGpuComposition) { + bool updateOffsetsNeeded = false; + + if (mEarlyTransactionStartTime.load() + MIN_EARLY_TRANSACTION_TIME <= + mLastTransactionCommitTime.load()) { + if (mEarlyTransactionFrames > 0) { + mEarlyTransactionFrames--; + updateOffsetsNeeded = true; + } + } + if (usedGpuComposition) { + mEarlyGpuFrames = MIN_EARLY_GPU_FRAMES; + updateOffsetsNeeded = true; + } else if (mEarlyGpuFrames > 0) { + mEarlyGpuFrames--; + updateOffsetsNeeded = true; + } + + if (!updateOffsetsNeeded) return std::nullopt; + return updateVsyncConfig(); +} + +VsyncModulator::VsyncConfig VsyncModulator::getVsyncConfig() const { + std::lock_guard<std::mutex> lock(mMutex); + return mVsyncConfig; +} + +const VsyncModulator::VsyncConfig& VsyncModulator::getNextVsyncConfig() const { + // Early offsets are used if we're in the middle of a refresh rate + // change, or if we recently begin a transaction. + if (mExplicitEarlyWakeup || mTransactionSchedule == Schedule::EarlyEnd || + mEarlyTransactionFrames > 0 || mRefreshRateChangePending) { + return mVsyncConfigSet.early; + } else if (mEarlyGpuFrames > 0) { + return mVsyncConfigSet.earlyGpu; + } else { + return mVsyncConfigSet.late; + } +} + +VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfig() { + std::lock_guard<std::mutex> lock(mMutex); + return updateVsyncConfigLocked(); +} + +VsyncModulator::VsyncConfig VsyncModulator::updateVsyncConfigLocked() { + const VsyncConfig& offsets = getNextVsyncConfig(); + mVsyncConfig = offsets; + + if (mTraceDetailedInfo) { + const bool isEarly = &offsets == &mVsyncConfigSet.early; + const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu; + const bool isLate = &offsets == &mVsyncConfigSet.late; + + ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly); + ATRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu); + ATRACE_INT("Vsync-LateOffsetsOn", isLate); + } + + return offsets; +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h new file mode 100644 index 0000000000..355a14adf8 --- /dev/null +++ b/services/surfaceflinger/Scheduler/VsyncModulator.h @@ -0,0 +1,130 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <chrono> +#include <mutex> +#include <optional> + +#include <android-base/thread_annotations.h> +#include <utils/Timers.h> + +namespace android::scheduler { + +// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets +// when a transaction is flagged EarlyStart or Early, lasting until an EarlyEnd transaction or a +// fixed number of frames, respectively. +enum class TransactionSchedule { + Late, // Default. + Early, // Deprecated. + EarlyStart, + EarlyEnd +}; + +// Modulates VSYNC phase depending on transaction schedule and refresh rate changes. +class VsyncModulator { +public: + // Number of frames to keep early offsets after an early transaction or GPU composition. + // This acts as a low-pass filter in case subsequent transactions are delayed, or if the + // composition strategy alternates on subsequent frames. + static constexpr int MIN_EARLY_TRANSACTION_FRAMES = 2; + static constexpr int MIN_EARLY_GPU_FRAMES = 2; + + // Duration to delay the MIN_EARLY_TRANSACTION_FRAMES countdown after an early transaction. + // This may keep early offsets for an extra frame, but avoids a race with transaction commit. + static const std::chrono::nanoseconds MIN_EARLY_TRANSACTION_TIME; + + // Phase offsets and work durations for SF and app deadlines from VSYNC. + struct VsyncConfig { + nsecs_t sfOffset; + nsecs_t appOffset; + std::chrono::nanoseconds sfWorkDuration; + std::chrono::nanoseconds appWorkDuration; + + bool operator==(const VsyncConfig& other) const { + return sfOffset == other.sfOffset && appOffset == other.appOffset && + sfWorkDuration == other.sfWorkDuration && + appWorkDuration == other.appWorkDuration; + } + + bool operator!=(const VsyncConfig& other) const { return !(*this == other); } + }; + + using VsyncConfigOpt = std::optional<VsyncConfig>; + + struct VsyncConfigSet { + VsyncConfig early; // Used for early transactions, and during refresh rate change. + VsyncConfig earlyGpu; // Used during GPU composition. + VsyncConfig late; // Default. + + bool operator==(const VsyncConfigSet& other) const { + return early == other.early && earlyGpu == other.earlyGpu && late == other.late; + } + + bool operator!=(const VsyncConfigSet& other) const { return !(*this == other); } + }; + + using Clock = std::chrono::steady_clock; + using TimePoint = Clock::time_point; + using Now = TimePoint (*)(); + + explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now); + + VsyncConfig getVsyncConfig() const EXCLUDES(mMutex); + + [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex); + + // Changes offsets in response to transaction flags or commit. + [[nodiscard]] VsyncConfigOpt setTransactionSchedule(TransactionSchedule); + [[nodiscard]] VsyncConfigOpt onTransactionCommit(); + + // Called when we send a refresh rate change to hardware composer, so that + // we can move into early offsets. + [[nodiscard]] VsyncConfigOpt onRefreshRateChangeInitiated(); + + // Called when we detect from VSYNC signals that the refresh rate changed. + // This way we can move out of early offsets if no longer necessary. + [[nodiscard]] VsyncConfigOpt onRefreshRateChangeCompleted(); + + [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition); + +private: + const VsyncConfig& getNextVsyncConfig() const REQUIRES(mMutex); + [[nodiscard]] VsyncConfig updateVsyncConfig() EXCLUDES(mMutex); + [[nodiscard]] VsyncConfig updateVsyncConfigLocked() REQUIRES(mMutex); + + mutable std::mutex mMutex; + VsyncConfigSet mVsyncConfigSet GUARDED_BY(mMutex); + + VsyncConfig mVsyncConfig GUARDED_BY(mMutex){mVsyncConfigSet.late}; + + using Schedule = TransactionSchedule; + std::atomic<Schedule> mTransactionSchedule = Schedule::Late; + std::atomic<bool> mExplicitEarlyWakeup = false; + + std::atomic<bool> mRefreshRateChangePending = false; + + std::atomic<int> mEarlyTransactionFrames = 0; + std::atomic<int> mEarlyGpuFrames = 0; + std::atomic<TimePoint> mEarlyTransactionStartTime = TimePoint(); + std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint(); + + const Now mNow; + const bool mTraceDetailedInfo; +}; + +} // namespace android::scheduler diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ec81bada3d..57c4d52653 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -28,8 +28,10 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> -#include <android/hardware/power/1.0/IPower.h> +#include <android/hardware/power/Boost.h> #include <android/native_window.h> +#include <android/os/BnSetInputWindowsListener.h> +#include <android/os/IInputFlinger.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/PermissionCache.h> @@ -46,11 +48,9 @@ #include <cutils/compiler.h> #include <cutils/properties.h> #include <dlfcn.h> -#include <dvr/vr_flinger.h> #include <errno.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> -#include <gui/GuiConfig.h> #include <gui/IDisplayEventConnection.h> #include <gui/IProducerListener.h> #include <gui/LayerDebugInfo.h> @@ -58,7 +58,6 @@ #include <gui/LayerState.h> #include <gui/Surface.h> #include <hidl/ServiceManagement.h> -#include <input/IInputFlinger.h> #include <layerproto/LayerProtoParser.h> #include <log/log.h> #include <private/android_filesystem_config.h> @@ -74,7 +73,6 @@ #include <ui/DisplayState.h> #include <ui/GraphicBufferAllocator.h> #include <ui/PixelFormat.h> -#include <ui/UiConfig.h> #include <utils/StopWatch.h> #include <utils/String16.h> #include <utils/String8.h> @@ -89,6 +87,7 @@ #include <functional> #include <mutex> #include <optional> +#include <type_traits> #include <unordered_map> #include "BufferLayer.h" @@ -103,24 +102,26 @@ #include "DisplayHardware/FramebufferSurface.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/VirtualDisplaySurface.h" +#include "DisplayRenderArea.h" #include "EffectLayer.h" #include "Effects/Daltonizer.h" +#include "FrameTimeline/FrameTimeline.h" #include "FrameTracer/FrameTracer.h" #include "Layer.h" +#include "LayerRenderArea.h" #include "LayerVector.h" #include "MonitoredProducer.h" #include "NativeWindowSurface.h" #include "Promise.h" #include "RefreshRateOverlay.h" #include "RegionSamplingThread.h" -#include "Scheduler/DispSync.h" #include "Scheduler/DispSyncSource.h" -#include "Scheduler/EventControlThread.h" #include "Scheduler/EventThread.h" #include "Scheduler/LayerHistory.h" #include "Scheduler/MessageQueue.h" -#include "Scheduler/PhaseOffsets.h" #include "Scheduler/Scheduler.h" +#include "Scheduler/VsyncConfiguration.h" +#include "Scheduler/VsyncController.h" #include "StartPropertySetThread.h" #include "SurfaceFlingerProperties.h" #include "SurfaceInterceptor.h" @@ -149,7 +150,7 @@ using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; using namespace android::sysprop; -using android::hardware::power::V1_0::PowerHint; +using android::hardware::power::Boost; using base::StringAppendF; using ui::ColorMode; using ui::Dataspace; @@ -254,6 +255,21 @@ private: } // namespace anonymous +struct SetInputWindowsListener : os::BnSetInputWindowsListener { + explicit SetInputWindowsListener(std::function<void()> listenerCb) : mListenerCb(listenerCb) {} + + binder::Status onSetInputWindowsFinished() override; + + std::function<void()> mListenerCb; +}; + +binder::Status SetInputWindowsListener::onSetInputWindowsFinished() { + if (mListenerCb != nullptr) { + mListenerCb(); + } + return binder::Status::ok(); +} + // --------------------------------------------------------------------------- const String16 sHardwareTest("android.permission.HARDWARE_TEST"); @@ -267,7 +283,6 @@ int64_t SurfaceFlinger::dispSyncPresentTimeOffset; bool SurfaceFlinger::useHwcForRgbToYuv; uint64_t SurfaceFlinger::maxVirtualDisplaySize; bool SurfaceFlinger::hasSyncFramework; -bool SurfaceFlinger::useVrFlinger; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; uint32_t SurfaceFlinger::maxGraphicsWidth; uint32_t SurfaceFlinger::maxGraphicsHeight; @@ -313,13 +328,16 @@ SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()) {} SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) : mFactory(factory), - mInterceptor(mFactory.createSurfaceInterceptor(this)), + mInterceptor(mFactory.createSurfaceInterceptor()), mTimeStats(std::make_shared<impl::TimeStats>()), mFrameTracer(std::make_unique<FrameTracer>()), + mFrameTimeline(std::make_unique<frametimeline::impl::FrameTimeline>(mTimeStats)), mEventQueue(mFactory.createMessageQueue()), mCompositionEngine(mFactory.createCompositionEngine()), mInternalDisplayDensity(getDensityFromProperty("ro.sf.lcd_density", true)), - mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) {} + mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)) { + mSetInputWindowsListener = new SetInputWindowsListener([&]() { setInputWindowsFinished(); }); +} SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) { ALOGI("SurfaceFlinger is starting"); @@ -332,9 +350,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI maxVirtualDisplaySize = max_virtual_display_dimension(0); - // Vr flinger is only enabled on Daydream ready devices. - useVrFlinger = use_vr_flinger(false); - maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2); maxGraphicsWidth = std::max(max_graphics_width(0), 0); @@ -342,7 +357,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI hasWideColorDisplay = has_wide_color_display(false); - useColorManagement = use_color_management(false); + // Android 12 and beyond, color management in display pipeline is turned on + // by default. + useColorManagement = use_color_management(true); mDefaultCompositionDataspace = static_cast<ui::Dataspace>(default_composition_dataspace(Dataspace::V0_SRGB)); @@ -397,10 +414,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI int debugDdms = atoi(value); ALOGI_IF(debugDdms, "DDMS debugging not supported"); - property_get("debug.sf.disable_backpressure", value, "0"); - mPropagateBackpressure = !atoi(value); - ALOGI_IF(!mPropagateBackpressure, "Disabling backpressure propagation"); - property_get("debug.sf.enable_gl_backpressure", value, "0"); mPropagateBackpressureClientComposition = atoi(value); ALOGI_IF(mPropagateBackpressureClientComposition, @@ -424,6 +437,10 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS; auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize)); mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize; + mGraphicBufferProducerListSizeLogThreshold = + std::max(static_cast<int>(0.95 * + static_cast<double>(mMaxGraphicBufferProducerListSize)), + 1); property_get("debug.sf.luma_sampling", value, "1"); mLumaSampling = atoi(value); @@ -449,6 +466,8 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false); base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false"); + + mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0); } SurfaceFlinger::~SurfaceFlinger() = default; @@ -487,6 +506,15 @@ sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() { } sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure) { + // onTransact already checks for some permissions, but adding an additional check here. + // This is to ensure that only system and graphics can request to create a secure + // display. Secure displays can show secure content so we add an additional restriction on it. + const int uid = IPCThreadState::self()->getCallingUid(); + if (secure && uid != AID_GRAPHICS && uid != AID_SYSTEM) { + ALOGE("Only privileged processes can create a secure display"); + return nullptr; + } + class DisplayToken : public BBinder { sp<SurfaceFlinger> flinger; virtual ~DisplayToken() { @@ -542,11 +570,11 @@ std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const { std::vector<PhysicalDisplayId> displayIds; displayIds.reserve(mPhysicalDisplayTokens.size()); - displayIds.push_back(internalDisplayId->value); + displayIds.push_back(*internalDisplayId); for (const auto& [id, token] : mPhysicalDisplayTokens) { if (id != *internalDisplayId) { - displayIds.push_back(id.value); + displayIds.push_back(id); } } @@ -555,7 +583,7 @@ std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const { sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const { Mutex::Autolock lock(mStateLock); - return getPhysicalDisplayTokenLocked(DisplayId{displayId}); + return getPhysicalDisplayTokenLocked(displayId); } status_t SurfaceFlinger::getColorManagement(bool* outGetColorManagement) const { @@ -601,17 +629,6 @@ void SurfaceFlinger::bootFinished() if (mWindowManager != 0) { mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); } - sp<IBinder> input(defaultServiceManager()->getService( - String16("inputflinger"))); - if (input == nullptr) { - ALOGE("Failed to link to input service"); - } else { - mInputFlinger = interface_cast<IInputFlinger>(input); - } - - if (mVrFlinger) { - mVrFlinger->OnBootFinished(); - } // stop boot animation // formerly we would just kill the process, but we now ask it to exit so it @@ -622,7 +639,15 @@ void SurfaceFlinger::bootFinished() LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - static_cast<void>(schedule([this] { + sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger"))); + + static_cast<void>(schedule([=] { + if (input == nullptr) { + ALOGE("Failed to link to input service"); + } else { + mInputFlinger = interface_cast<os::IInputFlinger>(input); + } + readPersistentProperties(); mPowerAdvisor.onBootFinished(); mBootStage = BootStage::FINISHED; @@ -688,42 +713,16 @@ void SurfaceFlinger::init() { : renderengine::RenderEngine::ContextPriority::MEDIUM) .build())); mCompositionEngine->setTimeStats(mTimeStats); - - LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay, - "Starting with vr flinger active is not currently supported."); mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName)); mCompositionEngine->getHwComposer().setConfiguration(this, getBE().mComposerSequenceId); // Process any initial hotplug and resulting display changes. processDisplayHotplugEventsLocked(); const auto display = getDefaultDisplayDeviceLocked(); LOG_ALWAYS_FATAL_IF(!display, "Missing internal display after registering composer callback."); - LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(*display->getId()), + const auto displayId = display->getPhysicalId(); + LOG_ALWAYS_FATAL_IF(!getHwComposer().isConnected(displayId), "Internal display is disconnected."); - if (useVrFlinger) { - auto vrFlingerRequestDisplayCallback = [this](bool requestDisplay) { - // This callback is called from the vr flinger dispatch thread. We - // need to call signalTransaction(), which requires holding - // mStateLock when we're not on the main thread. Acquiring - // mStateLock from the vr flinger dispatch thread might trigger a - // deadlock in surface flinger (see b/66916578), so post a message - // to be handled on the main thread instead. - static_cast<void>(schedule([=] { - ALOGI("VR request display mode: requestDisplay=%d", requestDisplay); - mVrFlingerRequestsDisplay = requestDisplay; - signalTransaction(); - })); - }; - mVrFlinger = dvr::VrFlinger::Create(getHwComposer().getComposer(), - getHwComposer() - .fromPhysicalDisplayId(*display->getId()) - .value_or(0), - vrFlingerRequestDisplayCallback); - if (!mVrFlinger) { - ALOGE("Failed to start vrflinger"); - } - } - // initialize our drawing state mDrawingState = mCurrentState; @@ -838,8 +837,9 @@ status_t SurfaceFlinger::getDisplayState(const sp<IBinder>& displayToken, ui::Di state->layerStack = display->getLayerStack(); state->orientation = display->getOrientation(); - const Rect viewport = display->getViewport(); - state->viewport = viewport.isValid() ? viewport.getSize() : display->getSize(); + const Rect layerStackRect = display->getLayerStackSpaceRect(); + state->layerStackSpaceRect = + layerStackRect.isValid() ? layerStackRect.getSize() : display->getSize(); return NO_ERROR; } @@ -872,7 +872,7 @@ status_t SurfaceFlinger::getDisplayInfo(const sp<IBinder>& displayToken, Display info->density /= ACONFIGURATION_DENSITY_MEDIUM; info->secure = display->isSecure(); - info->deviceProductInfo = getDeviceProductInfoLocked(*display); + info->deviceProductInfo = display->getDeviceProductInfo(); return NO_ERROR; } @@ -923,9 +923,10 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken, const nsecs_t period = hwConfig->getVsyncPeriod(); config.refreshRate = 1e9f / period; - const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate); - config.appVsyncOffset = offsets.late.app; - config.sfVsyncOffset = offsets.late.sf; + const auto vsyncConfigSet = + mVsyncConfiguration->getConfigsForRefreshRate(config.refreshRate); + config.appVsyncOffset = vsyncConfigSet.late.appOffset; + config.sfVsyncOffset = vsyncConfigSet.late.sfOffset; config.configGroup = hwConfig->getConfigGroup(); // This is how far in advance a buffer must be queued for @@ -935,7 +936,7 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken, // // Normally it's one full refresh period (to give SF a chance to // latch the buffer), but this can be reduced by configuring a - // DispSync offset. Any additional delays introduced by the hardware + // VsyncController offset. Any additional delays introduced by the hardware // composer or panel must be accounted for here. // // We add an additional 1ms to allow for processing time and @@ -953,7 +954,7 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* st return BAD_VALUE; } - mScheduler->getDisplayStatInfo(stats); + mScheduler->getDisplayStatInfo(stats, systemTime()); return NO_ERROR; } @@ -1011,11 +1012,10 @@ void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) { // switch. mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod()); // As we called to set period, we will call to onRefreshRateChangeCompleted once - // DispSync model is locked. - mVSyncModulator->onRefreshRateChangeInitiated(); + // VsyncController model is locked. + modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated); - mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps()); - mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets()); + updatePhaseConfiguration(refreshRate); mScheduler->setConfigChangePending(true); } @@ -1043,7 +1043,12 @@ status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mo } else { const HwcConfigIndexType config(mode); const float fps = mRefreshRateConfigs->getRefreshRateFromConfigId(config).getFps(); - const scheduler::RefreshRateConfigs::Policy policy{config, {fps, fps}}; + // Keep the old switching type. + const auto allowGroupSwitching = + mRefreshRateConfigs->getCurrentPolicy().allowGroupSwitching; + const scheduler::RefreshRateConfigs::Policy policy{config, + allowGroupSwitching, + {fps, fps}}; constexpr bool kOverridePolicy = false; return setDesiredDisplayConfigSpecsInternal(display, policy, kOverridePolicy); @@ -1074,15 +1079,15 @@ void SurfaceFlinger::setActiveConfigInternal() { if (refreshRate.getVsyncPeriod() != oldRefreshRate.getVsyncPeriod()) { mTimeStats->incrementRefreshRateSwitches(); } - mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps()); - mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets()); + updatePhaseConfiguration(refreshRate); ATRACE_INT("ActiveConfigFPS", refreshRate.getFps()); if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) { const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId) .getVsyncPeriod(); - mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, + const auto physicalId = display->getPhysicalId(); + mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId, mUpcomingActiveConfig.configId, vsyncPeriod); } } @@ -1094,9 +1099,9 @@ void SurfaceFlinger::desiredActiveConfigChangeDone() { const auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId); + mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod()); - mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps()); - mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets()); + updatePhaseConfiguration(refreshRate); mScheduler->setConfigChangePending(false); } @@ -1131,8 +1136,7 @@ void SurfaceFlinger::performSetActiveConfig() { } mUpcomingActiveConfig = *desiredActiveConfig; - const auto displayId = display->getId(); - LOG_ALWAYS_FATAL_IF(!displayId); + const auto displayId = display->getPhysicalId(); ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.getFps()); @@ -1143,7 +1147,7 @@ void SurfaceFlinger::performSetActiveConfig() { hal::VsyncPeriodChangeTimeline outTimeline; auto status = - getHwComposer().setActiveConfigWithConstraints(*displayId, + getHwComposer().setActiveConfigWithConstraints(displayId, mUpcomingActiveConfig.configId.value(), constraints, &outTimeline); if (status != NO_ERROR) { @@ -1339,30 +1343,6 @@ status_t SurfaceFlinger::getHdrCapabilities(const sp<IBinder>& displayToken, return NO_ERROR; } -std::optional<DeviceProductInfo> SurfaceFlinger::getDeviceProductInfoLocked( - const DisplayDevice& display) const { - // TODO(b/149075047): Populate DeviceProductInfo on hotplug and store it in DisplayDevice to - // avoid repetitive HAL IPC and EDID parsing. - const auto displayId = display.getId(); - LOG_FATAL_IF(!displayId); - - const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId); - LOG_FATAL_IF(!hwcDisplayId); - - uint8_t port; - DisplayIdentificationData data; - if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) { - ALOGV("%s: No identification data.", __FUNCTION__); - return {}; - } - - const auto info = parseDisplayIdentificationData(port, data); - if (!info) { - return {}; - } - return info->deviceProductInfo; -} - status_t SurfaceFlinger::getDisplayedContentSamplingAttributes(const sp<IBinder>& displayToken, ui::PixelFormat* outFormat, ui::Dataspace* outDataspace, @@ -1441,8 +1421,8 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { Mutex::Autolock lock(mStateLock); if (const auto handle = mScheduler->enableVSyncInjection(enable)) { - mEventQueue->setEventConnection( - mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle)); + mEventQueue->setEventConnection(enable ? mScheduler->getEventConnection(handle) + : nullptr); } }).wait(); @@ -1451,7 +1431,11 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { status_t SurfaceFlinger::injectVSync(nsecs_t when) { Mutex::Autolock lock(mStateLock); - return mScheduler->injectVSync(when, calculateExpectedPresentTime(when)) ? NO_ERROR : BAD_VALUE; + const auto expectedPresent = calculateExpectedPresentTime(when); + return mScheduler->injectVSync(when, /*expectedVSyncTime=*/expectedPresent, + /*deadlineTimestamp=*/expectedPresent) + ? NO_ERROR + : BAD_VALUE; } status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) { @@ -1530,10 +1514,10 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, f .get(); } -status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) { - PowerHint powerHint = static_cast<PowerHint>(hintId); +status_t SurfaceFlinger::notifyPowerBoost(int32_t boostId) { + Boost powerBoost = static_cast<Boost>(boostId); - if (powerHint == PowerHint::INTERACTION) { + if (powerBoost == Boost::INTERACTION) { mScheduler->notifyTouchEvent(); } @@ -1599,7 +1583,7 @@ void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDis bool periodFlushed = false; mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed); if (periodFlushed) { - mVSyncModulator->onRefreshRateChangeCompleted(); + modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted); } } @@ -1679,125 +1663,26 @@ void SurfaceFlinger::onRefreshReceived(int sequenceId, hal::HWDisplayId /*hwcDis repaintEverythingForHWC(); } -void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) { +void SurfaceFlinger::setVsyncEnabled(bool enabled) { ATRACE_CALL(); - // Enable / Disable HWVsync from the main thread to avoid race conditions with - // display power state. - static_cast<void>(schedule([=]() MAIN_THREAD { setPrimaryVsyncEnabledInternal(enabled); })); -} - -void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) { - ATRACE_CALL(); - - mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE; + // On main thread to avoid race conditions with display power state. + static_cast<void>(schedule([=]() MAIN_THREAD { + mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE; - if (const auto displayId = getInternalDisplayIdLocked()) { - sp<DisplayDevice> display = getDefaultDisplayDeviceLocked(); - if (display && display->isPoweredOn()) { - getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState); + if (const auto display = getDefaultDisplayDeviceLocked(); + display && display->isPoweredOn()) { + getHwComposer().setVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState); } - } -} - -void SurfaceFlinger::resetDisplayState() { - mScheduler->disableHardwareVsync(true); - // Clear the drawing state so that the logic inside of - // handleTransactionLocked will fire. It will determine the delta between - // mCurrentState and mDrawingState and re-apply all changes when we make the - // transition. - mDrawingState.displays.clear(); - mDisplays.clear(); -} - -void SurfaceFlinger::updateVrFlinger() { - ATRACE_CALL(); - if (!mVrFlinger) - return; - bool vrFlingerRequestsDisplay = mVrFlingerRequestsDisplay; - if (vrFlingerRequestsDisplay == getHwComposer().isUsingVrComposer()) { - return; - } - - if (vrFlingerRequestsDisplay && !getHwComposer().getComposer()->isRemote()) { - ALOGE("Vr flinger is only supported for remote hardware composer" - " service connections. Ignoring request to transition to vr" - " flinger."); - mVrFlingerRequestsDisplay = false; - return; - } - - Mutex::Autolock _l(mStateLock); - - sp<DisplayDevice> display = getDefaultDisplayDeviceLocked(); - LOG_ALWAYS_FATAL_IF(!display); - - const hal::PowerMode currentDisplayPowerMode = display->getPowerMode(); - - // Clear out all the output layers from the composition engine for all - // displays before destroying the hardware composer interface. This ensures - // any HWC layers are destroyed through that interface before it becomes - // invalid. - for (const auto& [token, displayDevice] : mDisplays) { - displayDevice->getCompositionDisplay()->clearOutputLayers(); - } - - // This DisplayDevice will no longer be relevant once resetDisplayState() is - // called below. Clear the reference now so we don't accidentally use it - // later. - display.clear(); - - if (!vrFlingerRequestsDisplay) { - mVrFlinger->SeizeDisplayOwnership(); - } - - resetDisplayState(); - // Delete the current instance before creating the new one - mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); - mCompositionEngine->setHwComposer(getFactory().createHWComposer( - vrFlingerRequestsDisplay ? "vr" : getBE().mHwcServiceName)); - mCompositionEngine->getHwComposer().setConfiguration(this, ++getBE().mComposerSequenceId); - - LOG_ALWAYS_FATAL_IF(!getHwComposer().getComposer()->isRemote(), - "Switched to non-remote hardware composer"); - - if (vrFlingerRequestsDisplay) { - mVrFlinger->GrantDisplayOwnership(); - } - - mVisibleRegionsDirty = true; - invalidateHwcGeometry(); - - // Re-enable default display. - display = getDefaultDisplayDeviceLocked(); - LOG_ALWAYS_FATAL_IF(!display); - setPowerModeInternal(display, currentDisplayPowerMode); - - // Reset the timing values to account for the period of the swapped in HWC - const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); - mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod); - - // The present fences returned from vr_hwc are not an accurate - // representation of vsync times. - mScheduler->setIgnorePresentFences(getHwComposer().isUsingVrComposer() || !hasSyncFramework); - - // Use phase of 0 since phase is not known. - // Use latency of 0, which will snap to the ideal latency. - DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod}; - setCompositorTimingSnapped(stats, 0); - - mScheduler->resyncToHardwareVsync(false, vsyncPeriod); - - mRepaintEverything = true; - setTransactionFlags(eDisplayTransactionNeeded); + })); } sp<Fence> SurfaceFlinger::previousFrameFence() { // We are storing the last 2 present fences. If sf's phase offset is to be // woken up before the actual vsync but targeting the next vsync, we need to check // fence N-2 - return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0] - : mPreviousPresentFences[1]; + return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? mPreviousPresentFences[0] + : mPreviousPresentFences[1]; } bool SurfaceFlinger::previousFramePending(int graceTimeMs) { @@ -1826,17 +1711,17 @@ nsecs_t SurfaceFlinger::previousFramePresentTime() { nsecs_t SurfaceFlinger::calculateExpectedPresentTime(nsecs_t now) const { DisplayStatInfo stats; - mScheduler->getDisplayStatInfo(&stats); - const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(now); + mScheduler->getDisplayStatInfo(&stats, now); // Inflate the expected present time if we're targetting the next vsync. - return mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod; + return mVsyncModulator->getVsyncConfig().sfOffset > 0 ? stats.vsyncTime + : stats.vsyncTime + stats.vsyncPeriod; } -void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) { +void SurfaceFlinger::onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime) { ATRACE_CALL(); switch (what) { case MessageQueue::INVALIDATE: { - onMessageInvalidate(expectedVSyncTime); + onMessageInvalidate(vsyncId, expectedVSyncTime); break; } case MessageQueue::REFRESH: { @@ -1846,7 +1731,7 @@ void SurfaceFlinger::onMessageReceived(int32_t what, nsecs_t expectedVSyncTime) } } -void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) { +void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime) { ATRACE_CALL(); const nsecs_t frameStart = systemTime(); @@ -1860,10 +1745,7 @@ void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) { // for the present fence to fire instead of just giving up on this frame to handle cases // where present fence is just about to get signaled. const int graceTimeForPresentFenceMs = - (mPropagateBackpressure && - (mPropagateBackpressureClientComposition || !mHadClientComposition)) - ? 1 - : 0; + (mPropagateBackpressureClientComposition || !mHadClientComposition) ? 1 : 0; // Pending frames may trigger backpressure propagation. const TracedOrdinal<bool> framePending = {"PrevFramePending", @@ -1878,7 +1760,7 @@ void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) { // smaller than a typical frame duration, but should not be so small // that it reports reasonable drift as a missed frame. DisplayStatInfo stats; - mScheduler->getDisplayStatInfo(&stats); + mScheduler->getDisplayStatInfo(&stats, systemTime()); const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2; const nsecs_t previousPresentTime = previousFramePresentTime(); const TracedOrdinal<bool> frameMissed = {"PrevFrameMissed", @@ -1922,7 +1804,7 @@ void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) { ON_MAIN_THREAD(setActiveConfigInternal()); } - if (framePending && mPropagateBackpressure) { + if (framePending) { if ((hwcFrameMissed && !gpuFrameMissed) || mPropagateBackpressureClientComposition) { signalLayerUpdate(); return; @@ -1960,26 +1842,31 @@ void SurfaceFlinger::onMessageInvalidate(nsecs_t expectedVSyncTime) { } } - // Now that we're going to make it to the handleMessageTransaction() - // call below it's safe to call updateVrFlinger(), which will - // potentially trigger a display handoff. - updateVrFlinger(); - if (mTracingEnabledChanged) { mTracingEnabled = mTracing.isEnabled(); mTracingEnabledChanged = false; } + if (mRefreshRateOverlaySpinner) { + if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) { + mRefreshRateOverlay->onInvalidate(); + } + } + bool refreshNeeded; { - ConditionalLockGuard<std::mutex> lock(mTracingLock, mTracingEnabled); + mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) || + mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) || + mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS); + const bool tracePreComposition = mTracingEnabled && !mTracePostComposition; + ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition); + + mFrameTimeline->setSfWakeUp(vsyncId, frameStart); refreshNeeded = handleMessageTransaction(); refreshNeeded |= handleMessageInvalidate(); - if (mTracingEnabled) { - mAddCompositionStateToTrace = - mTracing.flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION); - if (mVisibleRegionsDirty && !mAddCompositionStateToTrace) { + if (tracePreComposition) { + if (mVisibleRegionsDirty) { mTracing.notifyLocked("visibleRegionsDirty"); } } @@ -2056,7 +1943,7 @@ void SurfaceFlinger::onMessageRefresh() { refreshArgs.layers.push_back(layerFE); }); refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); - for (sp<Layer> layer : mLayersWithQueuedFrames) { + for (auto layer : mLayersWithQueuedFrames) { if (auto layerFE = layer->getCompositionEngineLayerFE()) refreshArgs.layersWithQueuedFrames.push_back(layerFE); } @@ -2101,7 +1988,10 @@ void SurfaceFlinger::onMessageRefresh() { postFrame(); postComposition(); - const bool prevFrameHadDeviceComposition = mHadDeviceComposition; + mFrameTimeline->setSfPresent(systemTime(), + std::make_shared<FenceTime>(mPreviousPresentFences[0])); + + const bool prevFrameHadClientComposition = mHadClientComposition; mHadClientComposition = std::any_of(displays.cbegin(), displays.cend(), [](const auto& pair) { const auto& state = pair.second->getCompositionDisplay()->getState(); @@ -2116,22 +2006,25 @@ void SurfaceFlinger::onMessageRefresh() { const auto& state = pair.second->getCompositionDisplay()->getState(); return state.reusedClientComposition; }); - - // Only report a strategy change if we move in and out of composition with hw overlays - if (prevFrameHadDeviceComposition != mHadDeviceComposition) { + // Only report a strategy change if we move in and out of client composition + if (prevFrameHadClientComposition != mHadClientComposition) { mTimeStats->incrementCompositionStrategyChanges(); } // TODO: b/160583065 Enable skip validation when SF caches all client composition layers - mVSyncModulator->onRefreshed(mHadClientComposition || mReusedClientComposition); + const bool usedGpuComposition = mHadClientComposition || mReusedClientComposition; + modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition); mLayersWithQueuedFrames.clear(); - if (mVisibleRegionsDirty) { - mVisibleRegionsDirty = false; - if (mTracingEnabled && mAddCompositionStateToTrace) { + if (mTracingEnabled && mTracePostComposition) { + // This may block if SurfaceTracing is running in sync mode. + if (mVisibleRegionsDirty) { mTracing.notify("visibleRegionsDirty"); + } else if (mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS)) { + mTracing.notify("bufferLatched"); } } + mVisibleRegionsDirty = false; if (mCompositionEngine->needsAnotherUpdate()) { signalLayerUpdate(); @@ -2185,12 +2078,12 @@ void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats, nsecs_t compositeToPresentLatency) { // Integer division and modulo round toward 0 not -inf, so we need to // treat negative and positive offsets differently. - nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0) + nsecs_t idealLatency = (mVsyncConfiguration->getCurrentConfigs().late.sfOffset > 0) ? (stats.vsyncPeriod - - (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod)) - : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod); + (mVsyncConfiguration->getCurrentConfigs().late.sfOffset % stats.vsyncPeriod)) + : ((-mVsyncConfiguration->getCurrentConfigs().late.sfOffset) % stats.vsyncPeriod); - // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval. + // Just in case mVsyncConfiguration->getCurrentConfigs().late.sf == -vsyncInterval. if (idealLatency <= 0) { idealLatency = stats.vsyncPeriod; } @@ -2200,7 +2093,7 @@ void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats, // Reducing jitter is important if an app attempts to extrapolate // something (such as user input) to an accurate diasplay time. // Snapping also allows an app to precisely calculate - // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval). + // mVsyncConfiguration->getCurrentConfigs().late.sf with (presentLatency % interval). nsecs_t bias = stats.vsyncPeriod / 2; int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod; nsecs_t snappedCompositeToPresentLatency = @@ -2218,7 +2111,7 @@ void SurfaceFlinger::postComposition() ALOGV("postComposition"); nsecs_t dequeueReadyTime = systemTime(); - for (auto& layer : mLayersWithQueuedFrames) { + for (auto layer : mLayersWithQueuedFrames) { layer->releasePendingBuffer(dequeueReadyTime); } @@ -2239,12 +2132,12 @@ void SurfaceFlinger::postComposition() getBE().mDisplayTimeline.updateSignalTimes(); mPreviousPresentFences[1] = mPreviousPresentFences[0]; mPreviousPresentFences[0] = - display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE; + display ? getHwComposer().getPresentFence(display->getPhysicalId()) : Fence::NO_FENCE; auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]); getBE().mDisplayTimeline.push(presentFenceTime); DisplayStatInfo stats; - mScheduler->getDisplayStatInfo(&stats); + mScheduler->getDisplayStatInfo(&stats, systemTime()); // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might // be sampled a little later than when we started doing work for this frame, @@ -2273,7 +2166,8 @@ void SurfaceFlinger::postComposition() mScheduler->addPresentFence(presentFenceTime); } - const bool isDisplayConnected = display && getHwComposer().isConnected(*display->getId()); + const bool isDisplayConnected = + display && getHwComposer().isConnected(display->getPhysicalId()); if (!hasSyncFramework) { if (isDisplayConnected && display->isPoweredOn()) { @@ -2290,7 +2184,8 @@ void SurfaceFlinger::postComposition() } else if (isDisplayConnected) { // The HWC doesn't support present fences, so use the refresh // timestamp instead. - const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*display->getId()); + const nsecs_t presentTime = + getHwComposer().getRefreshTimestamp(display->getPhysicalId()); mAnimFrameTracker.setActualPresentTime(presentTime); } mAnimFrameTracker.advanceFrame(); @@ -2371,7 +2266,7 @@ void SurfaceFlinger::postComposition() } FloatRect SurfaceFlinger::getLayerClipBoundsForDisplay(const DisplayDevice& displayDevice) const { - return displayDevice.getViewport().toFloatRect(); + return displayDevice.getLayerStackSpaceRect().toFloatRect(); } void SurfaceFlinger::computeLayerBounds() { @@ -2392,7 +2287,7 @@ void SurfaceFlinger::computeLayerBounds() { void SurfaceFlinger::postFrame() { const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()); - if (display && getHwComposer().isConnected(*display->getId())) { + if (display && getHwComposer().isConnected(display->getPhysicalId())) { uint32_t flipCount = display->getPageFlipCount(); if (flipCount % LOG_FRAME_STATS_PERIOD == 0) { logFrameStats(); @@ -2419,7 +2314,7 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) // with mStateLock held to guarantee that mCurrentState won't change // until the transaction is committed. - mVSyncModulator->onTransactionHandled(); + modulateVsync(&VsyncModulator::onTransactionCommit); transactionFlags = getTransactionFlags(eTransactionMask); handleTransactionLocked(transactionFlags); @@ -2430,14 +2325,14 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) void SurfaceFlinger::processDisplayHotplugEventsLocked() { for (const auto& event : mPendingHotplugEvents) { - const std::optional<DisplayIdentificationInfo> info = + std::optional<DisplayIdentificationInfo> info = getHwComposer().onHotplug(event.hwcDisplayId, event.connection); if (!info) { continue; } - const DisplayId displayId = info->id; + const auto displayId = info->id; const auto it = mPhysicalDisplayTokens.find(displayId); if (event.connection == hal::Connection::CONNECTED) { @@ -2449,10 +2344,12 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { } DisplayDeviceState state; - state.physical = {displayId, getHwComposer().getDisplayConnectionType(displayId), - event.hwcDisplayId}; + state.physical = {.id = displayId, + .type = getHwComposer().getDisplayConnectionType(displayId), + .hwcDisplayId = event.hwcDisplayId, + .deviceProductInfo = std::move(info->deviceProductInfo)}; state.isSecure = true; // All physical displays are currently considered secure. - state.displayName = info->name; + state.displayName = std::move(info->name); sp<IBinder> token = new BBinder(); mCurrentState.displays.add(token, state); @@ -2464,7 +2361,10 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { const auto token = it->second; auto& state = mCurrentState.displays.editValueFor(token); - state.sequenceId = DisplayDeviceState{}.sequenceId; + state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId + if (getHwComposer().updatesDeviceProductInfoOnHotplugReconnect()) { + state.physical->deviceProductInfo = std::move(info->deviceProductInfo); + } } } else { ALOGV("Removing display %s", to_string(displayId).c_str()); @@ -2495,7 +2395,6 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& displaySurface, const sp<IGraphicBufferProducer>& producer) { - auto displayId = compositionDisplay->getDisplayId(); DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay); creationArgs.sequenceId = state.sequenceId; creationArgs.isSecure = state.isSecure; @@ -2507,26 +2406,26 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( creationArgs.connectionType = physical->type; } - const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked(); - creationArgs.isPrimary = isInternalDisplay; + if (const auto id = PhysicalDisplayId::tryCast(compositionDisplay->getId())) { + creationArgs.isPrimary = id == getInternalDisplayIdLocked(); - if (useColorManagement && displayId) { - std::vector<ColorMode> modes = getHwComposer().getColorModes(*displayId); - for (ColorMode colorMode : modes) { - if (isWideColorMode(colorMode)) { - creationArgs.hasWideColorGamut = true; - } + if (useColorManagement) { + std::vector<ColorMode> modes = getHwComposer().getColorModes(*id); + for (ColorMode colorMode : modes) { + if (isWideColorMode(colorMode)) { + creationArgs.hasWideColorGamut = true; + } - std::vector<RenderIntent> renderIntents = - getHwComposer().getRenderIntents(*displayId, colorMode); - creationArgs.hwcColorModes.emplace(colorMode, renderIntents); + std::vector<RenderIntent> renderIntents = + getHwComposer().getRenderIntents(*id, colorMode); + creationArgs.hwcColorModes.emplace(colorMode, renderIntents); + } } } - if (displayId) { - getHwComposer().getHdrCapabilities(*displayId, &creationArgs.hdrCapabilities); - creationArgs.supportedPerFrameMetadata = - getHwComposer().getSupportedPerFrameMetadata(*displayId); + if (const auto id = HalDisplayId::tryCast(compositionDisplay->getId())) { + getHwComposer().getHdrCapabilities(*id, &creationArgs.hdrCapabilities); + creationArgs.supportedPerFrameMetadata = getHwComposer().getSupportedPerFrameMetadata(*id); } auto nativeWindowSurface = getFactory().createNativeWindowSurface(producer); @@ -2541,7 +2440,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( } creationArgs.physicalOrientation = - isInternalDisplay ? internalDisplayOrientation : ui::ROTATION_0; + creationArgs.isPrimary ? internalDisplayOrientation : ui::ROTATION_0; // virtual displays are always considered enabled creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF; @@ -2563,13 +2462,15 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( RenderIntent::COLORIMETRIC, Dataspace::UNKNOWN}); if (!state.isVirtual()) { - LOG_ALWAYS_FATAL_IF(!displayId); - auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId)); + const auto physicalId = display->getPhysicalId(); + auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(physicalId)); display->setActiveConfig(activeConfigId); + display->setDeviceProductInfo(state.physical->deviceProductInfo); } display->setLayerStack(state.layerStack); - display->setProjection(state.orientation, state.viewport, state.frame); + display->setProjection(state.orientation, state.layerStackSpaceRect, + state.orientedDisplaySpaceRect); display->setDisplayName(state.displayName); return display; @@ -2611,7 +2512,8 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, builder.setIsSecure(state.isSecure); builder.setLayerStackId(state.layerStack); builder.setPowerAdvisor(&mPowerAdvisor); - builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer()); + builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays); + builder.setGpuVirtualDisplayIdGenerator(mGpuVirtualDisplayIdGenerator); builder.setName(state.displayName); const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build()); @@ -2621,11 +2523,13 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, sp<IGraphicBufferConsumer> bqConsumer; getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false); - std::optional<DisplayId> displayId = compositionDisplay->getId(); + DisplayId displayId = compositionDisplay->getId(); if (state.isVirtual()) { + const auto virtualId = VirtualDisplayId::tryCast(displayId); + LOG_FATAL_IF(!virtualId); sp<VirtualDisplaySurface> vds = - new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer, + new VirtualDisplaySurface(getHwComposer(), *virtualId, state.surface, bqProducer, bqConsumer, state.displayName); displaySurface = vds; @@ -2635,9 +2539,9 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, "adding a supported display, but rendering " "surface is provided (%p), ignoring it", state.surface.get()); - - LOG_ALWAYS_FATAL_IF(!displayId); - displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer, + const auto physicalId = PhysicalDisplayId::tryCast(displayId); + LOG_FATAL_IF(!physicalId); + displaySurface = new FramebufferSurface(getHwComposer(), *physicalId, bqConsumer, maxGraphicsWidth, maxGraphicsHeight); producer = bqProducer; } @@ -2647,8 +2551,7 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, displaySurface, producer); mDisplays.emplace(displayToken, display); if (!state.isVirtual()) { - LOG_FATAL_IF(!displayId); - dispatchDisplayHotplugEvent(displayId->value, true); + dispatchDisplayHotplugEvent(display->getPhysicalId(), true); } if (display->isPrimary()) { @@ -2658,13 +2561,9 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { if (const auto display = getDisplayDeviceLocked(displayToken)) { - // Save display ID before disconnecting. - const auto displayId = display->getId(); display->disconnect(); - if (!display->isVirtual()) { - LOG_FATAL_IF(!displayId); - dispatchDisplayHotplugEvent(displayId->value, false); + dispatchDisplayHotplugEvent(display->getPhysicalId(), false); } } @@ -2678,6 +2577,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface); if (currentBinder != drawingBinder || currentState.sequenceId != drawingState.sequenceId) { // changing the surface is like destroying and recreating the DisplayDevice + getRenderEngine().cleanFramebufferCache(); if (const auto display = getDisplayDeviceLocked(displayToken)) { display->disconnect(); } @@ -2698,10 +2598,10 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, display->setLayerStack(currentState.layerStack); } if ((currentState.orientation != drawingState.orientation) || - (currentState.viewport != drawingState.viewport) || - (currentState.frame != drawingState.frame)) { - display->setProjection(currentState.orientation, currentState.viewport, - currentState.frame); + (currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) || + (currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) { + display->setProjection(currentState.orientation, currentState.layerStackSpaceRect, + currentState.orientedDisplaySpaceRect); } if (currentState.width != drawingState.width || currentState.height != drawingState.height) { @@ -2909,23 +2809,26 @@ void SurfaceFlinger::updateInputFlinger() { setInputWindowsFinished(); } + for (const auto& focusRequest : mInputWindowCommands.focusRequests) { + mInputFlinger->setFocusedWindow(focusRequest); + } mInputWindowCommands.clear(); } void SurfaceFlinger::updateInputWindowInfo() { - std::vector<InputWindowInfo> inputHandles; + 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. - inputHandles.push_back(layer->fillInputInfo()); + inputInfos.push_back(layer->fillInputInfo()); } }); - mInputFlinger->setInputWindows(inputHandles, - mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener - : nullptr); + mInputFlinger->setInputWindows(inputInfos, + mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener + : nullptr); } void SurfaceFlinger::commitInputWindowCommands() { @@ -2936,7 +2839,7 @@ void SurfaceFlinger::commitInputWindowCommands() { void SurfaceFlinger::updateCursorAsync() { compositionengine::CompositionRefreshArgs refreshArgs; for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) { - if (display->getId()) { + if (HalDisplayId::tryCast(display->getId())) { refreshArgs.outputs.push_back(display->getCompositionDisplay()); } } @@ -2954,7 +2857,7 @@ void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, changeRefreshRateLocked(refreshRate, event); } -void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { +void SurfaceFlinger::initScheduler(PhysicalDisplayId primaryDisplayId) { if (mScheduler) { // In practice it's not allowed to hotplug in/out the primary display once it's been // connected during startup, but some tests do it, so just warn and return. @@ -2972,24 +2875,29 @@ void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { currentConfig, hal::PowerMode::OFF); mRefreshRateStats->setConfigMode(currentConfig); - mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs); + mVsyncConfiguration = getFactory().createVsyncConfiguration(*mRefreshRateConfigs); + mVsyncModulator.emplace(mVsyncConfiguration->getCurrentConfigs()); // start the EventThread - mScheduler = - getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); }, - *mRefreshRateConfigs, *this); + mScheduler = getFactory().createScheduler(*mRefreshRateConfigs, *this); + const auto configs = mVsyncConfiguration->getCurrentConfigs(); + const nsecs_t vsyncPeriod = + mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod(); mAppConnectionHandle = - mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app, + mScheduler->createConnection("app", mFrameTimeline->getTokenManager(), + /*workDuration=*/configs.late.appWorkDuration, + /*readyDuration=*/configs.late.sfWorkDuration, impl::EventThread::InterceptVSyncsCallback()); mSfConnectionHandle = - mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf, + mScheduler->createConnection("appSf", mFrameTimeline->getTokenManager(), + /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod), + /*readyDuration=*/configs.late.sfWorkDuration, [this](nsecs_t timestamp) { mInterceptor->saveVSyncEvent(timestamp); }); - mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); - mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle, - mPhaseConfiguration->getCurrentOffsets()); + mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(), + configs.late.sfWorkDuration); mRegionSamplingThread = new RegionSamplingThread(*this, *mScheduler, @@ -3001,14 +2909,33 @@ void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { // This is a bit hacky, but this avoids a back-pointer into the main SF // classes from EventThread, and there should be no run-time binder cost // anyway since there are no connected apps at this point. - const nsecs_t vsyncPeriod = - mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod(); - mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId.value, - currentConfig, vsyncPeriod); + mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId, currentConfig, + vsyncPeriod); + static auto ignorePresentFences = + base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false); + mScheduler->setIgnorePresentFences( + ignorePresentFences || + getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE)); } -void SurfaceFlinger::commitTransaction() -{ +void SurfaceFlinger::updatePhaseConfiguration(const RefreshRate& refreshRate) { + mVsyncConfiguration->setRefreshRateFps(refreshRate.getFps()); + setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()), + refreshRate.getVsyncPeriod()); +} + +void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config, + nsecs_t vsyncPeriod) { + mScheduler->setDuration(mAppConnectionHandle, + /*workDuration=*/config.appWorkDuration, + /*readyDuration=*/config.sfWorkDuration); + mScheduler->setDuration(mSfConnectionHandle, + /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod), + /*readyDuration=*/config.sfWorkDuration); + mEventQueue->setDuration(config.sfWorkDuration); +} + +void SurfaceFlinger::commitTransaction() { commitTransactionLocked(); mTransactionPending = false; mAnimTransactionPending = false; @@ -3044,15 +2971,19 @@ void SurfaceFlinger::commitTransactionLocked() { // clear the "changed" flags in current state mCurrentState.colorMatrixChanged = false; - mDrawingState.traverse([&](Layer* layer) { - layer->commitChildList(); - - // If the layer can be reached when traversing mDrawingState, then the layer is no - // longer offscreen. Remove the layer from the offscreenLayer set. - if (mOffscreenLayers.count(layer)) { - mOffscreenLayers.erase(layer); - } - }); + for (const auto& rootLayer : mDrawingState.layersSortedByZ) { + rootLayer->commitChildList(); + } + // TODO(b/163019109): See if this traversal is needed at all... + if (!mOffscreenLayers.empty()) { + mDrawingState.traverse([&](Layer* layer) { + // If the layer can be reached when traversing mDrawingState, then the layer is no + // longer offscreen. Remove the layer from the offscreenLayer set. + if (mOffscreenLayers.count(layer)) { + mOffscreenLayers.erase(layer); + } + }); + } commitOffscreenLayers(); mDrawingState.traverse([&](Layer* layer) { layer->updateMirrorInfo(); }); @@ -3105,7 +3036,7 @@ bool SurfaceFlinger::handlePageFlip() if (layer->hasReadyFrame()) { frameQueued = true; if (layer->shouldPresentNow(expectedPresentTime)) { - mLayersWithQueuedFrames.push_back(layer); + mLayersWithQueuedFrames.emplace(layer); } else { ATRACE_NAME("!layer->shouldPresentNow()"); layer->useEmptyDamage(); @@ -3209,6 +3140,11 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers", mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize, mNumLayers.load()); + if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) { + ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers", + mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize, + mNumLayers.load()); + } } if (const auto display = getDefaultDisplayDeviceLocked()) { @@ -3243,16 +3179,13 @@ uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) { } uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { - return setTransactionFlags(flags, Scheduler::TransactionStart::Normal); + return setTransactionFlags(flags, TransactionSchedule::Late); } -uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, - Scheduler::TransactionStart transactionStart) { +uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, TransactionSchedule schedule) { uint32_t old = mTransactionFlags.fetch_or(flags); - mVSyncModulator->setTransactionStart(transactionStart); - if ((old & flags)==0) { // wake the server up - signalTransaction(); - } + modulateVsync(&VsyncModulator::setTransactionSchedule, schedule); + if ((old & flags) == 0) signalTransaction(); return old; } @@ -3281,11 +3214,13 @@ bool SurfaceFlinger::flushTransactionQueues() { break; } transactions.push_back(transaction); - applyTransactionState(transaction.states, transaction.displays, transaction.flags, + applyTransactionState(transaction.frameTimelineVsyncId, transaction.states, + transaction.displays, transaction.flags, mPendingInputWindowCommands, transaction.desiredPresentTime, transaction.buffer, transaction.postTime, transaction.privileged, transaction.hasListenerCallbacks, - transaction.listenerCallbacks, /*isMainThread*/ true); + transaction.listenerCallbacks, transaction.originPid, + transaction.originUid, transaction.id, /*isMainThread*/ true); transactionQueue.pop(); flushedATransaction = true; } @@ -3329,11 +3264,12 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime, return true; } -void SurfaceFlinger::setTransactionState( - const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, - int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks) { +status_t SurfaceFlinger::setTransactionState( + int64_t frameTimelineVsyncId, const Vector<ComposerState>& states, + const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, + const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, + const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) { ATRACE_CALL(); const int64_t postTime = systemTime(); @@ -3365,25 +3301,33 @@ void SurfaceFlinger::setTransactionState( mExpectedPresentTime = calculateExpectedPresentTime(systemTime()); } + IPCThreadState* ipc = IPCThreadState::self(); + const int originPid = ipc->getCallingPid(); + const int originUid = ipc->getCallingUid(); + if (pendingTransactions || !transactionIsReadyToBeApplied(desiredPresentTime, states)) { - mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime, - uncacheBuffer, postTime, privileged, - hasListenerCallbacks, listenerCallbacks); + mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags, + desiredPresentTime, uncacheBuffer, postTime, + privileged, hasListenerCallbacks, listenerCallbacks, + originPid, originUid, transactionId); setTransactionFlags(eTransactionFlushNeeded); - return; + return NO_ERROR; } - applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime, - uncacheBuffer, postTime, privileged, hasListenerCallbacks, - listenerCallbacks); + applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands, + desiredPresentTime, uncacheBuffer, postTime, privileged, + hasListenerCallbacks, listenerCallbacks, originPid, originUid, + transactionId, /*isMainThread*/ false); + return NO_ERROR; } void SurfaceFlinger::applyTransactionState( - const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, + int64_t frameTimelineVsyncId, const Vector<ComposerState>& states, + const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged, bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, - bool isMainThread) { + int originPid, int originUid, uint64_t transactionId, bool isMainThread) { uint32_t transactionFlags = 0; if (flags & eAnimation) { @@ -3417,8 +3361,9 @@ void SurfaceFlinger::applyTransactionState( std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces; uint32_t clientStateFlags = 0; for (const ComposerState& state : states) { - clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged, - listenerCallbacksWithSurfaces); + clientStateFlags |= + setClientStateLocked(frameTimelineVsyncId, state, desiredPresentTime, postTime, + privileged, listenerCallbacksWithSurfaces); if ((flags & eAnimation) && state.state.surface) { if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) { mScheduler->recordLayerHistory(layer.get(), desiredPresentTime, @@ -3437,7 +3382,11 @@ void SurfaceFlinger::applyTransactionState( } transactionFlags |= clientStateFlags; - transactionFlags |= addInputWindowCommands(inputWindowCommands); + if (privileged) { + transactionFlags |= addInputWindowCommands(inputWindowCommands); + } else if (!inputWindowCommands.empty()) { + ALOGE("Only privileged callers are allowed to send input commands."); + } if (uncacheBuffer.isValid()) { ClientCache::getInstance().erase(uncacheBuffer); @@ -3460,22 +3409,17 @@ void SurfaceFlinger::applyTransactionState( mForceTraversal = true; } - const auto transactionStart = [](uint32_t flags) { - if (flags & eEarlyWakeup) { - return Scheduler::TransactionStart::Early; - } - if (flags & eExplicitEarlyWakeupEnd) { - return Scheduler::TransactionStart::EarlyEnd; - } - if (flags & eExplicitEarlyWakeupStart) { - return Scheduler::TransactionStart::EarlyStart; - } - return Scheduler::TransactionStart::Normal; + const auto schedule = [](uint32_t flags) { + if (flags & eEarlyWakeup) return TransactionSchedule::Early; + if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd; + if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart; + return TransactionSchedule::Late; }(flags); if (transactionFlags) { if (mInterceptor->isEnabled()) { - mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags); + mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags, + originPid, originUid, transactionId); } // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag @@ -3489,7 +3433,7 @@ void SurfaceFlinger::applyTransactionState( } // this triggers the transaction - setTransactionFlags(transactionFlags, transactionStart); + setTransactionFlags(transactionFlags, schedule); if (flags & eAnimation) { mAnimTransactionPending = true; @@ -3527,11 +3471,10 @@ void SurfaceFlinger::applyTransactionState( } } } else { - // even if a transaction is not needed, we need to update VsyncModulator - // about explicit early indications - if (transactionStart == Scheduler::TransactionStart::EarlyStart || - transactionStart == Scheduler::TransactionStart::EarlyEnd) { - mVSyncModulator->setTransactionStart(transactionStart); + // Update VsyncModulator state machine even if transaction is not needed. + if (schedule == TransactionSchedule::EarlyStart || + schedule == TransactionSchedule::EarlyEnd) { + modulateVsync(&VsyncModulator::setTransactionSchedule, schedule); } } } @@ -3561,12 +3504,12 @@ uint32_t SurfaceFlinger::setDisplayStateLocked(const DisplayState& s) { state.orientation = s.orientation; flags |= eDisplayTransactionNeeded; } - if (state.frame != s.frame) { - state.frame = s.frame; + if (state.orientedDisplaySpaceRect != s.orientedDisplaySpaceRect) { + state.orientedDisplaySpaceRect = s.orientedDisplaySpaceRect; flags |= eDisplayTransactionNeeded; } - if (state.viewport != s.viewport) { - state.viewport = s.viewport; + if (state.layerStackSpaceRect != s.layerStackSpaceRect) { + state.layerStackSpaceRect = s.layerStackSpaceRect; flags |= eDisplayTransactionNeeded; } } @@ -3597,8 +3540,8 @@ bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermis } uint32_t SurfaceFlinger::setClientStateLocked( - const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime, - bool privileged, + int64_t frameTimelineVsyncId, const ComposerState& composerState, + int64_t desiredPresentTime, int64_t postTime, bool privileged, std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) { const layer_state_t& s = composerState.state; @@ -3667,7 +3610,8 @@ uint32_t SurfaceFlinger::setClientStateLocked( const auto& p = layer->getParent(); if (p == nullptr) { ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); - if (layer->setRelativeLayer(s.relativeLayerHandle, s.z) && idx >= 0) { + if (layer->setRelativeLayer(s.relativeLayerSurfaceControl->getHandle(), s.z) && + idx >= 0) { mCurrentState.layersSortedByZ.removeAt(idx); mCurrentState.layersSortedByZ.add(layer); // we need traversal (state changed) @@ -3675,7 +3619,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTransactionNeeded|eTraversalNeeded; } } else { - if (p->setChildRelativeLayer(layer, s.relativeLayerHandle, s.z)) { + if (p->setChildRelativeLayer(layer, s.relativeLayerSurfaceControl->getHandle(), s.z)) { flags |= eTransactionNeeded|eTraversalNeeded; } } @@ -3740,6 +3684,9 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs && mSupportsBlur) { if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eBlurRegionsChanged) { + if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded; + } if (what & layer_state_t::eLayerStackChanged) { ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer); // We only allow setting layer stacks for top level layers, @@ -3761,35 +3708,19 @@ uint32_t SurfaceFlinger::setClientStateLocked( } } if (what & layer_state_t::eDeferTransaction_legacy) { - if (s.barrierHandle_legacy != nullptr) { - layer->deferTransactionUntil_legacy(s.barrierHandle_legacy, s.frameNumber_legacy); - } else if (s.barrierGbp_legacy != nullptr) { - const sp<IGraphicBufferProducer>& gbp = s.barrierGbp_legacy; - if (authenticateSurfaceTextureLocked(gbp)) { - const auto& otherLayer = - (static_cast<MonitoredProducer*>(gbp.get()))->getLayer(); - layer->deferTransactionUntil_legacy(otherLayer, s.frameNumber_legacy); - } else { - ALOGE("Attempt to defer transaction to to an" - " unrecognized GraphicBufferProducer"); - } - } + layer->deferTransactionUntil_legacy(s.barrierSurfaceControl_legacy->getHandle(), + s.barrierFrameNumber); // We don't trigger a traversal here because if no other state is // changed, we don't want this to cause any more work } if (what & layer_state_t::eReparentChildren) { - if (layer->reparentChildren(s.reparentHandle)) { + if (layer->reparentChildren(s.reparentSurfaceControl->getHandle())) { flags |= eTransactionNeeded|eTraversalNeeded; } } if (what & layer_state_t::eDetachChildren) { layer->detachChildren(); } - if (what & layer_state_t::eOverrideScalingModeChanged) { - layer->setOverrideScalingMode(s.overrideScalingMode); - // We don't trigger a traversal here because if no other state is - // changed, we don't want this to cause any more work - } if (what & layer_state_t::eTransformChanged) { if (layer->setTransform(s.transform)) flags |= eTraversalNeeded; } @@ -3801,7 +3732,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (layer->setCrop(s.crop)) flags |= eTraversalNeeded; } if (what & layer_state_t::eFrameChanged) { - if (layer->setFrame(s.frame)) flags |= eTraversalNeeded; + if (layer->setFrame(s.orientedDisplaySpaceRect)) flags |= eTraversalNeeded; } if (what & layer_state_t::eAcquireFenceChanged) { if (layer->setAcquireFence(s.acquireFence)) flags |= eTraversalNeeded; @@ -3823,7 +3754,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( } if (what & layer_state_t::eInputInfoChanged) { if (privileged) { - layer->setInputInfo(s.inputInfo); + layer->setInputInfo(*s.inputHandle->getInfo()); flags |= eTraversalNeeded; } else { ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER"); @@ -3854,6 +3785,11 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } } + if (what & layer_state_t::eFrameTimelineVsyncChanged) { + layer->setFrameTimelineVsyncForTransaction(s.frameTimelineVsyncId, postTime); + } else if (frameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID) { + layer->setFrameTimelineVsyncForTransaction(frameTimelineVsyncId, postTime); + } if (what & layer_state_t::eFixedTransformHintChanged) { if (layer->setFixedTransformHint(s.fixedTransformHint)) { flags |= eTraversalNeeded | eTransformHintUpdateNeeded; @@ -3865,7 +3801,10 @@ uint32_t SurfaceFlinger::setClientStateLocked( // lose its relative z order. if (what & layer_state_t::eReparent) { bool hadParent = layer->hasParent(); - if (layer->reparent(s.parentHandleForChild)) { + auto parentHandle = (s.parentSurfaceControlForChild) + ? s.parentSurfaceControlForChild->getHandle() + : nullptr; + if (layer->reparent(parentHandle)) { if (!hadParent) { mCurrentState.layersSortedByZ.remove(layer); } @@ -3899,11 +3838,17 @@ uint32_t SurfaceFlinger::setClientStateLocked( buffer = s.buffer; } if (buffer) { - if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, - s.cachedBuffer)) { + const bool frameNumberChanged = what & layer_state_t::eFrameNumberChanged; + const uint64_t frameNumber = frameNumberChanged + ? s.frameNumber + : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1; + + if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, s.cachedBuffer, + frameNumber)) { flags |= eTraversalNeeded; } } + if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded; // Do not put anything that updates layer state or modifies flags after // setTransactionCompletedListener @@ -3911,17 +3856,12 @@ uint32_t SurfaceFlinger::setClientStateLocked( } uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) { - uint32_t flags = 0; - if (inputWindowCommands.syncInputWindows) { - flags |= eTraversalNeeded; - } - - mPendingInputWindowCommands.merge(inputWindowCommands); - return flags; + bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands); + return hasChanges ? eTraversalNeeded : 0; } status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle, - sp<IBinder>* outHandle) { + sp<IBinder>* outHandle, int32_t* outLayerId) { if (!mirrorFromHandle) { return NAME_NOT_FOUND; } @@ -3946,6 +3886,7 @@ status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder> mirrorLayer->mClonedChild = mirrorFrom->createClone(); } + *outLayerId = mirrorLayer->sequence; return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false, nullptr /* outTransformHint */); } @@ -3954,8 +3895,8 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie uint32_t h, PixelFormat format, uint32_t flags, LayerMetadata metadata, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, - const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer, - uint32_t* outTransformHint) { + const sp<IBinder>& parentHandle, int32_t* outLayerId, + const sp<Layer>& parentLayer, uint32_t* outTransformHint) { if (int32_t(w|h) < 0) { ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)", int(w), int(h)); @@ -3972,18 +3913,6 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie std::string uniqueName = getUniqueLayerName(name.string()); - bool primaryDisplayOnly = false; - - // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java - // TODO b/64227542 - if (metadata.has(METADATA_WINDOW_TYPE)) { - int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0); - if (windowType == 441731) { - metadata.setInt32(METADATA_WINDOW_TYPE, InputWindowInfo::TYPE_NAVIGATION_BAR_PANEL); - primaryDisplayOnly = true; - } - } - switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { case ISurfaceComposerClient::eFXSurfaceBufferQueue: result = createBufferQueueLayer(client, std::move(uniqueName), w, h, flags, @@ -4024,10 +3953,6 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie return result; } - if (primaryDisplayOnly) { - layer->setPrimaryDisplayOnly(); - } - bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess(); result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer, addToCurrentState, outTransformHint); @@ -4037,6 +3962,7 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie mInterceptor->saveSurfaceCreation(layer); setTransactionFlags(eTransactionNeeded); + *outLayerId = layer->sequence; return result; } @@ -4184,13 +4110,14 @@ void SurfaceFlinger::onInitializeDisplays() { d.token = token; d.layerStack = 0; d.orientation = ui::ROTATION_0; - d.frame.makeInvalid(); - d.viewport.makeInvalid(); + d.orientedDisplaySpaceRect.makeInvalid(); + d.layerStackSpaceRect.makeInvalid(); d.width = 0; d.height = 0; displays.add(d); - setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false, - {}); + setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr, + mPendingInputWindowCommands, -1, {}, false, {}, + 0 /* Undefined transactionId */); setPowerModeInternal(display, hal::PowerMode::ON); const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); @@ -4213,10 +4140,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: return; } - const auto displayId = display->getId(); - LOG_ALWAYS_FATAL_IF(!displayId); - - ALOGD("Setting power mode %d on display %s", mode, to_string(*displayId).c_str()); + const auto displayId = display->getPhysicalId(); + ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str()); const hal::PowerMode currentMode = display->getPowerMode(); if (mode == currentMode) { @@ -4233,9 +4158,9 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) { ALOGW("Couldn't set SCHED_FIFO on display on: %s\n", strerror(errno)); } - getHwComposer().setPowerMode(*displayId, mode); + getHwComposer().setPowerMode(displayId, mode); if (display->isPrimary() && mode != hal::PowerMode::DOZE_SUSPEND) { - getHwComposer().setVsyncEnabled(*displayId, mHWCVsyncPendingState); + getHwComposer().setVsyncEnabled(displayId, mHWCVsyncPendingState); mScheduler->onScreenAcquired(mAppConnectionHandle); mScheduler->resyncToHardwareVsync(true, vsyncPeriod); } @@ -4254,14 +4179,14 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } // Make sure HWVsync is disabled before turning off the display - getHwComposer().setVsyncEnabled(*displayId, hal::Vsync::DISABLE); + getHwComposer().setVsyncEnabled(displayId, hal::Vsync::DISABLE); - getHwComposer().setPowerMode(*displayId, mode); + getHwComposer().setPowerMode(displayId, mode); mVisibleRegionsDirty = true; // from this point on, SF will stop drawing on this display } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) { // Update display while dozing - getHwComposer().setPowerMode(*displayId, mode); + getHwComposer().setPowerMode(displayId, mode); if (display->isPrimary() && currentMode == hal::PowerMode::DOZE_SUSPEND) { mScheduler->onScreenAcquired(mAppConnectionHandle); mScheduler->resyncToHardwareVsync(true, vsyncPeriod); @@ -4272,10 +4197,10 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mScheduler->disableHardwareVsync(true); mScheduler->onScreenReleased(mAppConnectionHandle); } - getHwComposer().setPowerMode(*displayId, mode); + getHwComposer().setPowerMode(displayId, mode); } else { ALOGE("Attempting to set unknown power mode: %d\n", mode); - getHwComposer().setPowerMode(*displayId, mode); + getHwComposer().setPowerMode(displayId, mode); } if (display->isPrimary()) { @@ -4284,7 +4209,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON); } - ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str()); + ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str()); } void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) { @@ -4315,8 +4240,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { } else { static const std::unordered_map<std::string, Dumper> dumpers = { {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)}, - {"--dispsync"s, - dumper([this](std::string& s) { mScheduler->getPrimaryDispSync().dump(s); })}, + {"--dispsync"s, dumper([this](std::string& s) { mScheduler->dumpVsync(s); })}, {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)}, {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)}, {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, @@ -4326,6 +4250,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { {"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)}, {"--vsync"s, dumper(&SurfaceFlinger::dumpVSync)}, {"--wide-color"s, dumper(&SurfaceFlinger::dumpWideColorInfo)}, + {"--frametimeline"s, argsDumper(&SurfaceFlinger::dumpFrameTimeline)}, }; const auto flag = args.empty() ? ""s : std::string(String8(args[0])); @@ -4365,7 +4290,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) { if (asProto && mTracing.isEnabled()) { - mTracing.writeToFileAsync(); + mTracing.writeToFile(); } return doDump(fd, DumpArgs(), asProto); @@ -4408,6 +4333,10 @@ void SurfaceFlinger::dumpTimeStats(const DumpArgs& args, bool asProto, std::stri mTimeStats->parseArgs(asProto, args, result); } +void SurfaceFlinger::dumpFrameTimeline(const DumpArgs& args, std::string& result) const { + mFrameTimeline->parseArgs(args, result); +} + // This should only be called from the main thread. Otherwise it would need // the lock and should use mCurrentState rather than mDrawingState. void SurfaceFlinger::logFrameStats() { @@ -4439,31 +4368,24 @@ void SurfaceFlinger::dumpVSync(std::string& result) const { mRefreshRateStats->dump(result); result.append("\n"); - mPhaseConfiguration->dump(result); + mVsyncConfiguration->dump(result); StringAppendF(&result, " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", dispSyncPresentTimeOffset, getVsyncPeriodFromHWC()); scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy(); - StringAppendF(&result, - "DesiredDisplayConfigSpecs (DisplayManager): default config ID: %d" - ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n", - policy.defaultConfig.value(), policy.primaryRange.min, policy.primaryRange.max, - policy.appRequestRange.min, policy.appRequestRange.max); + StringAppendF(&result, "DesiredDisplayConfigSpecs (DisplayManager): %s\n\n", + policy.toString().c_str()); StringAppendF(&result, "(config override by backdoor: %s)\n\n", mDebugDisplayConfigSetByBackdoor ? "yes" : "no"); scheduler::RefreshRateConfigs::Policy currentPolicy = mRefreshRateConfigs->getCurrentPolicy(); if (currentPolicy != policy) { - StringAppendF(&result, - "DesiredDisplayConfigSpecs (Override): default config ID: %d" - ", primary range: [%.2f %.2f], app request range: [%.2f %.2f]\n\n", - currentPolicy.defaultConfig.value(), currentPolicy.primaryRange.min, - currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min, - currentPolicy.appRequestRange.max); + StringAppendF(&result, "DesiredDisplayConfigSpecs (Override): %s\n\n", + currentPolicy.toString().c_str()); } mScheduler->dump(mAppConnectionHandle, result); - mScheduler->getPrimaryDispSync().dump(result); + mScheduler->dumpVsync(result); } void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const { @@ -4543,7 +4465,7 @@ void SurfaceFlinger::dumpBufferingStats(std::string& result) const { void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { for (const auto& [token, display] : mDisplays) { - const auto displayId = display->getId(); + const auto displayId = PhysicalDisplayId::tryCast(display->getId()); if (!displayId) { continue; } @@ -4600,7 +4522,7 @@ void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { // TODO: print out if wide-color mode is active or not for (const auto& [token, display] : mDisplays) { - const auto displayId = display->getId(); + const auto displayId = PhysicalDisplayId::tryCast(display->getId()); if (!displayId) { continue; } @@ -4689,8 +4611,6 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("Build configuration:"); colorizer.reset(result); appendSfConfigString(result); - appendUiConfigString(result); - appendGuiConfigString(result); result.append("\n"); result.append("\nDisplay identification data:\n"); @@ -4733,7 +4653,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co StringAppendF(&result, "Composition layers\n"); mDrawingState.traverseInZOrder([&](Layer* layer) { auto* compositionState = layer->getCompositionState(); - if (!compositionState) return; + if (!compositionState || !compositionState->isVisible) return; android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer, layer->getDebugName() ? layer->getDebugName() @@ -4786,12 +4706,22 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co if (const auto displayId = getInternalDisplayIdLocked(); displayId && getHwComposer().isConnected(*displayId)) { const auto activeConfig = getHwComposer().getActiveConfig(*displayId); + std::string fps, xDpi, yDpi; + if (activeConfig) { + fps = base::StringPrintf("%.2f Hz", + 1e9f / getHwComposer().getDisplayVsyncPeriod(*displayId)); + xDpi = base::StringPrintf("%.2f", activeConfig->getDpiX()); + yDpi = base::StringPrintf("%.2f", activeConfig->getDpiY()); + } else { + fps = "unknown"; + xDpi = "unknown"; + yDpi = "unknown"; + } StringAppendF(&result, - " refresh-rate : %f fps\n" - " x-dpi : %f\n" - " y-dpi : %f\n", - 1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId), - activeConfig->getDpiX(), activeConfig->getDpiY()); + " refresh-rate : %s\n" + " x-dpi : %s\n" + " y-dpi : %s\n", + fps.c_str(), xDpi.c_str(), yDpi.c_str()); } StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0); @@ -4806,7 +4736,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co * HWC layer minidump */ for (const auto& [token, display] : mDisplays) { - const auto displayId = display->getId(); + const auto displayId = HalDisplayId::tryCast(display->getId()); if (!displayId) { continue; } @@ -4835,15 +4765,6 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); alloc.dump(result); - /* - * Dump VrFlinger state if in use. - */ - if (mVrFlingerRequestsDisplay && mVrFlinger) { - result.append("VrFlinger state:\n"); - result.append(mVrFlinger->Dump()); - result.append("\n"); - } - result.append(mTimeStats->miniDump()); result.append("\n"); } @@ -4897,7 +4818,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES: case SET_DISPLAY_CONTENT_SAMPLING_ENABLED: case GET_DISPLAYED_CONTENT_SAMPLE: - case NOTIFY_POWER_HINT: + case NOTIFY_POWER_BOOST: case SET_GLOBAL_SHADOW_SETTINGS: case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: { // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN is used by CTS tests, which acquire the @@ -4950,11 +4871,14 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { // special permissions. case SET_FRAME_RATE: case GET_DISPLAY_BRIGHTNESS_SUPPORT: - case SET_DISPLAY_BRIGHTNESS: { + // captureLayers and captureDisplay will handle the permission check in the function + case CAPTURE_LAYERS: + case CAPTURE_DISPLAY: + case SET_DISPLAY_BRIGHTNESS: + case SET_FRAME_TIMELINE_VSYNC: { return OK; } - case CAPTURE_LAYERS: - case CAPTURE_SCREEN: + case ADD_REGION_SAMPLING_LISTENER: case REMOVE_REGION_SAMPLING_LISTENER: { // codes that require permission check @@ -4968,7 +4892,8 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { } return OK; } - case CAPTURE_SCREEN_BY_ID: { + case ADD_TRANSACTION_TRACE_LISTENER: + case CAPTURE_DISPLAY_BY_ID: { IPCThreadState* ipc = IPCThreadState::self(); const int uid = ipc->getCallingUid(); if (uid == AID_ROOT || uid == AID_GRAPHICS || uid == AID_SYSTEM || uid == AID_SHELL) { @@ -4986,9 +4911,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { code == IBinder::SYSPROPS_TRANSACTION) { return OK; } - // Numbers from 1000 to 1036 are currently used for backdoors. The code + // Numbers from 1000 to 1038 are currently used for backdoors. The code // in onTransact verifies that the user is root, and has access to use SF. - if (code >= 1000 && code <= 1036) { + if (code >= 1000 && code <= 1039) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -5131,14 +5056,14 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r mForceFullDamage = n != 0; return NO_ERROR; } - case 1018: { // Modify Choreographer's phase offset + case 1018: { // Modify Choreographer's duration n = data.readInt32(); - mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n)); + mScheduler->setDuration(mAppConnectionHandle, std::chrono::nanoseconds(n), 0ns); return NO_ERROR; } - case 1019: { // Modify SurfaceFlinger's phase offset + case 1019: { // Modify SurfaceFlinger's duration n = data.readInt32(); - mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n)); + mScheduler->setDuration(mSfConnectionHandle, std::chrono::nanoseconds(n), 0ns); return NO_ERROR; } case 1020: { // Layer updates interceptor @@ -5182,19 +5107,19 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } case 1025: { // Set layer tracing n = data.readInt32(); + bool tracingEnabledChanged; if (n) { ALOGD("LayerTracing enabled"); - mTracingEnabledChanged = mTracing.enable(); - reply->writeInt32(NO_ERROR); + tracingEnabledChanged = mTracing.enable(); + if (tracingEnabledChanged) { + schedule([&]() MAIN_THREAD { mTracing.notify("start"); }).wait(); + } } else { ALOGD("LayerTracing disabled"); - mTracingEnabledChanged = mTracing.disable(); - if (mTracingEnabledChanged) { - reply->writeInt32(mTracing.writeToFile()); - } else { - reply->writeInt32(NO_ERROR); - } + tracingEnabledChanged = mTracing.disable(); } + mTracingEnabledChanged = tracingEnabledChanged; + reply->writeInt32(NO_ERROR); return NO_ERROR; } case 1026: { // Get layer tracing status @@ -5226,11 +5151,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } return NO_ERROR; } - // Is VrFlinger active? - case 1028: { - Mutex::Autolock _l(mStateLock); - reply->writeBool(getHwComposer().isUsingVrComposer()); - return NO_ERROR; + case 1028: { // Unused. + return NAME_NOT_FOUND; } // Set buffer size for SF tracing (value in KB) case 1029: { @@ -5307,7 +5229,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1035: { n = data.readInt32(); mDebugDisplayConfigSetByBackdoor = false; - if (n >= 0) { + const auto numConfigs = mRefreshRateConfigs->getAllRefreshRates().size(); + if (n >= 0 && n < numConfigs) { const auto displayToken = getInternalDisplayToken(); status_t result = setActiveConfig(displayToken, n); if (result != NO_ERROR) { @@ -5329,6 +5252,39 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } return NO_ERROR; } + // Inject a hotplug connected event for the primary display. This will deallocate and + // reallocate the display state including framebuffers. + case 1037: { + std::optional<hal::HWDisplayId> hwcId; + { + Mutex::Autolock lock(mStateLock); + hwcId = getHwComposer().getInternalHwcDisplayId(); + } + onHotplugReceived(getBE().mComposerSequenceId, *hwcId, hal::Connection::CONNECTED); + return NO_ERROR; + } + // Modify the max number of display frames stored within FrameTimeline + case 1038: { + n = data.readInt32(); + if (n < 0 || n > MAX_ALLOWED_DISPLAY_FRAMES) { + ALOGW("Invalid max size. Maximum allowed is %d", MAX_ALLOWED_DISPLAY_FRAMES); + return BAD_VALUE; + } + if (n == 0) { + // restore to default + mFrameTimeline->reset(); + return NO_ERROR; + } + mFrameTimeline->setMaxDisplayFrames(n); + return NO_ERROR; + } + case 1039: { + // The first parameter is the uid + n = data.readInt32(); + const float refreshRateHz = data.readFloat(); + mRefreshRateConfigs->setPreferredRefreshRateForUid(n, refreshRateHz); + } + return NO_ERROR; } } return err; @@ -5413,45 +5369,6 @@ private: const int mApi; }; -status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken, - sp<GraphicBuffer>* outBuffer, bool& outCapturedSecureLayers, - Dataspace reqDataspace, ui::PixelFormat reqPixelFormat, - const Rect& sourceCrop, uint32_t reqWidth, - uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, bool captureSecureLayers) { - ATRACE_CALL(); - - if (!displayToken) return BAD_VALUE; - - auto renderAreaRotation = ui::Transform::toRotationFlags(rotation); - if (renderAreaRotation == ui::Transform::ROT_INVALID) { - ALOGE("%s: Invalid rotation: %s", __FUNCTION__, toCString(rotation)); - renderAreaRotation = ui::Transform::ROT_0; - } - - sp<DisplayDevice> display; - { - Mutex::Autolock lock(mStateLock); - - display = getDisplayDeviceLocked(displayToken); - if (!display) return NAME_NOT_FOUND; - - // set the requested width/height to the logical display viewport size - // by default - if (reqWidth == 0 || reqHeight == 0) { - reqWidth = uint32_t(display->getViewport().width()); - reqHeight = uint32_t(display->getViewport().height()); - } - } - - DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace, - renderAreaRotation, captureSecureLayers); - auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display, - std::placeholders::_1); - return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, - useIdentityTransform, outCapturedSecureLayers); -} - static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) { switch (colorMode) { case ColorMode::DISPLAY_P3: @@ -5464,6 +5381,24 @@ static Dataspace pickDataspaceFromColorMode(const ColorMode colorMode) { } } +static status_t validateScreenshotPermissions(const CaptureArgs& captureArgs) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if (uid == AID_GRAPHICS || PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { + return OK; + } + + // If the caller doesn't have the correct permissions but is only attempting to screenshot + // itself, we allow it to continue. + if (captureArgs.uid == uid) { + return OK; + } + + ALOGE("Permission Denial: can't take screenshot pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; +} + status_t SurfaceFlinger::setSchedFifo(bool enabled) { static constexpr int kFifoPriority = 2; static constexpr int kOtherPriority = 0; @@ -5485,8 +5420,8 @@ status_t SurfaceFlinger::setSchedFifo(bool enabled) { } sp<DisplayDevice> SurfaceFlinger::getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) { - const sp<IBinder> displayToken = getPhysicalDisplayTokenLocked(DisplayId{displayOrLayerStack}); - if (displayToken) { + if (const sp<IBinder> displayToken = + getPhysicalDisplayTokenLocked(PhysicalDisplayId{displayOrLayerStack})) { return getDisplayDeviceLocked(displayToken); } // Couldn't find display by displayId. Try to get display by layerStack since virtual displays @@ -5503,154 +5438,113 @@ sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) { return nullptr; } -status_t SurfaceFlinger::captureScreen(uint64_t displayOrLayerStack, Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer) { - sp<DisplayDevice> display; - uint32_t width; - uint32_t height; - ui::Transform::RotationFlags captureOrientation; - { - Mutex::Autolock lock(mStateLock); - display = getDisplayByIdOrLayerStack(displayOrLayerStack); - if (!display) { - return NAME_NOT_FOUND; - } +status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) { + ATRACE_CALL(); - width = uint32_t(display->getViewport().width()); - height = uint32_t(display->getViewport().height()); + status_t validate = validateScreenshotPermissions(args); + if (validate != OK) { + return validate; + } - const auto orientation = display->getOrientation(); - captureOrientation = ui::Transform::toRotationFlags(orientation); + if (!args.displayToken) return BAD_VALUE; - switch (captureOrientation) { - case ui::Transform::ROT_90: - captureOrientation = ui::Transform::ROT_270; - break; - - case ui::Transform::ROT_270: - captureOrientation = ui::Transform::ROT_90; - break; + wp<DisplayDevice> displayWeak; + ui::LayerStack layerStack; + ui::Size reqSize(args.width, args.height); + ui::Dataspace dataspace; + { + Mutex::Autolock lock(mStateLock); + sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken); + if (!display) return NAME_NOT_FOUND; + displayWeak = display; + layerStack = display->getLayerStack(); - case ui::Transform::ROT_INVALID: - ALOGE("%s: Invalid orientation: %s", __FUNCTION__, toCString(orientation)); - captureOrientation = ui::Transform::ROT_0; - break; + // set the requested width/height to the logical display layer stack rect size by default + if (args.width == 0 || args.height == 0) { + reqSize = display->getLayerStackSpaceRect().getSize(); + } - default: - break; + // The dataspace is depended on the color mode of display, that could use non-native mode + // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes, + // and failed if display is not in native mode. This provide a way to force using native + // colors when capture. + dataspace = args.dataspace; + if (dataspace == ui::Dataspace::UNKNOWN) { + const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode; + dataspace = pickDataspaceFromColorMode(colorMode); } - *outDataspace = - pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode); } - DisplayRenderArea renderArea(display, Rect(), width, height, *outDataspace, captureOrientation, - false /* captureSecureLayers */); + RenderAreaFuture renderAreaFuture = promise::defer([=] { + return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, dataspace, + args.useIdentityTransform, args.captureSecureLayers); + }); + + auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) { + traverseLayersInLayerStack(layerStack, args.uid, visitor); + }; - auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display, - std::placeholders::_1); - bool ignored = false; - return captureScreenCommon(renderArea, traverseLayers, outBuffer, ui::PixelFormat::RGBA_8888, - false /* useIdentityTransform */, - ignored /* outCapturedSecureLayers */); + return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, + args.pixelFormat, args.allowProtected, captureListener); } -status_t SurfaceFlinger::captureLayers( - const sp<IBinder>& layerHandleBinder, sp<GraphicBuffer>* outBuffer, - const Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& excludeHandles, - float frameScale, bool childrenOnly) { - ATRACE_CALL(); - - class LayerRenderArea : public RenderArea { - public: - LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop, - int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace, - bool childrenOnly, const Rect& displayViewport) - : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport), - mLayer(layer), - mCrop(crop), - mNeedsFiltering(false), - mFlinger(flinger), - mChildrenOnly(childrenOnly) {} - const ui::Transform& getTransform() const override { return mTransform; } - Rect getBounds() const override { return mLayer->getBufferSize(mLayer->getDrawingState()); } - int getHeight() const override { - return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight(); - } - int getWidth() const override { - return mLayer->getBufferSize(mLayer->getDrawingState()).getWidth(); - } - bool isSecure() const override { return false; } - bool needsFiltering() const override { return mNeedsFiltering; } - sp<const DisplayDevice> getDisplayDevice() const override { return nullptr; } - Rect getSourceCrop() const override { - if (mCrop.isEmpty()) { - return getBounds(); - } else { - return mCrop; - } +status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener) { + ui::LayerStack layerStack; + wp<DisplayDevice> displayWeak; + ui::Size size; + ui::Dataspace dataspace; + { + Mutex::Autolock lock(mStateLock); + sp<DisplayDevice> display = getDisplayByIdOrLayerStack(displayOrLayerStack); + if (!display) { + return NAME_NOT_FOUND; } - class ReparentForDrawing { - public: - const sp<Layer>& oldParent; - const sp<Layer>& newParent; - - ReparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent, - const Rect& drawingBounds) - : oldParent(oldParent), newParent(newParent) { - // Compute and cache the bounds for the new parent layer. - newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(), - 0.f /* shadowRadius */); - oldParent->setChildrenDrawingParent(newParent); - } - ~ReparentForDrawing() { oldParent->setChildrenDrawingParent(oldParent); } - }; - - void render(std::function<void()> drawLayers) override { - const Rect sourceCrop = getSourceCrop(); - // no need to check rotation because there is none - mNeedsFiltering = sourceCrop.width() != getReqWidth() || - sourceCrop.height() != getReqHeight(); + layerStack = display->getLayerStack(); + displayWeak = display; - if (!mChildrenOnly) { - mTransform = mLayer->getTransform().inverse(); - drawLayers(); - } else { - uint32_t w = static_cast<uint32_t>(getWidth()); - uint32_t h = static_cast<uint32_t>(getHeight()); - // In the "childrenOnly" case we reparent the children to a screenshot - // layer which has no properties set and which does not draw. - sp<ContainerLayer> screenshotParentLayer = - mFlinger->getFactory().createContainerLayer({mFlinger, nullptr, - "Screenshot Parent"s, w, h, 0, - LayerMetadata()}); - - ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop); - drawLayers(); - } - } + size = display->getLayerStackSpaceRect().getSize(); - private: - const sp<Layer> mLayer; - const Rect mCrop; + dataspace = + pickDataspaceFromColorMode(display->getCompositionDisplay()->getState().colorMode); + } - ui::Transform mTransform; - bool mNeedsFiltering; + RenderAreaFuture renderAreaFuture = promise::defer([=] { + return DisplayRenderArea::create(displayWeak, Rect(), size, dataspace, + false /* useIdentityTransform */, + false /* captureSecureLayers */); + }); - SurfaceFlinger* mFlinger; - const bool mChildrenOnly; + auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) { + traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor); }; - int reqWidth = 0; - int reqHeight = 0; + return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size, + ui::PixelFormat::RGBA_8888, false /* allowProtected */, + captureListener); +} + +status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) { + ATRACE_CALL(); + + status_t validate = validateScreenshotPermissions(args); + if (validate != OK) { + return validate; + } + + ui::Size reqSize; sp<Layer> parent; - Rect crop(sourceCrop); + Rect crop(args.sourceCrop); std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers; - Rect displayViewport; + Rect layerStackSpaceRect; + ui::Dataspace dataspace; + bool captureSecureLayers; { Mutex::Autolock lock(mStateLock); - parent = fromHandleLocked(layerHandleBinder).promote(); + parent = fromHandleLocked(args.layerHandle).promote(); if (parent == nullptr || parent->isRemovedFromCurrentState()) { ALOGE("captureLayers called with an invalid or removed parent"); return NAME_NOT_FOUND; @@ -5664,25 +5558,24 @@ status_t SurfaceFlinger::captureLayers( } Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState()); - if (sourceCrop.width() <= 0) { + if (args.sourceCrop.width() <= 0) { crop.left = 0; crop.right = parentSourceBounds.getWidth(); } - if (sourceCrop.height() <= 0) { + if (args.sourceCrop.height() <= 0) { crop.top = 0; crop.bottom = parentSourceBounds.getHeight(); } - if (crop.isEmpty() || frameScale <= 0.0f) { + if (crop.isEmpty() || args.frameScale <= 0.0f) { // Error out if the layer has no source bounds (i.e. they are boundless) and a source // crop was not specified, or an invalid frame scale was provided. return BAD_VALUE; } - reqWidth = crop.width() * frameScale; - reqHeight = crop.height() * frameScale; + reqSize = ui::Size(crop.width() * args.frameScale, crop.height() * args.frameScale); - for (const auto& handle : excludeHandles) { + for (const auto& handle : args.excludeHandles) { sp<Layer> excludeLayer = fromHandleLocked(handle).promote(); if (excludeLayer != nullptr) { excludeLayers.emplace(excludeLayer); @@ -5697,25 +5590,43 @@ status_t SurfaceFlinger::captureLayers( return NAME_NOT_FOUND; } - displayViewport = display->getViewport(); + layerStackSpaceRect = display->getLayerStackSpaceRect(); + + // The dataspace is depended on the color mode of display, that could use non-native mode + // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes, + // and failed if display is not in native mode. This provide a way to force using native + // colors when capture. + dataspace = args.dataspace; + if (dataspace == ui::Dataspace::UNKNOWN) { + const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode; + dataspace = pickDataspaceFromColorMode(colorMode); + } + + captureSecureLayers = args.captureSecureLayers && display->isSecure(); } // mStateLock // really small crop or frameScale - if (reqWidth <= 0) { - reqWidth = 1; + if (reqSize.width <= 0) { + reqSize.width = 1; } - if (reqHeight <= 0) { - reqHeight = 1; + if (reqSize.height <= 0) { + reqSize.height = 1; } - LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly, - displayViewport); - auto traverseLayers = [parent, childrenOnly, - &excludeLayers](const LayerVector::Visitor& visitor) { + bool childrenOnly = args.childrenOnly; + RenderAreaFuture renderAreaFuture = promise::defer([=]() -> std::unique_ptr<RenderArea> { + return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace, + childrenOnly, layerStackSpaceRect, + captureSecureLayers); + }); + + auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) { parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { if (!layer->isVisible()) { return; - } else if (childrenOnly && layer == parent.get()) { + } else if (args.childrenOnly && layer == parent.get()) { + return; + } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) { return; } @@ -5731,84 +5642,120 @@ status_t SurfaceFlinger::captureLayers( }); }; - bool outCapturedSecureLayers = false; - return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat, false, - outCapturedSecureLayers); + return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, + args.pixelFormat, args.allowProtected, captureListener); } -status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, +status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers, - sp<GraphicBuffer>* outBuffer, - const ui::PixelFormat reqPixelFormat, - bool useIdentityTransform, - bool& outCapturedSecureLayers) { + ui::Size bufferSize, ui::PixelFormat reqPixelFormat, + const bool allowProtected, + const sp<IScreenCaptureListener>& captureListener) { ATRACE_CALL(); - // TODO(b/116112787) Make buffer usage a parameter. - const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; - *outBuffer = - getFactory().createGraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(), - static_cast<android_pixel_format>(reqPixelFormat), 1, - usage, "screenshot"); + // Loop over all visible layers to see whether there's any protected layer. A protected layer is + // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer. + // A protected layer has no implication on whether it's secure, which is explicitly set by + // application to avoid being screenshot or drawn via unsecure display. + const bool supportsProtected = getRenderEngine().supportsProtectedContent(); + bool hasProtectedLayer = false; + if (allowProtected && supportsProtected) { + traverseLayers([&](Layer* layer) { + hasProtectedLayer = hasProtectedLayer || (layer->isVisible() && layer->isProtected()); + }); + } - return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform, - false /* regionSampling */, outCapturedSecureLayers); + const uint32_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE | + (hasProtectedLayer && allowProtected && supportsProtected + ? GRALLOC_USAGE_PROTECTED + : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + sp<GraphicBuffer> buffer = + getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), + static_cast<android_pixel_format>(reqPixelFormat), + 1 /* layerCount */, usage, "screenshot"); + return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, + false /* regionSampling */, captureListener); } -status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, +status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers, - const sp<GraphicBuffer>& buffer, - bool useIdentityTransform, bool regionSampling, - bool& outCapturedSecureLayers) { - const int uid = IPCThreadState::self()->getCallingUid(); - const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM; + sp<GraphicBuffer>& buffer, const bool regionSampling, + const sp<IScreenCaptureListener>& captureListener) { + ATRACE_CALL(); - status_t result; - int syncFd; - - do { - std::tie(result, syncFd) = - schedule([&] { - if (mRefreshPending) { - ATRACE_NAME("Skipping screenshot for now"); - return std::make_pair(EAGAIN, -1); - } + if (captureListener == nullptr) { + ALOGE("capture screen must provide a capture listener callback"); + return BAD_VALUE; + } - status_t result = NO_ERROR; - int fd = -1; + const int uid = IPCThreadState::self()->getCallingUid(); + const bool forSystem = uid == AID_GRAPHICS || uid == AID_SYSTEM; - Mutex::Autolock lock(mStateLock); - renderArea.render([&] { - result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(), - useIdentityTransform, forSystem, &fd, - regionSampling, outCapturedSecureLayers); - }); + static_cast<void>(schedule([=, renderAreaFuture = std::move(renderAreaFuture)]() mutable { + if (mRefreshPending) { + ALOGW("Skipping screenshot for now"); + captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, regionSampling, + captureListener); + return; + } + ScreenCaptureResults captureResults; + std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get(); + if (!renderArea) { + ALOGW("Skipping screen capture because of invalid render area."); + captureResults.result = NO_MEMORY; + captureListener->onScreenCaptureComplete(captureResults); + return; + } - return std::make_pair(result, fd); - }).get(); - } while (result == EAGAIN); + status_t result = NO_ERROR; + int syncFd = -1; + renderArea->render([&] { + result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem, &syncFd, + regionSampling, captureResults); + }); - if (result == NO_ERROR) { - sync_wait(syncFd, -1); - close(syncFd); - } + if (result == NO_ERROR) { + sync_wait(syncFd, -1); + close(syncFd); + } + captureResults.result = result; + captureListener->onScreenCaptureComplete(captureResults); + })); - return result; + return NO_ERROR; } -void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, - TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, bool useIdentityTransform, - bool regionSampling, int* outSyncFd) { +status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, + TraverseLayersFunction traverseLayers, + const sp<GraphicBuffer>& buffer, bool forSystem, + int* outSyncFd, bool regionSampling, + ScreenCaptureResults& captureResults) { ATRACE_CALL(); + traverseLayers([&](Layer* layer) { + captureResults.capturedSecureLayers = + captureResults.capturedSecureLayers || (layer->isVisible() && layer->isSecure()); + }); + + const bool useProtected = buffer->getUsage() & GRALLOC_USAGE_PROTECTED; + + // We allow the system server to take screenshots of secure layers for + // use in situations like the Screen-rotation animation and place + // the impetus on WindowManager to not persist them. + if (captureResults.capturedSecureLayers && !forSystem) { + ALOGW("FB is protected: PERMISSION_DENIED"); + return PERMISSION_DENIED; + } + + captureResults.buffer = buffer; + captureResults.capturedDataspace = renderArea.getReqDataSpace(); + const auto reqWidth = renderArea.getReqWidth(); const auto reqHeight = renderArea.getReqHeight(); const auto sourceCrop = renderArea.getSourceCrop(); const auto transform = renderArea.getTransform(); const auto rotation = renderArea.getRotationFlags(); - const auto& displayViewport = renderArea.getDisplayViewport(); + const auto& layerStackSpaceRect = renderArea.getLayerStackSpaceRect(); renderengine::DisplaySettings clientCompositionDisplay; std::vector<compositionengine::LayerFE::LayerSettings> clientCompositionLayers; @@ -5836,17 +5783,15 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, std::vector<Layer*> renderedLayers; Region clearRegion = Region::INVALID_REGION; traverseLayers([&](Layer* layer) { - const bool supportProtectedContent = false; Region clip(renderArea.getBounds()); compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{ clip, - useIdentityTransform, layer->needsFilteringForScreenshots(display.get(), transform) || renderArea.needsFiltering(), renderArea.isSecure(), - supportProtectedContent, + useProtected, clearRegion, - displayViewport, + layerStackSpaceRect, clientCompositionDisplay.outputDataspace, true, /* realContentIsVisible */ false, /* clearContent */ @@ -5882,7 +5827,7 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, // there is no need for synchronization with the GPU. base::unique_fd bufferFence; base::unique_fd drawFence; - getRenderEngine().useProtectedContext(false); + getRenderEngine().useProtectedContext(useProtected); getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayerPointers, buffer, /*useFramebufferCache=*/false, std::move(bufferFence), &drawFence); @@ -5894,30 +5839,9 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, layer->onLayerDisplayed(releaseFence); } } -} - -status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea, - TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, - bool useIdentityTransform, bool forSystem, - int* outSyncFd, bool regionSampling, - bool& outCapturedSecureLayers) { - ATRACE_CALL(); - - traverseLayers([&](Layer* layer) { - outCapturedSecureLayers = - outCapturedSecureLayers || (layer->isVisible() && layer->isSecure()); - }); + // Always switch back to unprotected context. + getRenderEngine().useProtectedContext(false); - // We allow the system server to take screenshots of secure layers for - // use in situations like the Screen-rotation animation and place - // the impetus on WindowManager to not persist them. - if (outCapturedSecureLayers && !forSystem) { - ALOGW("FB is protected: PERMISSION_DENIED"); - return PERMISSION_DENIED; - } - renderScreenImplLocked(renderArea, traverseLayers, buffer, useIdentityTransform, regionSampling, - outSyncFd); return NO_ERROR; } @@ -5943,22 +5867,25 @@ void SurfaceFlinger::State::traverseInReverseZOrder(const LayerVector::Visitor& layersSortedByZ.traverseInReverseZOrder(stateSet, visitor); } -void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& display, - const LayerVector::Visitor& visitor) { +void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const int32_t uid, + const LayerVector::Visitor& visitor) { // We loop through the first level of layers without traversing, // as we need to determine which layers belong to the requested display. for (const auto& layer : mDrawingState.layersSortedByZ) { - if (!layer->belongsToDisplay(display->getLayerStack(), false)) { + if (!layer->belongsToDisplay(layerStack)) { continue; } // relative layers are traversed in Layer::traverseInZOrder layer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { - if (!layer->belongsToDisplay(display->getLayerStack(), false)) { + if (layer->getPrimaryDisplayOnly()) { return; } if (!layer->isVisible()) { return; } + if (uid != CaptureArgs::UNSET_UID && layer->getOwnerUid() != uid) { + return; + } visitor(layer); }); } @@ -5978,16 +5905,14 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( // as well. For now, just call directly to setActiveConfigWithConstraints but ideally // it should go thru setDesiredActiveConfig, similar to primary display. ALOGV("setAllowedDisplayConfigsInternal for non-primary display"); - const auto displayId = display->getId(); - LOG_ALWAYS_FATAL_IF(!displayId); + const auto displayId = display->getPhysicalId(); hal::VsyncPeriodChangeConstraints constraints; constraints.desiredTimeNanos = systemTime(); constraints.seamlessRequired = false; hal::VsyncPeriodChangeTimeline timeline = {0, 0, 0}; - if (getHwComposer().setActiveConfigWithConstraints(*displayId, - policy->defaultConfig.value(), + if (getHwComposer().setActiveConfigWithConstraints(displayId, policy->defaultConfig.value(), constraints, &timeline) < 0) { return BAD_VALUE; } @@ -5997,9 +5922,9 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( display->setActiveConfig(policy->defaultConfig); const nsecs_t vsyncPeriod = getHwComposer() - .getConfigs(*displayId)[policy->defaultConfig.value()] + .getConfigs(displayId)[policy->defaultConfig.value()] ->getVsyncPeriod(); - mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, + mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, displayId, policy->defaultConfig, vsyncPeriod); return NO_ERROR; } @@ -6031,7 +5956,8 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()) .getVsyncPeriod(); - mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, + const auto physicalId = display->getPhysicalId(); + mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, physicalId, display->getActiveConfig(), vsyncPeriod); toggleKernelIdleTimer(); @@ -6056,12 +5982,10 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( return NO_ERROR; } -status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t defaultConfig, - float primaryRefreshRateMin, - float primaryRefreshRateMax, - float appRequestRefreshRateMin, - float appRequestRefreshRateMax) { +status_t SurfaceFlinger::setDesiredDisplayConfigSpecs( + const sp<IBinder>& displayToken, int32_t defaultConfig, bool allowGroupSwitching, + float primaryRefreshRateMin, float primaryRefreshRateMax, float appRequestRefreshRateMin, + float appRequestRefreshRateMax) { ATRACE_CALL(); if (!displayToken) { @@ -6080,6 +6004,7 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& display } else { using Policy = scheduler::RefreshRateConfigs::Policy; const Policy policy{HwcConfigIndexType(defaultConfig), + allowGroupSwitching, {primaryRefreshRateMin, primaryRefreshRateMax}, {appRequestRefreshRateMin, appRequestRefreshRateMax}}; constexpr bool kOverridePolicy = false; @@ -6091,12 +6016,10 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& display return future.get(); } -status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t* outDefaultConfig, - float* outPrimaryRefreshRateMin, - float* outPrimaryRefreshRateMax, - float* outAppRequestRefreshRateMin, - float* outAppRequestRefreshRateMax) { +status_t SurfaceFlinger::getDesiredDisplayConfigSpecs( + const sp<IBinder>& displayToken, int32_t* outDefaultConfig, bool* outAllowGroupSwitching, + float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, + float* outAppRequestRefreshRateMin, float* outAppRequestRefreshRateMax) { ATRACE_CALL(); if (!displayToken || !outDefaultConfig || !outPrimaryRefreshRateMin || @@ -6114,6 +6037,7 @@ status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& display scheduler::RefreshRateConfigs::Policy policy = mRefreshRateConfigs->getDisplayManagerPolicy(); *outDefaultConfig = policy.defaultConfig.value(); + *outAllowGroupSwitching = policy.allowGroupSwitching; *outPrimaryRefreshRateMin = policy.primaryRange.min; *outPrimaryRefreshRateMax = policy.primaryRange.max; *outAppRequestRefreshRateMin = policy.appRequestRange.min; @@ -6122,11 +6046,10 @@ status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& display } else if (display->isVirtual()) { return INVALID_OPERATION; } else { - const auto displayId = display->getId(); - LOG_FATAL_IF(!displayId); - - *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId); - auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod(); + const auto displayId = display->getPhysicalId(); + *outDefaultConfig = getHwComposer().getActiveConfigIndex(displayId); + *outAllowGroupSwitching = false; + auto vsyncPeriod = getHwComposer().getActiveConfig(displayId)->getVsyncPeriod(); *outPrimaryRefreshRateMin = 1e9f / vsyncPeriod; *outPrimaryRefreshRateMax = 1e9f / vsyncPeriod; *outAppRequestRefreshRateMin = 1e9f / vsyncPeriod; @@ -6135,10 +6058,6 @@ status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& display } } -void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() { - mFlinger->setInputWindowsFinished(); -} - wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) { Mutex::Autolock _l(mStateLock); return fromHandleLocked(handle); @@ -6209,9 +6128,6 @@ const std::unordered_map<std::string, uint32_t>& SurfaceFlinger::getGenericLayer // on the work to remove the table in that bug rather than adding more to // it. static const std::unordered_map<std::string, uint32_t> genericLayerMetadataKeyMap{ - // Note: METADATA_OWNER_UID and METADATA_WINDOW_TYPE are officially - // supported, and exposed via the - // IVrComposerClient::VrCommand::SET_LAYER_INFO command. {"org.chromium.arc.V1_0.TaskId", METADATA_TASK_ID}, {"org.chromium.arc.V1_0.CursorInfo", METADATA_MOUSE_CURSOR}, }; @@ -6263,8 +6179,8 @@ status_t SurfaceFlinger::acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) // This is a little racy, but not in a way that hurts anything. As we grab the // defaultConfig from the display manager policy, we could be setting a new display // manager policy, leaving us using a stale defaultConfig. The defaultConfig doesn't - // matter for the override policy though, since we set allowGroupSwitching to true, so - // it's not a problem. + // matter for the override policy though, since we set allowGroupSwitching to + // true, so it's not a problem. scheduler::RefreshRateConfigs::Policy overridePolicy; overridePolicy.defaultConfig = mRefreshRateConfigs->getDisplayManagerPolicy().defaultConfig; @@ -6316,11 +6232,29 @@ void SurfaceFlinger::onFrameRateFlexibilityTokenReleased() { })); } +status_t SurfaceFlinger::setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface, + int64_t frameTimelineVsyncId) { + Mutex::Autolock lock(mStateLock); + if (!authenticateSurfaceTextureLocked(surface)) { + ALOGE("Attempt to set frame timeline vsync on an unrecognized IGraphicBufferProducer"); + return BAD_VALUE; + } + + sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer(); + if (layer == nullptr) { + ALOGE("Attempt to set frame timeline vsync on a layer that no longer exists"); + return BAD_VALUE; + } + + layer->setFrameTimelineVsyncForBuffer(frameTimelineVsyncId); + return NO_ERROR; +} + void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { static_cast<void>(schedule([=] { std::unique_ptr<RefreshRateOverlay> overlay; if (enable) { - overlay = std::make_unique<RefreshRateOverlay>(*this); + overlay = std::make_unique<RefreshRateOverlay>(*this, mRefreshRateOverlaySpinner); } { @@ -6339,6 +6273,17 @@ void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { })); } +status_t SurfaceFlinger::addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& listener) { + if (!listener) { + return BAD_VALUE; + } + + mInterceptor->addTransactionTraceListener(listener); + + return NO_ERROR; +} + } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index c727574780..a821d4473f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -33,7 +33,6 @@ #include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> #include <gui/OccupancyTracker.h> -#include <input/ISetInputWindowsListener.h> #include <layerproto/LayerProtoHeader.h> #include <math/mat4.h> #include <renderengine/LayerSettings.h> @@ -53,13 +52,14 @@ #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/PowerAdvisor.h" +#include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" #include "FrameTracker.h" #include "LayerVector.h" #include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/RefreshRateStats.h" #include "Scheduler/Scheduler.h" -#include "Scheduler/VSyncModulator.h" +#include "Scheduler/VsyncModulator.h" #include "SurfaceFlingerFactory.h" #include "SurfaceTracing.h" #include "TracedOrdinal.h" @@ -89,15 +89,24 @@ namespace android { class Client; class EventThread; class HWComposer; +struct SetInputWindowsListener; class IGraphicBufferProducer; -class IInputFlinger; class Layer; class MessageBase; class RefreshRateOverlay; class RegionSamplingThread; +class RenderArea; class TimeStats; class FrameTracer; +namespace frametimeline { +class FrameTimeline; +} + +namespace os { + class IInputFlinger; +} + namespace compositionengine { class DisplaySurface; class OutputLayer; @@ -109,10 +118,6 @@ namespace renderengine { class RenderEngine; } // namespace renderengine -namespace dvr { -class VrFlinger; -} // namespace dvr - enum { eTransactionNeeded = 0x01, eTraversalNeeded = 0x02, @@ -179,8 +184,15 @@ class SurfaceFlinger : public BnSurfaceComposer, private HWC2::ComposerCallback, private ISchedulerCallback { public: - SurfaceFlingerBE& getBE() { return mBE; } - const SurfaceFlingerBE& getBE() const { return mBE; } + struct SkipInitializationTag {}; + + SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API; + explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API; + + // set main thread scheduling policy + static status_t setSchedFifo(bool enabled) ANDROID_API; + + static char const* getServiceName() ANDROID_API { return "SurfaceFlinger"; } // This is the phase offset in nanoseconds of the software vsync event // relative to the vsync event reported by HWComposer. The software vsync @@ -208,7 +220,7 @@ public: // If fences from sync Framework are supported. static bool hasSyncFramework; - // The offset in nanoseconds to use when DispSync timestamps present fence + // The offset in nanoseconds to use when VsyncController timestamps present fence // signaling time. static int64_t dispSyncPresentTimeOffset; @@ -259,17 +271,7 @@ public: // overhead that is caused by reading from sysprop. static bool useFrameRateApi; - // set main thread scheduling policy - static status_t setSchedFifo(bool enabled) ANDROID_API; - - static char const* getServiceName() ANDROID_API { - return "SurfaceFlinger"; - } - - struct SkipInitializationTag {}; static constexpr SkipInitializationTag SkipInitialization; - SurfaceFlinger(surfaceflinger::Factory&, SkipInitializationTag) ANDROID_API; - explicit SurfaceFlinger(surfaceflinger::Factory&) ANDROID_API; // must be called before clients can connect void init() ANDROID_API; @@ -277,6 +279,9 @@ public: // starts SurfaceFlinger main loop in the current thread void run() ANDROID_API; + SurfaceFlingerBE& getBE() { return mBE; } + const SurfaceFlingerBE& getBE() const { return mBE; } + // Schedule an asynchronous or synchronous task on the main thread. template <typename F, typename T = std::invoke_result_t<F>> [[nodiscard]] std::future<T> schedule(F&&); @@ -296,17 +301,10 @@ public: // utility function to delete a texture on the main thread void deleteTextureAsync(uint32_t texture); - // enable/disable h/w composer event - // TODO: this should be made accessible only to EventThread - void setPrimaryVsyncEnabled(bool enabled); - - // main thread function to enable/disable h/w composer event - void setPrimaryVsyncEnabledInternal(bool enabled) REQUIRES(mStateLock); - // called on the main thread by MessageQueue when an internal message // is received // TODO: this should be made accessible only to MessageQueue - void onMessageReceived(int32_t what, nsecs_t expectedVSyncTime); + void onMessageReceived(int32_t what, int64_t vsyncId, nsecs_t expectedVSyncTime); renderengine::RenderEngine& getRenderEngine() const; @@ -335,6 +333,25 @@ public: // If set, disables reusing client composition buffers. This can be set by // debug.sf.disable_client_composition_cache bool mDisableClientCompositionCache = false; + void setInputWindowsFinished(); + +protected: + // We're reference counted, never destroy SurfaceFlinger directly + virtual ~SurfaceFlinger(); + + virtual uint32_t setClientStateLocked( + int64_t frameTimelineVsyncId, const ComposerState& composerState, + int64_t desiredPresentTime, int64_t postTime, bool privileged, + std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) + REQUIRES(mStateLock); + virtual void commitTransactionLocked(); + + // Used internally by computeLayerBounds() to gets the clip rectangle to use for the + // root layers on a particular display in layer-coordinate space. The + // layers (and effectively their children) will be clipped against this + // rectangle. The base behavior is to clip to the visible region of the + // display. + virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const; private: friend class BufferLayer; @@ -351,21 +368,18 @@ private: friend class TestableSurfaceFlinger; friend class TransactionApplicationTest; + using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; + using VsyncModulator = scheduler::VsyncModulator; + using TransactionSchedule = scheduler::TransactionSchedule; + using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>; + using RenderAreaFuture = std::future<std::unique_ptr<RenderArea>>; + using DumpArgs = Vector<String16>; + using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>; + // This value is specified in number of frames. Log frame stats at most // every half hour. enum { LOG_FRAME_STATS_PERIOD = 30*60*60 }; - static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB - -protected: - // We're reference counted, never destroy SurfaceFlinger directly - virtual ~SurfaceFlinger(); - -private: - /* ------------------------------------------------------------------------ - * Internal data structures - */ - class State { public: explicit State(LayerVector::StateSet set) : stateSet(set), layersSortedByZ(set) {} @@ -397,29 +411,119 @@ private: void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const; }; - /* ------------------------------------------------------------------------ - * IBinder interface - */ + struct ActiveConfigInfo { + HwcConfigIndexType configId; + Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None; + + bool operator!=(const ActiveConfigInfo& other) const { + return configId != other.configId || event != other.event; + } + }; + + enum class BootStage { + BOOTLOADER, + BOOTANIMATION, + FINISHED, + }; + + struct HotplugEvent { + hal::HWDisplayId hwcDisplayId; + hal::Connection connection = hal::Connection::INVALID; + }; + + struct TransactionState { + TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates, + const Vector<DisplayState>& displayStates, uint32_t transactionFlags, + int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, + int64_t postTime, bool privileged, bool hasListenerCallbacks, + std::vector<ListenerCallbacks> listenerCallbacks, int originPid, + int originUid, uint64_t transactionId) + : frameTimelineVsyncId(frameTimelineVsyncId), + states(composerStates), + displays(displayStates), + flags(transactionFlags), + desiredPresentTime(desiredPresentTime), + buffer(uncacheBuffer), + postTime(postTime), + privileged(privileged), + hasListenerCallbacks(hasListenerCallbacks), + listenerCallbacks(listenerCallbacks), + originPid(originPid), + originUid(originUid), + id(transactionId) {} + + int64_t frameTimelineVsyncId; + Vector<ComposerState> states; + Vector<DisplayState> displays; + uint32_t flags; + const int64_t desiredPresentTime; + client_cache_t buffer; + const int64_t postTime; + bool privileged; + bool hasListenerCallbacks; + std::vector<ListenerCallbacks> listenerCallbacks; + int originPid; + int originUid; + uint64_t id; + }; + + template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr> + static Dumper dumper(F&& dump) { + using namespace std::placeholders; + return std::bind(std::forward<F>(dump), _3); + } + + template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> + Dumper dumper(F dump) { + using namespace std::placeholders; + return std::bind(dump, this, _3); + } + + template <typename F> + Dumper argsDumper(F dump) { + using namespace std::placeholders; + return std::bind(dump, this, _1, _3); + } + + template <typename F> + Dumper protoDumper(F dump) { + using namespace std::placeholders; + return std::bind(dump, this, _1, _2, _3); + } + + template <typename... Args, + typename Handler = VsyncModulator::VsyncConfigOpt (VsyncModulator::*)(Args...)> + void modulateVsync(Handler handler, Args... args) { + if (const auto config = (*mVsyncModulator.*handler)(args...)) { + const auto vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); + setVsyncConfig(*config, vsyncPeriod); + } + } + + static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB + // Maximum allowed number of display frames that can be set through backdoor + static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048; + + // Implements IBinder. status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override; status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); } bool callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache = true) EXCLUDES(mStateLock); - /* ------------------------------------------------------------------------ - * ISurfaceComposer interface - */ + // Implements ISurfaceComposer sp<ISurfaceComposerClient> createConnection() override; sp<IBinder> createDisplay(const String8& displayName, bool secure) override; void destroyDisplay(const sp<IBinder>& displayToken) override; std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override; sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override; - void setTransactionState(const Vector<ComposerState>& state, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, - const InputWindowCommands& inputWindowCommands, - int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, - bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks) override; + status_t setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state, + const Vector<DisplayState>& displays, uint32_t flags, + const sp<IBinder>& applyToken, + const InputWindowCommands& inputWindowCommands, + int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, + bool hasListenerCallbacks, + const std::vector<ListenerCallbacks>& listenerCallbacks, + uint64_t transactionId) override; void bootFinished() override; bool authenticateSurfaceTexture( const sp<IGraphicBufferProducer>& bufferProducer) const override; @@ -428,19 +532,12 @@ private: ISurfaceComposer::VsyncSource vsyncSource = eVsyncSourceApp, ISurfaceComposer::ConfigChanged configChanged = ISurfaceComposer::eConfigChangedSuppress) override; - status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer, - bool& outCapturedSecureLayers, ui::Dataspace reqDataspace, - ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, - uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ui::Rotation rotation, bool captureSecureLayers) override; - status_t captureScreen(uint64_t displayOrLayerStack, ui::Dataspace* outDataspace, - sp<GraphicBuffer>* outBuffer) override; - status_t captureLayers( - const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer, - const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, - const Rect& sourceCrop, - const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>& exclude, - float frameScale, bool childrenOnly) override; + status_t captureDisplay(const DisplayCaptureArgs& args, + const sp<IScreenCaptureListener>& captureListener) override; + status_t captureDisplay(uint64_t displayOrLayerStack, + const sp<IScreenCaptureListener>& captureListener) override; + status_t captureLayers(const LayerCaptureArgs& args, + 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; @@ -486,11 +583,12 @@ private: const sp<IRegionSamplingListener>& listener) override; status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override; status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId, - float primaryRefreshRateMin, float primaryRefreshRateMax, + bool allowGroupSwitching, float primaryRefreshRateMin, + float primaryRefreshRateMax, float appRequestRefreshRateMin, float appRequestRefreshRateMax) override; status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, - int32_t* outDefaultConfig, + int32_t* outDefaultConfig, bool* outAllowGroupSwitching, float* outPrimaryRefreshRateMin, float* outPrimaryRefreshRateMax, float* outAppRequestRefreshRateMin, @@ -498,23 +596,26 @@ private: status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, bool* outSupport) const override; status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) override; - status_t notifyPowerHint(int32_t hintId) override; + status_t notifyPowerBoost(int32_t boostId) override; status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor, float lightPosY, float lightPosZ, float lightRadius) override; status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate, int8_t compatibility) override; status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override; - /* ------------------------------------------------------------------------ - * DeathRecipient interface - */ + + status_t setFrameTimelineVsync(const sp<IGraphicBufferProducer>& surface, + int64_t frameTimelineVsyncId) override; + + status_t addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& listener) override; + + // Implements IBinder::DeathRecipient. void binderDied(const wp<IBinder>& who) override; - /* ------------------------------------------------------------------------ - * RefBase interface - */ + // Implements RefBase. void onFirstRef() override; - /* ------------------------------------------------------------------------ + /* * HWC2::ComposerCallback / HWComposer::EventHandler interface */ void onVsyncReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId, int64_t timestamp, @@ -527,11 +628,15 @@ private: const hal::VsyncPeriodChangeTimeline& updatedTimeline) override; void onSeamlessPossible(int32_t sequenceId, hal::HWDisplayId display) override; - /* ------------------------------------------------------------------------ + /* * ISchedulerCallback */ + + // Toggles hardware VSYNC by calling into HWC. + void setVsyncEnabled(bool) override; + // Initiates a refresh rate change to be applied on invalidate. void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override; - // force full composition on all displays without resetting the scheduler idle timer. + // Forces full composition on all displays without resetting the scheduler idle timer. void repaintEverythingForHWC() override; // Called when kernel idle timer has expired. Used to update the refresh rate overlay. void kernelTimerChanged(bool expired) override; @@ -542,7 +647,10 @@ private: bool mKernelIdleTimerEnabled = false; // Keeps track of whether the kernel timer is supported on the SF side. bool mSupportKernelIdleTimer = false; - /* ------------------------------------------------------------------------ + // Show spinner with refresh rate overlay + bool mRefreshRateOverlaySpinner = false; + + /* * Message handling */ // Can only be called from the main thread or with mStateLock held @@ -551,17 +659,6 @@ private: void signalLayerUpdate(); void signalRefresh(); - using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; - - struct ActiveConfigInfo { - HwcConfigIndexType configId; - Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None; - - bool operator!=(const ActiveConfigInfo& other) const { - return configId != other.configId || event != other.event; - } - }; - // called on the main thread in response to initializeDisplays() void onInitializeDisplays() REQUIRES(mStateLock); // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig. @@ -587,7 +684,7 @@ private: // Handle the INVALIDATE message queue event, latching new buffers and applying // incoming transactions - void onMessageInvalidate(nsecs_t expectedVSyncTime); + void onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncTime); // Returns whether the transaction actually modified any state bool handleMessageTransaction(); @@ -605,9 +702,11 @@ private: void updateInputFlinger(); void updateInputWindowInfo(); void commitInputWindowCommands() REQUIRES(mStateLock); - void setInputWindowsFinished(); void updateCursorAsync(); - void initScheduler(DisplayId primaryDisplayId); + + void initScheduler(PhysicalDisplayId primaryDisplayId); + void updatePhaseConfiguration(const RefreshRate&); + void setVsyncConfig(const VsyncModulator::VsyncConfig&, nsecs_t vsyncPeriod); /* handlePageFlip - latch a new buffer if available and compute the dirty * region. Returns whether a new buffer has been latched, i.e., whether it @@ -615,16 +714,17 @@ private: */ bool handlePageFlip(); - /* ------------------------------------------------------------------------ + /* * Transactions */ - void applyTransactionState(const Vector<ComposerState>& state, + void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged, bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, + int originPid, int originUid, uint64_t transactionId, bool isMainThread = false) REQUIRES(mStateLock); // Returns true if at least one transaction was flushed bool flushTransactionQueues(); @@ -640,7 +740,7 @@ private: // but there is no need to try and wake up immediately to do it. Rather we rely on // onFrameAvailable or another layer update to wake us up. void setTraversalNeeded(); - uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart); + uint32_t setTransactionFlags(uint32_t flags, TransactionSchedule); void commitTransaction() REQUIRES(mStateLock); void commitOffscreenLayers(); bool transactionIsReadyToBeApplied(int64_t desiredPresentTime, @@ -648,30 +748,14 @@ private: uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) REQUIRES(mStateLock); - -protected: - virtual uint32_t setClientStateLocked( - const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime, - bool privileged, - std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) - REQUIRES(mStateLock); - virtual void commitTransactionLocked(); - - // Used internally by computeLayerBounds() to gets the clip rectangle to use for the - // root layers on a particular display in layer-coordinate space. The - // layers (and effectively their children) will be clipped against this - // rectangle. The base behavior is to clip to the visible region of the - // display. - virtual FloatRect getLayerClipBoundsForDisplay(const DisplayDevice&) const; - -private: - /* ------------------------------------------------------------------------ + /* * Layer management */ status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, LayerMetadata metadata, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, - const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer = nullptr, + const sp<IBinder>& parentHandle, int32_t* outLayerId, + const sp<Layer>& parentLayer = nullptr, uint32_t* outTransformHint = nullptr); status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w, @@ -692,7 +776,7 @@ private: sp<IBinder>* outHandle, sp<Layer>* outLayer); status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle, - sp<IBinder>* outHandle); + sp<IBinder>* outHandle, int32_t* outLayerId); std::string getUniqueLayerName(const char* name); @@ -711,47 +795,31 @@ private: // Traverse through all the layers and compute and cache its bounds. void computeLayerBounds(); - /* ------------------------------------------------------------------------ - * Boot animation, on/off animations and screen capture - */ - + // Boot animation, on/off animations and screen capture void startBootAnim(); - using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>; + status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize, + ui::PixelFormat, const bool allowProtected, + const sp<IScreenCaptureListener>&); + status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp<GraphicBuffer>&, + bool regionSampling, const sp<IScreenCaptureListener>&); + status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction, + const sp<GraphicBuffer>&, bool forSystem, int* outSyncFd, + bool regionSampling, ScreenCaptureResults&); - void renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, bool useIdentityTransform, - bool regionSampling, int* outSyncFd); - status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, - sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat, - bool useIdentityTransform, bool& outCapturedSecureLayers); - status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, - const sp<GraphicBuffer>& buffer, bool useIdentityTransform, - bool regionSampling, bool& outCapturedSecureLayers); sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock); sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock); - status_t captureScreenImplLocked(const RenderArea& renderArea, - TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, bool useIdentityTransform, - bool forSystem, int* outSyncFd, bool regionSampling, - bool& outCapturedSecureLayers); - void traverseLayersInDisplay(const sp<const DisplayDevice>& display, - const LayerVector::Visitor& visitor); - sp<StartPropertySetThread> mStartPropertySetThread; + // If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a + // matching ownerUid + void traverseLayersInLayerStack(ui::LayerStack, const int32_t uid, const LayerVector::Visitor&); - /* ------------------------------------------------------------------------ - * Properties - */ void readPersistentProperties(); - /* ------------------------------------------------------------------------ - * EGL - */ size_t getMaxTextureSize() const; size_t getMaxViewportDims() const; - /* ------------------------------------------------------------------------ + /* * Display and layer stack management */ // called when starting, or restarting after system_server death @@ -783,13 +851,11 @@ private: return getDefaultDisplayDeviceLocked(); } - std::optional<DeviceProductInfo> getDeviceProductInfoLocked(const DisplayDevice&) const; - // mark a region of a layer stack dirty. this updates the dirty // region of all screens presenting this layer stack. void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty); - /* ------------------------------------------------------------------------ + /* * H/W composer */ @@ -798,8 +864,7 @@ private: // The following thread safety rules apply when accessing mHwc, either // directly or via getHwComposer(): // - // 1. When recreating mHwc, acquire mStateLock. We currently recreate mHwc - // only when switching into and out of vr. Recreating mHwc must only be + // 1. When recreating mHwc, acquire mStateLock. Recreating mHwc must only be // done on the main thread. // // 2. When accessing mHwc on the main thread, it's not necessary to acquire @@ -815,7 +880,7 @@ private: // acquiring mStateLock. HWComposer& getHwComposer() const; - /* ------------------------------------------------------------------------ + /* * Compositing */ void invalidateHwcGeometry(); @@ -829,7 +894,7 @@ private: void postFrame(); - /* ------------------------------------------------------------------------ + /* * Display management */ sp<DisplayDevice> setupNewDisplayDeviceInternal( @@ -849,15 +914,14 @@ private: void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected); - /* ------------------------------------------------------------------------ - * VSync + /* + * VSYNC */ nsecs_t getVsyncPeriodFromHWC() const REQUIRES(mStateLock); // Sets the refresh rate by switching active configs, if they are available for // the desired refresh rate. - void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event) - REQUIRES(mStateLock); + void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent) REQUIRES(mStateLock); bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock); @@ -884,13 +948,14 @@ private: /* * Display identification */ - sp<IBinder> getPhysicalDisplayTokenLocked(DisplayId displayId) const REQUIRES(mStateLock) { + sp<IBinder> getPhysicalDisplayTokenLocked(PhysicalDisplayId displayId) const + REQUIRES(mStateLock) { const auto it = mPhysicalDisplayTokens.find(displayId); return it != mPhysicalDisplayTokens.end() ? it->second : nullptr; } - std::optional<DisplayId> getPhysicalDisplayIdLocked(const sp<IBinder>& displayToken) const - REQUIRES(mStateLock) { + std::optional<PhysicalDisplayId> getPhysicalDisplayIdLocked( + const sp<IBinder>& displayToken) const REQUIRES(mStateLock) { for (const auto& [id, token] : mPhysicalDisplayTokens) { if (token == displayToken) { return id; @@ -905,7 +970,7 @@ private: return displayId ? getPhysicalDisplayTokenLocked(*displayId) : nullptr; } - std::optional<DisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) { + std::optional<PhysicalDisplayId> getInternalDisplayIdLocked() const REQUIRES(mStateLock) { const auto hwcDisplayId = getHwComposer().getInternalHwcDisplayId(); return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt; } @@ -913,33 +978,6 @@ private: /* * Debugging & dumpsys */ - using DumpArgs = Vector<String16>; - using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>; - - template <typename F, std::enable_if_t<!std::is_member_function_pointer_v<F>>* = nullptr> - static Dumper dumper(F&& dump) { - using namespace std::placeholders; - return std::bind(std::forward<F>(dump), _3); - } - - template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr> - Dumper dumper(F dump) { - using namespace std::placeholders; - return std::bind(dump, this, _3); - } - - template <typename F> - Dumper argsDumper(F dump) { - using namespace std::placeholders; - return std::bind(dump, this, _1, _3); - } - - template <typename F> - Dumper protoDumper(F dump) { - using namespace std::placeholders; - return std::bind(dump, this, _1, _2, _3); - } - void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); void appendSfConfigString(std::string& result) const; @@ -947,6 +985,7 @@ private: void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); void clearStatsLocked(const DumpArgs& args, std::string& result); void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const; + void dumpFrameTimeline(const DumpArgs& args, std::string& result) const; void logFrameStats(); void dumpVSync(std::string& result) const REQUIRES(mStateLock); @@ -983,20 +1022,28 @@ private: void onFrameRateFlexibilityTokenReleased(); - /* ------------------------------------------------------------------------ - * VrFlinger - */ - void resetDisplayState() REQUIRES(mStateLock); + void updateColorMatrixLocked(); - // Check to see if we should handoff to vr flinger. - void updateVrFlinger(); + // Verify that transaction is being called by an approved process: + // either AID_GRAPHICS or AID_SYSTEM. + status_t CheckTransactCodeCredentials(uint32_t code); - void updateColorMatrixLocked(); + /* + * Generic Layer Metadata + */ + const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const; - /* ------------------------------------------------------------------------ - * Attributes + /* + * Misc */ + std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) { + std::lock_guard<std::mutex> lock(mActiveConfigLock); + if (mDesiredActiveConfigChanged) return mDesiredActiveConfig; + return std::nullopt; + } + + sp<StartPropertySetThread> mStartPropertySetThread; surfaceflinger::Factory& mFactory; // access must be protected by mStateLock @@ -1017,6 +1064,10 @@ private: // Can't be unordered_set because wp<> isn't hashable std::set<wp<IBinder>> mGraphicBufferProducerList; size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS; + // If there are more GraphicBufferProducers tracked by SurfaceFlinger than + // this threshold, then begin logging. + size_t mGraphicBufferProducerListSizeLogThreshold = + static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS)); void removeGraphicBufferProducerAsync(const wp<IBinder>&); @@ -1039,7 +1090,11 @@ private: bool mInputInfoChanged = false; bool mGeometryInvalid = false; bool mAnimCompositionPending = false; - std::vector<sp<Layer>> mLayersWithQueuedFrames; + + // Tracks layers that have pending frames which are candidates for being + // latched. Because this contains a set of raw layer pointers, can only be + // mutated on the main thread. + std::unordered_set<Layer*> mLayersWithQueuedFrames; // Tracks layers that need to update a display's dirty region. std::vector<sp<Layer>> mLayersPendingRefresh; std::array<sp<Fence>, 2> mPreviousPresentFences = {Fence::NO_FENCE, Fence::NO_FENCE}; @@ -1054,23 +1109,17 @@ private: // did not change. bool mReusedClientComposition = false; - enum class BootStage { - BOOTLOADER, - BOOTANIMATION, - FINISHED, - }; BootStage mBootStage = BootStage::BOOTLOADER; - struct HotplugEvent { - hal::HWDisplayId hwcDisplayId; - hal::Connection connection = hal::Connection::INVALID; - }; std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mStateLock); // this may only be written from the main thread with mStateLock held // it may be read from other threads with mStateLock held std::map<wp<IBinder>, sp<DisplayDevice>> mDisplays GUARDED_BY(mStateLock); - std::unordered_map<DisplayId, sp<IBinder>> mPhysicalDisplayTokens GUARDED_BY(mStateLock); + std::unordered_map<PhysicalDisplayId, sp<IBinder>> mPhysicalDisplayTokens + GUARDED_BY(mStateLock); + + RandomDisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator; std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock); @@ -1080,18 +1129,18 @@ private: bool mDebugDisableTransformHint = false; volatile nsecs_t mDebugInTransaction = 0; bool mForceFullDamage = false; - bool mPropagateBackpressure = true; bool mPropagateBackpressureClientComposition = false; - std::unique_ptr<SurfaceInterceptor> mInterceptor; + sp<SurfaceInterceptor> mInterceptor; SurfaceTracing mTracing{*this}; std::mutex mTracingLock; bool mTracingEnabled = false; - bool mAddCompositionStateToTrace = false; + bool mTracePostComposition = false; std::atomic<bool> mTracingEnabledChanged = false; const std::shared_ptr<TimeStats> mTimeStats; const std::unique_ptr<FrameTracer> mFrameTracer; + const std::unique_ptr<frametimeline::FrameTimeline> mFrameTimeline; bool mUseHwcVirtualDisplays = false; // If blurs should be enabled on this device. bool mSupportsBlur = false; @@ -1127,35 +1176,9 @@ private: uint32_t mTexturePoolSize = 0; std::vector<uint32_t> mTexturePool; - struct TransactionState { - TransactionState(const Vector<ComposerState>& composerStates, - const Vector<DisplayState>& displayStates, uint32_t transactionFlags, - int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, - int64_t postTime, bool privileged, bool hasListenerCallbacks, - std::vector<ListenerCallbacks> listenerCallbacks) - : states(composerStates), - displays(displayStates), - flags(transactionFlags), - desiredPresentTime(desiredPresentTime), - buffer(uncacheBuffer), - postTime(postTime), - privileged(privileged), - hasListenerCallbacks(hasListenerCallbacks), - listenerCallbacks(listenerCallbacks) {} - - Vector<ComposerState> states; - Vector<DisplayState> displays; - uint32_t flags; - const int64_t desiredPresentTime; - client_cache_t buffer; - const int64_t postTime; - bool privileged; - bool hasListenerCallbacks; - std::vector<ListenerCallbacks> listenerCallbacks; - }; std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues; - /* ------------------------------------------------------------------------ + /* * Feature prototyping */ @@ -1164,10 +1187,6 @@ private: std::atomic<size_t> mNumLayers = 0; - // Verify that transaction is being called by an approved process: - // either AID_GRAPHICS or AID_SYSTEM. - status_t CheckTransactCodeCredentials(uint32_t code); - // to linkToDeath sp<IBinder> mWindowManager; // We want to avoid multiple calls to BOOT_FINISHED as they come in on @@ -1175,9 +1194,6 @@ private: // to mWindowManager or mInputFlinger std::atomic<bool> mBootFinished = false; - std::unique_ptr<dvr::VrFlinger> mVrFlinger; - std::atomic<bool> mVrFlingerRequestsDisplay = false; - static bool useVrFlinger; std::thread::id mMainThreadId = std::this_thread::get_id(); DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::kEnhanced; @@ -1198,7 +1214,7 @@ private: SurfaceFlingerBE mBE; std::unique_ptr<compositionengine::CompositionEngine> mCompositionEngine; - /* ------------------------------------------------------------------------ + /* * Scheduler */ std::unique_ptr<Scheduler> mScheduler; @@ -1206,10 +1222,10 @@ private: scheduler::ConnectionHandle mSfConnectionHandle; // Stores phase offsets configured per refresh rate. - std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration; + std::unique_ptr<scheduler::VsyncConfiguration> mVsyncConfiguration; - // Optional to defer construction until scheduler connections are created. - std::optional<scheduler::VSyncModulator> mVSyncModulator; + // Optional to defer construction until PhaseConfiguration is created. + std::optional<scheduler::VsyncModulator> mVsyncModulator; std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; @@ -1217,21 +1233,6 @@ private: std::atomic<nsecs_t> mExpectedPresentTime = 0; hal::Vsync mHWCVsyncPendingState = hal::Vsync::DISABLE; - /* ------------------------------------------------------------------------ - * Generic Layer Metadata - */ - const std::unordered_map<std::string, uint32_t>& getGenericLayerMetadataKeyMap() const; - - /* ------------------------------------------------------------------------ - * Misc - */ - - std::optional<ActiveConfigInfo> getDesiredActiveConfig() EXCLUDES(mActiveConfigLock) { - std::lock_guard<std::mutex> lock(mActiveConfigLock); - if (mDesiredActiveConfigChanged) return mDesiredActiveConfig; - return std::nullopt; - } - std::mutex mActiveConfigLock; // This bit is set once we start setting the config. We read from this bit during the // process. If at the end, this bit is different than mDesiredActiveConfig, we restart @@ -1252,21 +1253,12 @@ private: const float mInternalDisplayDensity; const float mEmulatedDisplayDensity; - sp<IInputFlinger> mInputFlinger; + sp<os::IInputFlinger> mInputFlinger; InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock); // Should only be accessed by the main thread. InputWindowCommands mInputWindowCommands; - struct SetInputWindowsListener : BnSetInputWindowsListener { - explicit SetInputWindowsListener(sp<SurfaceFlinger> flinger) - : mFlinger(std::move(flinger)) {} - - void onSetInputWindowsFinished() override; - - const sp<SurfaceFlinger> mFlinger; - }; - - const sp<SetInputWindowsListener> mSetInputWindowsListener = new SetInputWindowsListener(this); + sp<SetInputWindowsListener> mSetInputWindowsListener; bool mPendingSyncInputWindows GUARDED_BY(mStateLock) = false; Hwc2::impl::PowerAdvisor mPowerAdvisor; diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp index ddd20a55e6..9a8deae67d 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp @@ -37,25 +37,15 @@ #include "SurfaceInterceptor.h" #include "DisplayHardware/ComposerHal.h" -#include "Scheduler/DispSync.h" -#include "Scheduler/EventControlThread.h" #include "Scheduler/MessageQueue.h" -#include "Scheduler/PhaseOffsets.h" #include "Scheduler/Scheduler.h" +#include "Scheduler/VsyncConfiguration.h" +#include "Scheduler/VsyncController.h" namespace android::surfaceflinger { DefaultFactory::~DefaultFactory() = default; -std::unique_ptr<DispSync> DefaultFactory::createDispSync(const char* name, bool hasSyncFramework) { - return std::make_unique<android::impl::DispSync>(name, hasSyncFramework); -} - -std::unique_ptr<EventControlThread> DefaultFactory::createEventControlThread( - SetVSyncEnabled setVSyncEnabled) { - return std::make_unique<android::impl::EventControlThread>(std::move(setVSyncEnabled)); -} - std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) { return std::make_unique<android::impl::HWComposer>(serviceName); } @@ -64,26 +54,22 @@ std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue() { return std::make_unique<android::impl::MessageQueue>(); } -std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration( +std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration( const scheduler::RefreshRateConfigs& refreshRateConfigs) { if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) { - return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs); + return std::make_unique<scheduler::impl::WorkDuration>(refreshRateConfigs); } else { return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs); } } std::unique_ptr<Scheduler> DefaultFactory::createScheduler( - SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs, - ISchedulerCallback& schedulerCallback) { - return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback, - property_get_bool("debug.sf.use_content_detection_v2", true), - sysprop::use_content_detection_for_refresh_rate(false)); + const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback) { + return std::make_unique<Scheduler>(configs, callback); } -std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor( - SurfaceFlinger* flinger) { - return std::make_unique<android::impl::SurfaceInterceptor>(flinger); +sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() { + return new android::impl::SurfaceInterceptor(); } sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread( diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h index bd40cfbcf2..40774ef108 100644 --- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h @@ -26,16 +26,13 @@ class DefaultFactory : public surfaceflinger::Factory { public: virtual ~DefaultFactory(); - std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override; - std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override; std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override; std::unique_ptr<MessageQueue> createMessageQueue() override; - std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration( + std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( const scheduler::RefreshRateConfigs&) override; - std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled, - const scheduler::RefreshRateConfigs&, + std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&) override; - std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override; + sp<SurfaceInterceptor> createSurfaceInterceptor() override; sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override; sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override; sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h index 6f4fcc6900..2dd563b416 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -34,13 +34,10 @@ class BufferLayerConsumer; class EffectLayer; class ContainerLayer; class DisplayDevice; -class DispSync; -class EventControlThread; class GraphicBuffer; class HWComposer; class IGraphicBufferConsumer; class IGraphicBufferProducer; -class ISchedulerCallback; class Layer; class MessageQueue; class Scheduler; @@ -49,6 +46,7 @@ class SurfaceFlinger; class SurfaceInterceptor; struct DisplayDeviceCreationArgs; +struct ISchedulerCallback; struct LayerCreationArgs; namespace compositionengine { @@ -56,7 +54,8 @@ class CompositionEngine; } // namespace compositionengine namespace scheduler { -class PhaseConfiguration; +class VsyncConfiguration; +class VsyncController; class RefreshRateConfigs; } // namespace scheduler @@ -68,18 +67,13 @@ class NativeWindowSurface; // of each interface. class Factory { public: - using SetVSyncEnabled = std::function<void(bool)>; - - virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0; - virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0; virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0; virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0; - virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration( + virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( const scheduler::RefreshRateConfigs&) = 0; - virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled, - const scheduler::RefreshRateConfigs&, + virtual std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&) = 0; - virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0; + virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0; virtual sp<StartPropertySetThread> createStartPropertySetThread( bool timestampPropertyValue) = 0; diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 9d78702a08..97725ec309 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -371,5 +371,10 @@ DisplayPrimaries getDisplayNativePrimaries() { return primaries; } +bool update_device_product_info_on_hotplug_reconnect(bool defaultValue) { + return SurfaceFlingerProperties::update_device_product_info_on_hotplug_reconnect().value_or( + defaultValue); +} + } // namespace sysprop } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index c63adfe3c2..37a6b4011c 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -95,6 +95,9 @@ bool use_frame_rate_api(bool defaultValue); int32_t display_update_imminent_timeout_ms(int32_t defaultValue); android::ui::DisplayPrimaries getDisplayNativePrimaries(); + +bool update_device_product_info_on_hotplug_reconnect(bool defaultValue); + } // namespace sysprop } // namespace android #endif // SURFACEFLINGERPROPERTIES_H_ diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 80102bdbb6..354892386b 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -40,9 +40,22 @@ SurfaceInterceptor::~SurfaceInterceptor() = default; namespace impl { -SurfaceInterceptor::SurfaceInterceptor(SurfaceFlinger* flinger) - : mFlinger(flinger) -{ +void SurfaceInterceptor::addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& listener) { + sp<IBinder> asBinder = IInterface::asBinder(listener); + + std::scoped_lock lock(mListenersMutex); + + asBinder->linkToDeath(this); + + listener->onToggled(mEnabled); // notifies of current state + + mTraceToggledListeners.emplace(asBinder, listener); +} + +void SurfaceInterceptor::binderDied(const wp<IBinder>& who) { + std::scoped_lock lock(mListenersMutex); + mTraceToggledListeners.erase(who); } void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers, @@ -52,8 +65,14 @@ void SurfaceInterceptor::enable(const SortedVector<sp<Layer>>& layers, return; } ATRACE_CALL(); + { + std::scoped_lock lock(mListenersMutex); + for (const auto& [_, listener] : mTraceToggledListeners) { + listener->onToggled(true); + } + } mEnabled = true; - std::lock_guard<std::mutex> protoGuard(mTraceMutex); + std::scoped_lock<std::mutex> protoGuard(mTraceMutex); saveExistingDisplaysLocked(displays); saveExistingSurfacesLocked(layers); } @@ -63,8 +82,14 @@ void SurfaceInterceptor::disable() { return; } ATRACE_CALL(); - std::lock_guard<std::mutex> protoGuard(mTraceMutex); + { + std::scoped_lock lock(mListenersMutex); + for (const auto& [_, listener] : mTraceToggledListeners) { + listener->onToggled(false); + } + } mEnabled = false; + std::scoped_lock<std::mutex> protoGuard(mTraceMutex); status_t err(writeProtoFileLocked()); ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied"); ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields"); @@ -115,12 +140,12 @@ void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment, addCropLocked(transaction, layerId, layer->mCurrentState.crop_legacy); addCornerRadiusLocked(transaction, layerId, layer->mCurrentState.cornerRadius); addBackgroundBlurRadiusLocked(transaction, layerId, layer->mCurrentState.backgroundBlurRadius); + addBlurRegionsLocked(transaction, layerId, layer->mCurrentState.blurRegions); if (layer->mCurrentState.barrierLayer_legacy != nullptr) { addDeferTransactionLocked(transaction, layerId, layer->mCurrentState.barrierLayer_legacy.promote(), - layer->mCurrentState.frameNumber_legacy); + layer->mCurrentState.barrierFrameNumber); } - addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode()); addFlagsLocked(transaction, layerId, layer->mCurrentState.flags, layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque | layer_state_t::eLayerSecure); @@ -143,7 +168,7 @@ void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment, addDisplayLayerStackLocked(transaction, display.sequenceId, display.layerStack); addDisplaySizeLocked(transaction, display.sequenceId, display.width, display.height); addDisplayProjectionLocked(transaction, display.sequenceId, toRotationInt(display.orientation), - display.viewport, display.frame); + display.layerStackSpaceRect, display.orientedDisplaySpaceRect); } status_t SurfaceInterceptor::writeProtoFileLocked() { @@ -221,6 +246,13 @@ void SurfaceInterceptor::setProtoRectLocked(Rectangle* protoRect, const Rect& re protoRect->set_bottom(rect.bottom); } +void SurfaceInterceptor::setTransactionOriginLocked(Transaction* transaction, int32_t pid, + int32_t uid) { + Origin* origin(transaction->mutable_origin()); + origin->set_pid(pid); + origin->set_uid(uid); +} + void SurfaceInterceptor::addPositionLocked(Transaction* transaction, int32_t layerId, float x, float y) { @@ -330,6 +362,25 @@ void SurfaceInterceptor::addBackgroundBlurRadiusLocked(Transaction* transaction, blurRadiusChange->set_background_blur_radius(backgroundBlurRadius); } +void SurfaceInterceptor::addBlurRegionsLocked(Transaction* transaction, int32_t layerId, + const std::vector<BlurRegion>& blurRegions) { + SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); + BlurRegionsChange* blurRegionsChange(change->mutable_blur_regions()); + for (const auto blurRegion : blurRegions) { + const auto blurRegionChange = blurRegionsChange->add_blur_regions(); + blurRegionChange->set_blur_radius(blurRegion.blurRadius); + blurRegionChange->set_corner_radius_tl(blurRegion.cornerRadiusTL); + blurRegionChange->set_corner_radius_tr(blurRegion.cornerRadiusTR); + blurRegionChange->set_corner_radius_bl(blurRegion.cornerRadiusBL); + blurRegionChange->set_corner_radius_br(blurRegion.cornerRadiusBR); + blurRegionChange->set_alpha(blurRegion.alpha); + blurRegionChange->set_left(blurRegion.left); + blurRegionChange->set_top(blurRegion.top); + blurRegionChange->set_right(blurRegion.right); + blurRegionChange->set_bottom(blurRegion.bottom); + } +} + void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int32_t layerId, const sp<const Layer>& layer, uint64_t frameNumber) { @@ -344,14 +395,6 @@ void SurfaceInterceptor::addDeferTransactionLocked(Transaction* transaction, int deferTransaction->set_frame_number(frameNumber); } -void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction, - int32_t layerId, int32_t overrideScalingMode) -{ - SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); - OverrideScalingModeChange* overrideChange(change->mutable_override_scaling_mode()); - overrideChange->set_override_scaling_mode(overrideScalingMode); -} - void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId) { SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); @@ -433,36 +476,36 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, if (state.what & layer_state_t::eBackgroundBlurRadiusChanged) { addBackgroundBlurRadiusLocked(transaction, layerId, state.backgroundBlurRadius); } + if (state.what & layer_state_t::eBlurRegionsChanged) { + addBlurRegionsLocked(transaction, layerId, state.blurRegions); + } if (state.what & layer_state_t::eDeferTransaction_legacy) { sp<Layer> otherLayer = nullptr; - if (state.barrierHandle_legacy != nullptr) { - otherLayer = - static_cast<Layer::Handle*>(state.barrierHandle_legacy.get())->owner.promote(); - } else if (state.barrierGbp_legacy != nullptr) { - auto const& gbp = state.barrierGbp_legacy; - if (mFlinger->authenticateSurfaceTextureLocked(gbp)) { - otherLayer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer(); - } else { - ALOGE("Attempt to defer transaction to to an unrecognized GraphicBufferProducer"); - } + if (state.barrierSurfaceControl_legacy != nullptr) { + otherLayer = static_cast<Layer::Handle*>( + state.barrierSurfaceControl_legacy->getHandle().get()) + ->owner.promote(); } - addDeferTransactionLocked(transaction, layerId, otherLayer, state.frameNumber_legacy); - } - if (state.what & layer_state_t::eOverrideScalingModeChanged) { - addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode); + addDeferTransactionLocked(transaction, layerId, otherLayer, state.barrierFrameNumber); } if (state.what & layer_state_t::eReparent) { - addReparentLocked(transaction, layerId, getLayerIdFromHandle(state.parentHandleForChild)); + auto parentHandle = (state.parentSurfaceControlForChild) + ? state.parentSurfaceControlForChild->getHandle() + : nullptr; + addReparentLocked(transaction, layerId, getLayerIdFromHandle(parentHandle)); } if (state.what & layer_state_t::eReparentChildren) { - addReparentChildrenLocked(transaction, layerId, getLayerIdFromHandle(state.reparentHandle)); + addReparentChildrenLocked(transaction, layerId, + getLayerIdFromHandle(state.reparentSurfaceControl->getHandle())); } if (state.what & layer_state_t::eDetachChildren) { addDetachChildrenLocked(transaction, layerId, true); } if (state.what & layer_state_t::eRelativeLayerChanged) { addRelativeParentLocked(transaction, layerId, - getLayerIdFromHandle(state.relativeLayerHandle), state.z); + getLayerIdFromHandle( + state.relativeLayerSurfaceControl->getHandle()), + state.z); } if (state.what & layer_state_t::eShadowRadiusChanged) { addShadowRadiusLocked(transaction, layerId, state.shadowRadius); @@ -483,18 +526,20 @@ void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction, } if (state.what & DisplayState::eDisplayProjectionChanged) { addDisplayProjectionLocked(transaction, sequenceId, toRotationInt(state.orientation), - state.viewport, state.frame); + state.layerStackSpaceRect, state.orientedDisplaySpaceRect); } } -void SurfaceInterceptor::addTransactionLocked(Increment* increment, - const Vector<ComposerState>& stateUpdates, - const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags) -{ +void SurfaceInterceptor::addTransactionLocked( + Increment* increment, const Vector<ComposerState>& stateUpdates, + const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, + const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags, int originPid, + int originUid, uint64_t transactionId) { Transaction* transaction(increment->mutable_transaction()); transaction->set_synchronous(transactionFlags & BnSurfaceComposer::eSynchronous); transaction->set_animation(transactionFlags & BnSurfaceComposer::eAnimation); + setTransactionOriginLocked(transaction, originPid, originUid); + transaction->set_id(transactionId); for (const auto& compState: stateUpdates) { addSurfaceChangesLocked(transaction, compState.state); } @@ -613,17 +658,18 @@ void SurfaceInterceptor::addPowerModeUpdateLocked(Increment* increment, int32_t powerModeUpdate->set_mode(mode); } -void SurfaceInterceptor::saveTransaction(const Vector<ComposerState>& stateUpdates, - const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t flags) -{ +void SurfaceInterceptor::saveTransaction( + const Vector<ComposerState>& stateUpdates, + const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, + const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, int originUid, + uint64_t transactionId) { if (!mEnabled || (stateUpdates.size() <= 0 && changedDisplays.size() <= 0)) { return; } ATRACE_CALL(); std::lock_guard<std::mutex> protoGuard(mTraceMutex); addTransactionLocked(createTraceIncrementLocked(), stateUpdates, displays, changedDisplays, - flags); + flags, originPid, originUid, transactionId); } void SurfaceInterceptor::saveSurfaceCreation(const sp<const Layer>& layer) { diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index 896bdcc259..3df79c6eb5 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -21,6 +21,8 @@ #include <mutex> +#include <binder/IBinder.h> + #include <gui/LayerState.h> #include <utils/KeyedVector.h> @@ -48,7 +50,7 @@ using DisplayChange = surfaceflinger::DisplayChange; constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb"; -class SurfaceInterceptor { +class SurfaceInterceptor : public IBinder::DeathRecipient { public: virtual ~SurfaceInterceptor(); @@ -58,11 +60,16 @@ public: virtual void disable() = 0; virtual bool isEnabled() = 0; + virtual void addTransactionTraceListener( + const sp<gui::ITransactionTraceListener>& listener) = 0; + virtual void binderDied(const wp<IBinder>& who) = 0; + // Intercept display and surface transactions virtual void saveTransaction( const Vector<ComposerState>& stateUpdates, const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t flags) = 0; + const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, + int originUid, uint64_t transactionId) = 0; // Intercept surface data virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0; @@ -85,7 +92,7 @@ namespace impl { */ class SurfaceInterceptor final : public android::SurfaceInterceptor { public: - explicit SurfaceInterceptor(SurfaceFlinger* const flinger); + SurfaceInterceptor() = default; ~SurfaceInterceptor() override = default; // Both vectors are used to capture the current state of SF as the initial snapshot in the trace @@ -94,10 +101,14 @@ public: void disable() override; bool isEnabled() override; + void addTransactionTraceListener(const sp<gui::ITransactionTraceListener>& listener) override; + void binderDied(const wp<IBinder>& who) override; + // Intercept display and surface transactions void saveTransaction(const Vector<ComposerState>& stateUpdates, const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t flags) override; + const Vector<DisplayState>& changedDisplays, uint32_t flags, int originPid, + int originUid, uint64_t transactionId) override; // Intercept surface data void saveSurfaceCreation(const sp<const Layer>& layer) override; @@ -154,14 +165,16 @@ private: void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius); void addBackgroundBlurRadiusLocked(Transaction* transaction, int32_t layerId, int32_t backgroundBlurRadius); + void addBlurRegionsLocked(Transaction* transaction, int32_t layerId, + const std::vector<BlurRegion>& effectRegions); void addDeferTransactionLocked(Transaction* transaction, int32_t layerId, const sp<const Layer>& layer, uint64_t frameNumber); - void addOverrideScalingModeLocked(Transaction* transaction, int32_t layerId, - int32_t overrideScalingMode); void addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state); void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates, - const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays, - const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags); + const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>& displays, + const Vector<DisplayState>& changedDisplays, + uint32_t transactionFlags, int originPid, int originUid, + uint64_t transactionId); void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId); void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId); void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached); @@ -182,12 +195,16 @@ private: void addDisplayChangesLocked(Transaction* transaction, const DisplayState& state, int32_t sequenceId); + // Add transaction origin to trace + void setTransactionOriginLocked(Transaction* transaction, int32_t pid, int32_t uid); bool mEnabled {false}; std::string mOutputFileName {DEFAULT_FILENAME}; std::mutex mTraceMutex {}; Trace mTrace {}; - SurfaceFlinger* const mFlinger; + std::mutex mListenersMutex; + std::map<wp<IBinder>, sp<gui::ITransactionTraceListener>> mTraceToggledListeners + GUARDED_BY(mListenersMutex); }; } // namespace impl diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp index d84ce69e56..1d1f0c5cf6 100644 --- a/services/surfaceflinger/SurfaceTracing.cpp +++ b/services/surfaceflinger/SurfaceTracing.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" #undef LOG_TAG #define LOG_TAG "SurfaceTracing" #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -32,65 +29,65 @@ namespace android { -SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) - : mFlinger(flinger), mSfLock(flinger.mTracingLock) {} +SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {} -void SurfaceTracing::mainLoop() { - bool enabled = addFirstEntry(); - while (enabled) { - LayersTraceProto entry = traceWhenNotified(); - enabled = addTraceToBuffer(entry); +bool SurfaceTracing::enable() { + std::scoped_lock lock(mTraceLock); + if (mEnabled) { + return false; } + + if (flagIsSet(TRACE_SYNC)) { + runner = std::make_unique<SurfaceTracing::Runner>(mFlinger, mConfig); + } else { + runner = std::make_unique<SurfaceTracing::AsyncRunner>(mFlinger, mConfig, + mFlinger.mTracingLock); + } + mEnabled = true; + return true; } -bool SurfaceTracing::addFirstEntry() { - LayersTraceProto entry; - { - std::scoped_lock lock(mSfLock); - entry = traceLayersLocked("tracing.enable"); +bool SurfaceTracing::disable() { + std::scoped_lock lock(mTraceLock); + if (!mEnabled) { + return false; } - return addTraceToBuffer(entry); + mEnabled = false; + runner->stop(); + return true; } -LayersTraceProto SurfaceTracing::traceWhenNotified() { - std::unique_lock<std::mutex> lock(mSfLock); - mCanStartTrace.wait(lock); - android::base::ScopedLockAssertion assumeLock(mSfLock); - LayersTraceProto entry = traceLayersLocked(mWhere); - mTracingInProgress = false; - mMissedTraceEntries = 0; - lock.unlock(); - return entry; +bool SurfaceTracing::isEnabled() const { + std::scoped_lock lock(mTraceLock); + return mEnabled; } -bool SurfaceTracing::addTraceToBuffer(LayersTraceProto& entry) { +status_t SurfaceTracing::writeToFile() { std::scoped_lock lock(mTraceLock); - mBuffer.emplace(std::move(entry)); - if (mWriteToFile) { - writeProtoFileLocked(); - mWriteToFile = false; + if (!mEnabled) { + return STATUS_OK; } - return mEnabled; + return runner->writeToFile(); } void SurfaceTracing::notify(const char* where) { - std::scoped_lock lock(mSfLock); - notifyLocked(where); + if (mEnabled) { + runner->notify(where); + } } void SurfaceTracing::notifyLocked(const char* where) { - mWhere = where; - if (mTracingInProgress) { - mMissedTraceEntries++; + if (mEnabled) { + runner->notifyLocked(where); } - mTracingInProgress = true; - mCanStartTrace.notify_one(); } -void SurfaceTracing::writeToFileAsync() { +void SurfaceTracing::dump(std::string& result) const { std::scoped_lock lock(mTraceLock); - mWriteToFile = true; - mCanStartTrace.notify_one(); + base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled"); + if (mEnabled) { + runner->dump(result); + } } void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) { @@ -101,12 +98,12 @@ void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) { } void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) { - auto protoSize = proto.ByteSize(); + size_t protoSize = static_cast<size_t>(proto.ByteSize()); while (mUsedInBytes + protoSize > mSizeInBytes) { if (mStorage.empty()) { return; } - mUsedInBytes -= mStorage.front().ByteSize(); + mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize()); mStorage.pop(); } mUsedInBytes += protoSize; @@ -115,7 +112,7 @@ void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) { } void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) { - fileProto->mutable_entry()->Reserve(mStorage.size()); + fileProto->mutable_entry()->Reserve(static_cast<int>(mStorage.size())); while (!mStorage.empty()) { auto entry = fileProto->add_entry(); @@ -124,77 +121,66 @@ void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) { } } -bool SurfaceTracing::enable() { - std::scoped_lock lock(mTraceLock); - - if (mEnabled) { - return false; - } +SurfaceTracing::Runner::Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config) + : mFlinger(flinger), mConfig(config) { + mBuffer.setSize(mConfig.bufferSize); +} - mBuffer.reset(mBufferSize); - mEnabled = true; - mThread = std::thread(&SurfaceTracing::mainLoop, this); - return true; +void SurfaceTracing::Runner::notify(const char* where) { + LayersTraceProto entry = traceLayers(where); + mBuffer.emplace(std::move(entry)); } -status_t SurfaceTracing::writeToFile() { - std::thread thread; - { - std::scoped_lock lock(mTraceLock); - thread = std::move(mThread); - } - thread.join(); - return mLastErr; +status_t SurfaceTracing::Runner::stop() { + return writeToFile(); } -bool SurfaceTracing::disable() { - std::scoped_lock lock(mTraceLock); +status_t SurfaceTracing::Runner::writeToFile() { + ATRACE_CALL(); - if (!mEnabled) { - return false; - } + LayersTraceFileProto fileProto; + std::string output; - mEnabled = false; - mWriteToFile = true; - mCanStartTrace.notify_all(); - return true; -} + fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 | + LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L); + mBuffer.flush(&fileProto); + mBuffer.reset(mConfig.bufferSize); -bool SurfaceTracing::isEnabled() const { - std::scoped_lock lock(mTraceLock); - return mEnabled; -} + if (!fileProto.SerializeToString(&output)) { + ALOGE("Could not save the proto file! Permission denied"); + return PERMISSION_DENIED; + } -void SurfaceTracing::setBufferSize(size_t bufferSizeInByte) { - std::scoped_lock lock(mTraceLock); - mBufferSize = bufferSizeInByte; - mBuffer.setSize(bufferSizeInByte); -} + // -rw-r--r-- + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if (!android::base::WriteStringToFile(output, DEFAULT_FILE_NAME, mode, getuid(), getgid(), + true)) { + ALOGE("Could not save the proto file! There are missing fields"); + return PERMISSION_DENIED; + } -void SurfaceTracing::setTraceFlags(uint32_t flags) { - std::scoped_lock lock(mSfLock); - mTraceFlags = flags; + return NO_ERROR; } -LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) { +LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) { ATRACE_CALL(); LayersTraceProto entry; entry.set_elapsed_realtime_nanos(elapsedRealtimeNano()); entry.set_where(where); - LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags)); + LayersProto layers(mFlinger.dumpDrawingStateProto(mConfig.flags)); - if (flagIsSetLocked(SurfaceTracing::TRACE_EXTRA)) { + if (flagIsSet(SurfaceTracing::TRACE_EXTRA)) { mFlinger.dumpOffscreenLayersProto(layers); } entry.mutable_layers()->Swap(&layers); - if (mTraceFlags & SurfaceTracing::TRACE_HWC) { + if (flagIsSet(SurfaceTracing::TRACE_HWC)) { std::string hwcDump; mFlinger.dumpHwc(hwcDump); entry.set_hwc_blob(hwcDump); } - if (!flagIsSetLocked(SurfaceTracing::TRACE_COMPOSITION)) { + if (!flagIsSet(SurfaceTracing::TRACE_COMPOSITION)) { entry.set_excludes_composition_state(true); } entry.set_missed_entries(mMissedTraceEntries); @@ -202,42 +188,70 @@ LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) { return entry; } -void SurfaceTracing::writeProtoFileLocked() { - ATRACE_CALL(); +void SurfaceTracing::Runner::dump(std::string& result) const { + base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n", + mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB), + float(mBuffer.size()) / float(1_MB)); +} - LayersTraceFileProto fileProto; - std::string output; +SurfaceTracing::AsyncRunner::AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, + std::mutex& sfLock) + : SurfaceTracing::Runner(flinger, config), mSfLock(sfLock) { + mEnabled = true; + mThread = std::thread(&AsyncRunner::loop, this); +} - fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 | - LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L); - mBuffer.flush(&fileProto); - mBuffer.reset(mBufferSize); +void SurfaceTracing::AsyncRunner::loop() { + while (mEnabled) { + LayersTraceProto entry; + bool entryAdded = traceWhenNotified(&entry); + if (entryAdded) { + mBuffer.emplace(std::move(entry)); + } + if (mWriteToFile) { + Runner::writeToFile(); + mWriteToFile = false; + } + } +} - if (!fileProto.SerializeToString(&output)) { - ALOGE("Could not save the proto file! Permission denied"); - mLastErr = PERMISSION_DENIED; +bool SurfaceTracing::AsyncRunner::traceWhenNotified(LayersTraceProto* outProto) { + std::unique_lock<std::mutex> lock(mSfLock); + mCanStartTrace.wait(lock); + if (!mAddEntry) { + return false; } + *outProto = traceLayers(mWhere); + mAddEntry = false; + mMissedTraceEntries = 0; + return true; +} - // -rw-r--r-- - const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - if (!android::base::WriteStringToFile(output, kDefaultFileName, mode, getuid(), getgid(), - true)) { - ALOGE("Could not save the proto file! There are missing fields"); - mLastErr = PERMISSION_DENIED; +void SurfaceTracing::AsyncRunner::notify(const char* where) { + std::scoped_lock lock(mSfLock); + notifyLocked(where); +} + +void SurfaceTracing::AsyncRunner::notifyLocked(const char* where) { + mWhere = where; + if (mAddEntry) { + mMissedTraceEntries++; } + mAddEntry = true; + mCanStartTrace.notify_one(); +} - mLastErr = NO_ERROR; +status_t SurfaceTracing::AsyncRunner::writeToFile() { + mWriteToFile = true; + mCanStartTrace.notify_one(); + return STATUS_OK; } -void SurfaceTracing::dump(std::string& result) const { - std::scoped_lock lock(mTraceLock); - base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled"); - base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n", - mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB), - float(mBuffer.size()) / float(1_MB)); +status_t SurfaceTracing::AsyncRunner::stop() { + mEnabled = false; + mCanStartTrace.notify_one(); + mThread.join(); + return Runner::writeToFile(); } } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h index f208eb8803..576bba72ad 100644 --- a/services/surfaceflinger/SurfaceTracing.h +++ b/services/surfaceflinger/SurfaceTracing.h @@ -32,25 +32,31 @@ using namespace android::surfaceflinger; namespace android { class SurfaceFlinger; - constexpr auto operator""_MB(unsigned long long const num) { return num * 1024 * 1024; } /* - * SurfaceTracing records layer states during surface flinging. + * SurfaceTracing records layer states during surface flinging. Manages tracing state and + * configuration. */ class SurfaceTracing { public: - explicit SurfaceTracing(SurfaceFlinger& flinger); + SurfaceTracing(SurfaceFlinger& flinger); bool enable(); bool disable(); status_t writeToFile(); bool isEnabled() const; + /* + * Adds a trace entry, must be called from the drawing thread or while holding the + * SurfaceFlinger tracing lock. + */ void notify(const char* where); - void notifyLocked(const char* where) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */; + /* + * Adds a trace entry, called while holding the SurfaceFlinger tracing lock. + */ + void notifyLocked(const char* where) /* REQUIRES(mSfLock) */; - void setBufferSize(size_t bufferSizeInByte); - void writeToFileAsync(); + void setBufferSize(size_t bufferSizeInBytes) { mConfig.bufferSize = bufferSizeInBytes; } void dump(std::string& result) const; enum : uint32_t { @@ -59,18 +65,34 @@ public: TRACE_COMPOSITION = 1 << 2, TRACE_EXTRA = 1 << 3, TRACE_HWC = 1 << 4, - TRACE_ALL = 0xffffffff + // Add non-geometry composition changes to the trace. + TRACE_BUFFERS = 1 << 5, + // Add entries from the drawing thread post composition. + TRACE_SYNC = 1 << 6, + TRACE_ALL = TRACE_CRITICAL | TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA, }; - void setTraceFlags(uint32_t flags); - bool flagIsSetLocked(uint32_t flags) NO_THREAD_SAFETY_ANALYSIS /* REQUIRES(mSfLock) */ { - return (mTraceFlags & flags) == flags; - } + void setTraceFlags(uint32_t flags) { mConfig.flags = flags; } + bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; } private: - static constexpr auto kDefaultBufferCapInByte = 5_MB; - static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb"; + class Runner; + static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB; + static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.pb"; - class LayersTraceBuffer { // ring buffer + SurfaceFlinger& mFlinger; + mutable std::mutex mTraceLock; + bool mEnabled = false; + std::unique_ptr<Runner> runner; + + struct Config { + uint32_t flags = TRACE_CRITICAL | TRACE_INPUT; + size_t bufferSize = DEFAULT_BUFFER_SIZE; + } mConfig; + + /* + * ring buffer. + */ + class LayersTraceBuffer { public: size_t size() const { return mSizeInBytes; } size_t used() const { return mUsedInBytes; } @@ -83,35 +105,59 @@ private: private: size_t mUsedInBytes = 0U; - size_t mSizeInBytes = 0U; + size_t mSizeInBytes = DEFAULT_BUFFER_SIZE; std::queue<LayersTraceProto> mStorage; }; - void mainLoop(); - bool addFirstEntry(); - LayersTraceProto traceWhenNotified(); - LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock); - - // Returns true if trace is enabled. - bool addTraceToBuffer(LayersTraceProto& entry); - void writeProtoFileLocked() REQUIRES(mTraceLock); - - SurfaceFlinger& mFlinger; - status_t mLastErr = NO_ERROR; - std::thread mThread; - std::condition_variable mCanStartTrace; + /* + * Implements a synchronous way of adding trace entries. This must be called + * from the drawing thread. + */ + class Runner { + public: + Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config); + virtual ~Runner() = default; + virtual status_t stop(); + virtual status_t writeToFile(); + virtual void notify(const char* where); + /* Cannot be called with a synchronous runner. */ + virtual void notifyLocked(const char* /* where */) {} + void dump(std::string& result) const; + + protected: + bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; } + SurfaceFlinger& mFlinger; + SurfaceTracing::Config mConfig; + SurfaceTracing::LayersTraceBuffer mBuffer; + uint32_t mMissedTraceEntries = 0; + LayersTraceProto traceLayers(const char* where); + }; - std::mutex& mSfLock; - uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_CRITICAL | TRACE_INPUT; - const char* mWhere GUARDED_BY(mSfLock) = ""; - uint32_t mMissedTraceEntries GUARDED_BY(mSfLock) = 0; - bool mTracingInProgress GUARDED_BY(mSfLock) = false; + /* + * Implements asynchronous way to add trace entries called from a separate thread while holding + * the SurfaceFlinger tracing lock. Trace entries may be missed if the tracing thread is not + * scheduled in time. + */ + class AsyncRunner : public Runner { + public: + AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, std::mutex& sfLock); + virtual ~AsyncRunner() = default; + status_t stop() override; + status_t writeToFile() override; + void notify(const char* where) override; + void notifyLocked(const char* where); - mutable std::mutex mTraceLock; - LayersTraceBuffer mBuffer GUARDED_BY(mTraceLock); - size_t mBufferSize GUARDED_BY(mTraceLock) = kDefaultBufferCapInByte; - bool mEnabled GUARDED_BY(mTraceLock) = false; - bool mWriteToFile GUARDED_BY(mTraceLock) = false; + private: + std::mutex& mSfLock; + std::condition_variable mCanStartTrace; + std::thread mThread; + const char* mWhere = ""; + bool mWriteToFile = false; + bool mEnabled = false; + bool mAddEntry = false; + void loop(); + bool traceWhenNotified(LayersTraceProto* outProto); + }; }; } // namespace android diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 37194c6355..fe9e7378db 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -14,9 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" #undef LOG_TAG #define LOG_TAG "TimeStats" #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -33,6 +30,8 @@ #include <algorithm> #include <chrono> +#include "timestatsproto/TimeStatsHelper.h" + namespace android { namespace impl { @@ -115,6 +114,13 @@ AStatsManager_PullAtomCallbackReturn TimeStats::populateGlobalAtom(AStatsEventLi 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); mStatsDelegate->statsEventBuild(event); clearGlobalLocked(); @@ -160,6 +166,13 @@ AStatsManager_PullAtomCallbackReturn TimeStats::populateLayerAtom(AStatsEventLis mStatsDelegate->statsEventWriteInt64(event, layer->lateAcquireFrames); mStatsDelegate->statsEventWriteInt64(event, layer->badDesiredPresentFrames); + mStatsDelegate->statsEventWriteInt32(event, layer->uid); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalFrames); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalJankyFrames); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongCpu); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFLongGpu); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalSFUnattributed); + mStatsDelegate->statsEventWriteInt32(event, layer->jankPayload.totalAppUnattributed); mStatsDelegate->statsEventBuild(event); } @@ -397,11 +410,13 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) { timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime); if (prevTimeRecord.ready) { + uid_t uid = layerRecord.uid; const std::string& layerName = layerRecord.layerName; - if (!mTimeStats.stats.count(layerName)) { - mTimeStats.stats[layerName].layerName = layerName; + if (!mTimeStats.stats.count({uid, layerName})) { + mTimeStats.stats[{uid, layerName}].uid = uid; + mTimeStats.stats[{uid, layerName}].layerName = layerName; } - TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName]; + TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}]; timeStatsLayer.totalFrames++; timeStatsLayer.droppedFrames += layerRecord.droppedFrames; timeStatsLayer.lateAcquireFrames += layerRecord.lateAcquireFrames; @@ -462,8 +477,13 @@ static bool layerNameIsValid(const std::string& layerName) { layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0; } +bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName) { + return mTimeStats.stats.count({uid, layerName}) > 0 || + mTimeStats.stats.size() < MAX_NUM_LAYER_STATS; +} + void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, - nsecs_t postTime) { + uid_t uid, nsecs_t postTime) { if (!mEnabled.load()) return; ATRACE_CALL(); @@ -471,11 +491,12 @@ void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::st postTime); std::lock_guard<std::mutex> lock(mMutex); - if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) { + if (!canAddNewAggregatedStats(uid, layerName)) { return; } if (!mTimeStatsTracker.count(layerId) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS && layerNameIsValid(layerName)) { + mTimeStatsTracker[layerId].uid = uid; mTimeStatsTracker[layerId].layerName = layerName; } if (!mTimeStatsTracker.count(layerId)) return; @@ -655,6 +676,66 @@ void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber, flushAvailableRecordsToStatsLocked(layerId); } +template <class T> +static void updateJankPayload(T& t, int32_t reasons) { + t.jankPayload.totalFrames++; + + static const constexpr int32_t kValidJankyReason = + TimeStats::JankType::SurfaceFlingerDeadlineMissed | + TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed | + TimeStats::JankType::AppDeadlineMissed | TimeStats::JankType::Display; + if (reasons & kValidJankyReason) { + t.jankPayload.totalJankyFrames++; + if ((reasons & TimeStats::JankType::SurfaceFlingerDeadlineMissed) != 0) { + t.jankPayload.totalSFLongCpu++; + } + if ((reasons & TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed) != 0) { + t.jankPayload.totalSFLongGpu++; + } + if ((reasons & TimeStats::JankType::Display) != 0) { + t.jankPayload.totalSFUnattributed++; + } + if ((reasons & TimeStats::JankType::AppDeadlineMissed) != 0) { + t.jankPayload.totalAppUnattributed++; + } + } +} + +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) { + if (!mEnabled.load()) return; + + ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mMutex); + + // Only update layer stats if we're allowed to do so. + // As an implementation detail, we do this because this method is expected to be + // called from FrameTimeline, which is allowed to do jank analysis well after a frame is + // presented. This means that we can't rely on TimeStats to flush layer records over to the + // aggregated stats. + if (!canAddNewAggregatedStats(uid, layerName)) { + return; + } + + // Defensively initialize the stats in case FrameTimeline flushes its signaled present fences + // before TimeStats does. + if (!mTimeStats.stats.count({uid, layerName})) { + mTimeStats.stats[{uid, layerName}].uid = uid; + mTimeStats.stats[{uid, layerName}].layerName = layerName; + } + + TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[{uid, layerName}]; + updateJankPayload<TimeStatsHelper::TimeStatsLayer>(timeStatsLayer, reasons); +} + void TimeStats::onDestroy(int32_t layerId) { ATRACE_CALL(); ALOGV("[%d]-onDestroy", layerId); @@ -860,6 +941,7 @@ void TimeStats::clearGlobalLocked() { mTimeStats.presentToPresent.hist.clear(); mTimeStats.frameDuration.hist.clear(); mTimeStats.renderEngineTiming.hist.clear(); + mTimeStats.jankPayload = TimeStatsHelper::JankPayload(); mTimeStats.refreshRateStats.clear(); mPowerTime.prevTime = systemTime(); mGlobalRecord.prevPresentTime = 0; @@ -905,6 +987,3 @@ void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::strin } // namespace impl } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index 8de5d0c3e8..4fa0a02770 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -17,6 +17,7 @@ #pragma once // TODO(b/129481165): remove the #pragma below and fix conversion issues +#include <cstdint> #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" @@ -85,7 +86,7 @@ public: const std::shared_ptr<FenceTime>& readyFence) = 0; virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, - nsecs_t postTime) = 0; + uid_t uid, nsecs_t postTime) = 0; virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0; // Reasons why latching a particular buffer may be skipped enum class LatchSkipReason { @@ -108,6 +109,40 @@ public: virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0; virtual void setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence) = 0; + + // Subset of jank metadata tracked by FrameTimeline for the purpose of funneling to telemetry. + enum JankType { + // No Jank + None = 0x0, + // Jank not related to SurfaceFlinger or the App + Display = 0x1, + // SF took too long on the CPU + SurfaceFlingerDeadlineMissed = 0x2, + // SF took too long on the GPU + SurfaceFlingerGpuDeadlineMissed = 0x4, + // Either App or GPU took too long on the frame + AppDeadlineMissed = 0x8, + // Predictions live for 120ms, if prediction is expired for a frame, there is definitely a + // jank + // associated with the App if this is for a SurfaceFrame, and SF for a DisplayFrame. + PredictionExpired = 0x10, + // Latching a buffer early might cause an early present of the frame + SurfaceFlingerEarlyLatch = 0x20, + }; + + // 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 + // accounting of janky frames. + virtual void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) = 0; // Clean up the layer record virtual void onDestroy(int32_t layerId) = 0; // If SF skips or rejects a buffer, remove the corresponding TimeRecord. @@ -142,6 +177,7 @@ class TimeStats : public android::TimeStats { }; struct LayerRecord { + uid_t uid; std::string layerName; // This is the index in timeRecords, at which the timestamps for that // specific frame are still not fully received. This is not waiting for @@ -241,7 +277,7 @@ public: void recordRenderEngineDuration(nsecs_t startTime, const std::shared_ptr<FenceTime>& readyFence) override; - void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, + void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid, nsecs_t postTime) override; void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override; void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override; @@ -253,6 +289,8 @@ public: void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) override; void setPresentFence(int32_t layerId, uint64_t frameNumber, const std::shared_ptr<FenceTime>& presentFence) override; + void incrementJankyFrames(int32_t reasons) override; + void incrementJankyFrames(uid_t uid, const std::string& layerName, int32_t reasons) override; // Clean up the layer record void onDestroy(int32_t layerId) override; // If SF skips or rejects a buffer, remove the corresponding TimeRecord. @@ -276,6 +314,7 @@ private: void flushAvailableRecordsToStatsLocked(int32_t layerId); void flushPowerTimeLocked(); void flushAvailableGlobalRecordsToStatsLocked(); + bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName); void enable(); void disable(); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index 894ee6de06..0fb748f1a6 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -77,14 +77,28 @@ std::string TimeStatsHelper::Histogram::toString() const { return result; } +std::string TimeStatsHelper::JankPayload::toString() const { + std::string result; + StringAppendF(&result, "totalTimelineFrames = %d\n", totalFrames); + 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); + return result; +} + std::string TimeStatsHelper::TimeStatsLayer::toString() const { std::string result = "\n"; + StringAppendF(&result, "uid = %d\n", uid); StringAppendF(&result, "layerName = %s\n", layerName.c_str()); StringAppendF(&result, "packageName = %s\n", packageName.c_str()); StringAppendF(&result, "totalFrames = %d\n", totalFrames); StringAppendF(&result, "droppedFrames = %d\n", droppedFrames); StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames); StringAppendF(&result, "badDesiredPresentFrames = %d\n", badDesiredPresentFrames); + result.append("Jank payload for this layer:\n"); + result.append(jankPayload.toString()); const auto iter = deltas.find("present2present"); if (iter != deltas.end()) { const float averageTime = iter->second.averageTime(); @@ -110,9 +124,11 @@ std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> m 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()); StringAppendF(&result, "displayConfigStats is as below:\n"); for (const auto& [fps, duration] : refreshRateStats) { - StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration)); + StringAppendF(&result, "%dfps = %ldms\n", fps, ns2ms(duration)); } result.back() = '\n'; StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime()); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 0c75f96e46..033eb5dcd9 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -40,14 +40,28 @@ public: std::string toString() const; }; + struct JankPayload { + // note that transactions are counted for these frames. + int32_t totalFrames = 0; + int32_t totalJankyFrames = 0; + int32_t totalSFLongCpu = 0; + int32_t totalSFLongGpu = 0; + int32_t totalSFUnattributed = 0; + int32_t totalAppUnattributed = 0; + + std::string toString() const; + }; + class TimeStatsLayer { public: + uid_t uid; std::string layerName; std::string packageName; int32_t totalFrames = 0; int32_t droppedFrames = 0; int32_t lateAcquireFrames = 0; int32_t badDesiredPresentFrames = 0; + JankPayload jankPayload; std::unordered_map<std::string, Histogram> deltas; std::string toString() const; @@ -69,8 +83,17 @@ public: Histogram presentToPresent; Histogram frameDuration; Histogram renderEngineTiming; - std::unordered_map<std::string, TimeStatsLayer> stats; + + 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); + } + }; + std::unordered_map<std::pair<uid_t, std::string>, TimeStatsLayer, StatsHasher> stats; std::unordered_map<uint32_t, nsecs_t> refreshRateStats; + JankPayload jankPayload; std::string toString(std::optional<uint32_t> maxLayers) const; SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const; diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h index 4e7f67d5ac..49cf80cc68 100644 --- a/services/surfaceflinger/TracedOrdinal.h +++ b/services/surfaceflinger/TracedOrdinal.h @@ -21,12 +21,32 @@ #include <cmath> #include <string> +namespace std { +template <class Rep, class Period> +bool signbit(std::chrono::duration<Rep, Period> v) { + return signbit(std::chrono::duration_cast<std::chrono::nanoseconds>(v).count()); +} +} // namespace std + namespace android { +namespace { +template <typename T> +int64_t to_int64(T v) { + return int64_t(v); +} + +template <class Rep, class Period> +int64_t to_int64(std::chrono::duration<Rep, Period> v) { + return int64_t(v.count()); +} +} // namespace + template <typename T> class TracedOrdinal { public: - static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()), + static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()) || + std::is_same<std::chrono::nanoseconds, T>(), "Type is not supported. Please test it with systrace before adding " "it to the list."); @@ -57,12 +77,12 @@ private: } if (!std::signbit(mData)) { - ATRACE_INT64(mName.c_str(), int64_t(mData)); + ATRACE_INT64(mName.c_str(), to_int64(mData)); if (mHasGoneNegative) { ATRACE_INT64(mNameNegative.c_str(), 0); } } else { - ATRACE_INT64(mNameNegative.c_str(), -int64_t(mData)); + ATRACE_INT64(mNameNegative.c_str(), -to_int64(mData)); ATRACE_INT64(mName.c_str(), 0); } } diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp index d03cb7b22a..0a73b23240 100644 --- a/services/surfaceflinger/layerproto/Android.bp +++ b/services/surfaceflinger/layerproto/Android.bp @@ -1,5 +1,5 @@ -cc_library_shared { - name: "liblayers_proto", +cc_defaults { + name: "liblayers_proto_defaults", export_include_dirs: ["include"], srcs: [ @@ -19,7 +19,7 @@ cc_library_shared { proto: { export_proto_headers: true, }, - + cppflags: [ "-Werror", "-Wno-unused-parameter", @@ -33,7 +33,20 @@ cc_library_shared { "-Wno-old-style-cast", "-Wno-undef", ], +} + +cc_library_shared { + name: "liblayers_proto", + defaults: [ + "liblayers_proto_defaults", + ], +} +cc_library_static { + name: "liblayers_proto_static", + defaults: [ + "liblayers_proto_defaults", + ], } java_library_static { diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp index 8fce0c9bf3..aef670da33 100644 --- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp +++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp @@ -115,6 +115,7 @@ LayerProtoParser::Layer LayerProtoParser::generateLayer(const LayerProto& layerP } layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop()); layer.shadowRadius = layerProto.shadow_radius(); + layer.ownerUid = layerProto.owner_uid(); return layer; } @@ -276,7 +277,7 @@ std::string LayerProtoParser::Region::to_string(const char* what) const { std::string LayerProtoParser::Layer::to_string() const { std::string result; - StringAppendF(&result, "+ %s (%s)\n", type.c_str(), name.c_str()); + StringAppendF(&result, "+ %s (%s) uid=%d\n", type.c_str(), name.c_str(), ownerUid); result.append(transparentRegion.to_string("TransparentRegion").c_str()); result.append(visibleRegion.to_string("VisibleRegion").c_str()); result.append(damageRegion.to_string("SurfaceDamageRegion").c_str()); diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h index 52b916555f..c48354fe95 100644 --- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h +++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h @@ -114,6 +114,7 @@ public: LayerMetadata metadata; LayerProtoParser::FloatRect cornerRadiusCrop; float shadowRadius; + uid_t ownerUid; std::string to_string() const; }; diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto index 7f1f542e4b..9f25674f1b 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -123,6 +123,11 @@ message LayerProto { bool is_relative_of = 51; // Layer's background blur radius in pixels. int32 background_blur_radius = 52; + + uint32 owner_uid = 53; + + // Regions of a layer, where blur should be applied. + repeated BlurRegion blur_regions = 54; } message PositionProto { @@ -191,20 +196,34 @@ message InputWindowInfoProto { uint32 surface_inset = 5; bool visible = 6; - bool can_receive_keys = 7; - bool has_focus = 8; + bool can_receive_keys = 7 [deprecated=true]; + bool focusable = 8; bool has_wallpaper = 9; float global_scale_factor = 10; - float window_x_scale = 11; - float window_y_scale = 12; + float window_x_scale = 11 [deprecated=true]; + float window_y_scale = 12 [deprecated=true]; uint32 crop_layer_id = 13; bool replace_touchable_region_with_crop = 14; RectProto touchable_region_crop = 15; + TransformProto transform = 16; } message ColorTransformProto { // This will be a 4x4 matrix of float values repeated float val = 1; } + +message BlurRegion { + uint32 blur_radius = 1; + uint32 corner_radius_tl = 2; + uint32 corner_radius_tr = 3; + uint32 corner_radius_bl = 4; + float corner_radius_br = 5; + float alpha = 6; + int32 left = 7; + int32 top = 8; + int32 right = 9; + int32 bottom = 10; +}
\ No newline at end of file diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto index acf621e16c..990f3cffda 100644 --- a/services/surfaceflinger/layerproto/layerstrace.proto +++ b/services/surfaceflinger/layerproto/layerstrace.proto @@ -42,7 +42,7 @@ message LayersTraceFileProto { /* one window manager trace entry. */ message LayersTraceProto { /* required: elapsed realtime in nanos since boot of when this entry was logged */ - optional fixed64 elapsed_realtime_nanos = 1; + optional sfixed64 elapsed_realtime_nanos = 1; /* where the trace originated */ optional string where = 2; @@ -56,5 +56,5 @@ message LayersTraceProto { optional bool excludes_composition_state = 5; /* Number of missed entries since the last entry was recorded. */ - optional int32 missed_entries = 6; + optional uint32 missed_entries = 6; } diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 7666f7f42d..421484ff73 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -435,3 +435,13 @@ prop { access: Readonly prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms" } + + +# Updates the DeviceProductInfo when a hoplug reconnect event is processed +prop { + api_name: "update_device_product_info_on_hotplug_reconnect" + type: Boolean + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect" +} diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index ba60a7defb..da66ecef81 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -124,6 +124,10 @@ props { prop_name: "ro.surface_flinger.supports_background_blur" } prop { + api_name: "update_device_product_info_on_hotplug_reconnect" + prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect" + } + prop { api_name: "use_color_management" prop_name: "ro.surface_flinger.use_color_management" } diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 1532855089..02b4308123 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -21,18 +21,22 @@ cc_test { "CommonTypes_test.cpp", "Credentials_test.cpp", "DereferenceSurfaceControl_test.cpp", + "DetachChildren_test.cpp", "DisplayConfigs_test.cpp", "EffectLayer_test.cpp", "InvalidHandles_test.cpp", "LayerCallback_test.cpp", "LayerRenderTypeTransaction_test.cpp", + "LayerState_test.cpp", "LayerTransaction_test.cpp", "LayerTypeAndRenderTypeTransaction_test.cpp", "LayerTypeTransaction_test.cpp", "LayerUpdate_test.cpp", "MirrorLayer_test.cpp", "MultiDisplayLayerBounds_test.cpp", + "RefreshRateOverlay_test.cpp", "RelativeZ_test.cpp", + "ScreenCapture_test.cpp", "SetFrameRate_test.cpp", "SetGeometry_test.cpp", "Stress_test.cpp", @@ -42,18 +46,19 @@ cc_test { data: ["SurfaceFlinger_test.filter"], static_libs: [ "libtrace_proto", + "liblayers_proto_static", + "android.hardware.graphics.composer@2.1", ], shared_libs: [ "android.hardware.graphics.common-unstable-ndk_platform", "android.hardware.graphics.common@1.2", - "android.hardware.graphics.composer@2.1", "libandroid", + "libbase", "libbinder", "libcutils", "libEGL", "libGLESv2", "libgui", - "liblayers_proto", "liblog", "libnativewindow", "libprotobuf-cpp-full", diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index c136708ff0..9302463190 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -1,3 +1,23 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + #include <gtest/gtest.h> #include <gui/ISurfaceComposer.h> #include <gui/LayerDebugInfo.h> @@ -7,8 +27,8 @@ #include <private/gui/ComposerService.h> #include <ui/DisplayConfig.h> #include <utils/String8.h> - #include <functional> +#include "utils/ScreenshotUtils.h" namespace android { @@ -18,7 +38,6 @@ using ui::ColorMode; namespace { const String8 DISPLAY_NAME("Credentials Display Test"); const String8 SURFACE_NAME("Test Surface Name"); -const float FRAME_SCALE = 1.0f; } // namespace /** @@ -79,26 +98,6 @@ protected: t.setLayer(mBGSurfaceControl, INT_MAX - 3).show(mBGSurfaceControl).apply()); } - void setupVirtualDisplay() { - mVirtualDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true); - const ssize_t displayWidth = 100; - const ssize_t displayHeight = 100; - - // Background surface - mVirtualSurfaceControl = - mComposerClient->createSurface(SURFACE_NAME, displayWidth, displayHeight, - PIXEL_FORMAT_RGBA_8888, 0); - ASSERT_TRUE(mVirtualSurfaceControl != nullptr); - ASSERT_TRUE(mVirtualSurfaceControl->isValid()); - - Transaction t; - t.setDisplayLayerStack(mVirtualDisplay, 0); - ASSERT_EQ(NO_ERROR, - t.setLayer(mVirtualSurfaceControl, INT_MAX - 3) - .show(mVirtualSurfaceControl) - .apply()); - } - /** * Sets UID to imitate Graphic's process. */ @@ -146,6 +145,10 @@ protected: // Check as a non-supported user. setBinUID(); ASSERT_EQ(unprivilegedValue, condition()); + + // Check as shell since shell has some additional permissions + seteuid(AID_SHELL); + ASSERT_EQ(unprivilegedValue, condition()); } }; @@ -188,7 +191,7 @@ TEST_F(CredentialsTest, AllowedGetterMethodsTest) { Vector<DisplayConfig> configs; ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs)); - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveConfig(display)); + ASSERT_TRUE(SurfaceComposerClient::getActiveConfig(display) >= 0); ASSERT_NE(static_cast<ui::ColorMode>(BAD_VALUE), SurfaceComposerClient::getActiveColorMode(display)); @@ -215,18 +218,21 @@ TEST_F(CredentialsTest, GetDisplayNativePrimariesTest) { TEST_F(CredentialsTest, SetDesiredDisplayConfigsTest) { const auto display = SurfaceComposerClient::getInternalDisplayToken(); int32_t defaultConfig; + bool allowGroupSwitching; float primaryFpsMin; float primaryFpsMax; float appRequestFpsMin; float appRequestFpsMax; status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(display, &defaultConfig, + &allowGroupSwitching, &primaryFpsMin, &primaryFpsMax, &appRequestFpsMin, &appRequestFpsMax); ASSERT_EQ(res, NO_ERROR); std::function<status_t()> condition = [=]() { return SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, defaultConfig, + allowGroupSwitching, primaryFpsMin, primaryFpsMax, appRequestFpsMin, appRequestFpsMax); @@ -243,11 +249,31 @@ TEST_F(CredentialsTest, SetActiveColorModeTest) { } TEST_F(CredentialsTest, CreateDisplayTest) { + // Only graphics and system processes can create a secure display. std::function<bool()> condition = [=]() { sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true); return testDisplay.get() != nullptr; }; - ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false)); + + // Check with root. + seteuid(AID_ROOT); + ASSERT_FALSE(condition()); + + // Check as a Graphics user. + setGraphicsUID(); + ASSERT_TRUE(condition()); + + // Check as a system user. + setSystemUID(); + ASSERT_TRUE(condition()); + + // Check as a non-supported user. + setBinUID(); + ASSERT_FALSE(condition()); + + // Check as shell since shell has some additional permissions + seteuid(AID_SHELL); + ASSERT_FALSE(condition()); condition = [=]() { sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); @@ -260,9 +286,10 @@ TEST_F(CredentialsTest, CaptureTest) { const auto display = SurfaceComposerClient::getInternalDisplayToken(); std::function<status_t()> condition = [=]() { sp<GraphicBuffer> outBuffer; - return ScreenshotClient::capture(display, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, Rect(), 0 /*reqWidth*/, - 0 /*reqHeight*/, false, ui::ROTATION_0, &outBuffer); + DisplayCaptureArgs captureArgs; + captureArgs.displayToken = display; + ScreenCaptureResults captureResults; + return ScreenCapture::captureDisplay(captureArgs, captureResults); }; ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED)); } @@ -271,10 +298,12 @@ TEST_F(CredentialsTest, CaptureLayersTest) { setupBackgroundSurface(); sp<GraphicBuffer> outBuffer; std::function<status_t()> condition = [=]() { - sp<GraphicBuffer> outBuffer; - return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(), - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, - Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer); + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mBGSurfaceControl->getHandle(); + captureArgs.sourceCrop = {0, 0, 1, 1}; + + ScreenCaptureResults captureResults; + return ScreenCapture::captureLayers(captureArgs, captureResults); }; ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED)); } diff --git a/services/surfaceflinger/tests/DetachChildren_test.cpp b/services/surfaceflinger/tests/DetachChildren_test.cpp new file mode 100644 index 0000000000..9c7b1fcc04 --- /dev/null +++ b/services/surfaceflinger/tests/DetachChildren_test.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + +#include "LayerTransactionTest.h" + +namespace android { + +class DetachChildren : public LayerTransactionTest { +protected: + virtual void SetUp() { + LayerTransactionTest::SetUp(); + + mMainSurface = createLayer(String8("Main Test Surface"), mMainSurfaceBounds.width(), + mMainSurfaceBounds.height(), 0, mBlackBgSurface.get()); + + ASSERT_TRUE(mMainSurface != nullptr); + ASSERT_TRUE(mMainSurface->isValid()); + + TransactionUtils::fillSurfaceRGBA8(mMainSurface, mMainSurfaceColor); + + asTransaction([&](Transaction& t) { + t.setLayer(mMainSurface, INT32_MAX - 1) + .setPosition(mMainSurface, mMainSurfaceBounds.left, mMainSurfaceBounds.top) + .show(mMainSurface); + }); + } + + virtual void TearDown() { + LayerTransactionTest::TearDown(); + mMainSurface = 0; + } + + sp<SurfaceControl> mMainSurface; + Color mMainSurfaceColor = {195, 63, 63, 255}; + Rect mMainSurfaceBounds = Rect(64, 64, 128, 128); + std::unique_ptr<ScreenCapture> mCapture; +}; + +TEST_F(DetachChildren, RelativesAreNotDetached) { + Color relativeColor = {10, 10, 10, 255}; + Rect relBounds = Rect(64, 64, 74, 74); + + sp<SurfaceControl> relative = + createLayer(String8("relativeTestSurface"), relBounds.width(), relBounds.height(), 0); + TransactionUtils::fillSurfaceRGBA8(relative, relativeColor); + + Transaction{} + .setRelativeLayer(relative, mMainSurface, 1) + .setPosition(relative, relBounds.left, relBounds.top) + .apply(); + + { + // The relative should be on top of the FG control. + mCapture = screenshot(); + mCapture->expectColor(relBounds, relativeColor); + } + Transaction{}.detachChildren(mMainSurface).apply(); + + { + // Nothing should change at this point. + mCapture = screenshot(); + mCapture->expectColor(relBounds, relativeColor); + } + + Transaction{}.hide(relative).apply(); + + { + // Ensure that the relative was actually hidden, rather than + // being left in the detached but visible state. + mCapture = screenshot(); + mCapture->expectColor(mMainSurfaceBounds, mMainSurfaceColor); + } +} + +TEST_F(DetachChildren, DetachChildrenSameClient) { + Color childColor = {200, 200, 200, 255}; + Rect childBounds = Rect(74, 74, 84, 84); + sp<SurfaceControl> child = createLayer(String8("Child surface"), childBounds.width(), + childBounds.height(), 0, mMainSurface.get()); + ASSERT_TRUE(child->isValid()); + + TransactionUtils::fillSurfaceRGBA8(child, childColor); + + asTransaction([&](Transaction& t) { + t.show(child); + t.setPosition(child, childBounds.left - mMainSurfaceBounds.left, + childBounds.top - mMainSurfaceBounds.top); + }); + + { + mCapture = screenshot(); + // Expect main color around the child surface + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectColor(childBounds, childColor); + } + + asTransaction([&](Transaction& t) { t.detachChildren(mMainSurface); }); + + asTransaction([&](Transaction& t) { t.hide(child); }); + + // Since the child has the same client as the parent, it will not get + // detached and will be hidden. + { + mCapture = screenshot(); + mCapture->expectColor(mMainSurfaceBounds, mMainSurfaceColor); + } +} + +TEST_F(DetachChildren, DetachChildrenDifferentClient) { + Color childColor = {200, 200, 200, 255}; + Rect childBounds = Rect(74, 74, 84, 84); + + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + createSurface(newComposerClient, "New Child Test Surface", childBounds.width(), + childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get()); + ASSERT_TRUE(childNewClient->isValid()); + + TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor); + + asTransaction([&](Transaction& t) { + t.show(childNewClient); + t.setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left, + childBounds.top - mMainSurfaceBounds.top); + }); + + { + mCapture = screenshot(); + // Expect main color around the child surface + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectColor(childBounds, childColor); + } + + asTransaction([&](Transaction& t) { t.detachChildren(mMainSurface); }); + + asTransaction([&](Transaction& t) { t.hide(childNewClient); }); + + // Nothing should have changed. + { + mCapture = screenshot(); + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectColor(childBounds, childColor); + } +} + +TEST_F(DetachChildren, DetachChildrenThenAttach) { + Color childColor = {200, 200, 200, 255}; + Rect childBounds = Rect(74, 74, 84, 84); + + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + createSurface(newComposerClient, "New Child Test Surface", childBounds.width(), + childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get()); + ASSERT_TRUE(childNewClient->isValid()); + + TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor); + + Transaction() + .show(childNewClient) + .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left, + childBounds.top - mMainSurfaceBounds.top) + .apply(); + + { + mCapture = screenshot(); + // Expect main color around the child surface + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectColor(childBounds, childColor); + } + + Transaction().detachChildren(mMainSurface).apply(); + Transaction().hide(childNewClient).apply(); + + // Nothing should have changed. + { + mCapture = screenshot(); + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectColor(childBounds, childColor); + } + + Color newParentColor = Color::RED; + Rect newParentBounds = Rect(20, 20, 52, 52); + sp<SurfaceControl> newParentSurface = + createLayer(String8("New Parent Surface"), newParentBounds.width(), + newParentBounds.height(), 0); + TransactionUtils::fillSurfaceRGBA8(newParentSurface, newParentColor); + Transaction() + .setLayer(newParentSurface, INT32_MAX - 1) + .show(newParentSurface) + .setPosition(newParentSurface, newParentBounds.left, newParentBounds.top) + .reparent(childNewClient, newParentSurface) + .apply(); + { + mCapture = screenshot(); + // Child is now hidden. + mCapture->expectColor(newParentBounds, newParentColor); + } +} + +TEST_F(DetachChildren, DetachChildrenWithDeferredTransaction) { + Color childColor = {200, 200, 200, 255}; + Rect childBounds = Rect(74, 74, 84, 84); + + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + createSurface(newComposerClient, "New Child Test Surface", childBounds.width(), + childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get()); + ASSERT_TRUE(childNewClient->isValid()); + + TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor); + + Transaction() + .show(childNewClient) + .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left, + childBounds.top - mMainSurfaceBounds.top) + .apply(); + + { + mCapture = screenshot(); + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectColor(childBounds, childColor); + } + + Transaction() + .deferTransactionUntil_legacy(childNewClient, mMainSurface, + mMainSurface->getSurface()->getNextFrameNumber()) + .apply(); + Transaction().detachChildren(mMainSurface).apply(); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mMainSurface, Color::RED, + mMainSurfaceBounds.width(), + mMainSurfaceBounds.height())); + + // BufferLayer can still dequeue buffers even though there's a detached layer with a + // deferred transaction. + { + SCOPED_TRACE("new buffer"); + mCapture = screenshot(); + mCapture->expectBorder(childBounds, Color::RED); + mCapture->expectColor(childBounds, childColor); + } +} + +/** + * Tests that a deferring transaction on an already detached layer will be dropped gracefully and + * allow the barrier layer to dequeue buffers. + * + * Fixes b/150924737 - buffer cannot be latched because it waits for a detached layer + * to commit its pending states. + */ +TEST_F(DetachChildren, DeferredTransactionOnDetachedChildren) { + Color childColor = {200, 200, 200, 255}; + Rect childBounds = Rect(74, 74, 84, 84); + + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + createSurface(newComposerClient, "New Child Test Surface", childBounds.width(), + childBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get()); + ASSERT_TRUE(childNewClient->isValid()); + + TransactionUtils::fillSurfaceRGBA8(childNewClient, childColor); + + Transaction() + .show(childNewClient) + .setPosition(childNewClient, childBounds.left - mMainSurfaceBounds.left, + childBounds.top - mMainSurfaceBounds.top) + .apply(); + + { + mCapture = screenshot(); + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectColor(childBounds, childColor); + } + + Transaction().detachChildren(mMainSurface).apply(); + Transaction() + .setCrop_legacy(childNewClient, {0, 0, childBounds.width(), childBounds.height()}) + .deferTransactionUntil_legacy(childNewClient, mMainSurface, + mMainSurface->getSurface()->getNextFrameNumber()) + .apply(); + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mMainSurface, Color::RED, + mMainSurfaceBounds.width(), + mMainSurfaceBounds.height())); + + // BufferLayer can still dequeue buffers even though there's a detached layer with a + // deferred transaction. + { + SCOPED_TRACE("new buffer"); + mCapture = screenshot(); + mCapture->expectBorder(childBounds, Color::RED); + mCapture->expectColor(childBounds, childColor); + } +} + +TEST_F(DetachChildren, ReparentParentLayerOfDetachedChildren) { + Color childColor = {200, 200, 200, 255}; + Rect childBounds = Rect(74, 74, 94, 94); + Color grandchildColor = Color::RED; + Rect grandchildBounds = Rect(80, 80, 90, 90); + + sp<SurfaceComposerClient> newClient1 = new SurfaceComposerClient; + sp<SurfaceComposerClient> newClient2 = new SurfaceComposerClient; + + sp<SurfaceControl> childSurface = + createSurface(newClient1, "Child surface", childBounds.width(), childBounds.height(), + PIXEL_FORMAT_RGBA_8888, 0, mMainSurface.get()); + sp<SurfaceControl> grandchildSurface = + createSurface(newClient2, "Grandchild Surface", grandchildBounds.width(), + grandchildBounds.height(), PIXEL_FORMAT_RGBA_8888, 0, childSurface.get()); + + TransactionUtils::fillSurfaceRGBA8(childSurface, childColor); + TransactionUtils::fillSurfaceRGBA8(grandchildSurface, grandchildColor); + + Transaction() + .show(childSurface) + .show(grandchildSurface) + .setPosition(childSurface, childBounds.left - mMainSurfaceBounds.left, + childBounds.top - mMainSurfaceBounds.top) + .setPosition(grandchildSurface, grandchildBounds.left - childBounds.left, + grandchildBounds.top - childBounds.top) + .apply(); + + { + mCapture = screenshot(); + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectBorder(grandchildBounds, childColor); + mCapture->expectColor(grandchildBounds, grandchildColor); + } + + Transaction().detachChildren(childSurface).apply(); + + // Remove main surface offscreen + Transaction().reparent(mMainSurface, nullptr).apply(); + { + mCapture = screenshot(); + mCapture->expectColor(mMainSurfaceBounds, Color::BLACK); + } + + Transaction().reparent(mMainSurface, mBlackBgSurface).apply(); + { + mCapture = screenshot(); + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectBorder(grandchildBounds, childColor); + mCapture->expectColor(grandchildBounds, grandchildColor); + } + + Transaction().hide(grandchildSurface).apply(); + + // grandchild is still detached so it will not hide + { + mCapture = screenshot(); + mCapture->expectBorder(childBounds, mMainSurfaceColor); + mCapture->expectBorder(grandchildBounds, childColor); + mCapture->expectColor(grandchildBounds, grandchildColor); + } +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp index debfe83577..3a8b40fd67 100644 --- a/services/surfaceflinger/tests/DisplayConfigs_test.cpp +++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp @@ -14,15 +14,17 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" +#include <gtest/gtest.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <private/gui/ComposerService.h> +#include <ui/DisplayConfig.h> +#include <utils/Errors.h> +#include <utils/Vector.h> -#include <thread> -#include "LayerTransactionTest.h" -namespace android { +#include "utils/TransactionUtils.h" -using android::hardware::graphics::common::V1_1::BufferUsage; +namespace android { ::testing::Environment* const binderEnv = ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); @@ -31,32 +33,54 @@ using android::hardware::graphics::common::V1_1::BufferUsage; * Test class for setting display configs and passing around refresh rate ranges. */ class RefreshRateRangeTest : public ::testing::Test { -protected: - void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); } - - sp<IBinder> mDisplayToken; -}; - -TEST_F(RefreshRateRangeTest, setAllConfigs) { +private: int32_t initialDefaultConfig; + bool initialAllowGroupSwitching; float initialPrimaryMin; float initialPrimaryMax; float initialAppRequestMin; float initialAppRequestMax; - status_t res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, - &initialDefaultConfig, - &initialPrimaryMin, - &initialPrimaryMax, - &initialAppRequestMin, - &initialAppRequestMax); - ASSERT_EQ(res, NO_ERROR); +protected: + void SetUp() override { + mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); + status_t res = + SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, + &initialDefaultConfig, + &initialAllowGroupSwitching, + &initialPrimaryMin, + &initialPrimaryMax, + &initialAppRequestMin, + &initialAppRequestMax); + ASSERT_EQ(res, NO_ERROR); + } + + void TearDown() override { + status_t res = + SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, + initialDefaultConfig, + initialAllowGroupSwitching, + initialPrimaryMin, + initialPrimaryMax, + initialAppRequestMin, + initialAppRequestMax); + ASSERT_EQ(res, NO_ERROR); + } + + void testSetAllowGroupSwitching(bool allowGroupSwitching); + + sp<IBinder> mDisplayToken; +}; + +TEST_F(RefreshRateRangeTest, setAllConfigs) { Vector<DisplayConfig> configs; - res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs); + status_t res = SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &configs); ASSERT_EQ(res, NO_ERROR); + ASSERT_GT(configs.size(), 0); for (size_t i = 0; i < configs.size(); i++) { - res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, i, + res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, + static_cast<int32_t>(i), false, configs[i].refreshRate, configs[i].refreshRate, configs[i].refreshRate, @@ -64,31 +88,58 @@ TEST_F(RefreshRateRangeTest, setAllConfigs) { ASSERT_EQ(res, NO_ERROR); int defaultConfig; + bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; float appRequestRefreshRateMin; float appRequestRefreshRateMax; res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig, + &allowGroupSwitching, &primaryRefreshRateMin, &primaryRefreshRateMax, &appRequestRefreshRateMin, &appRequestRefreshRateMax); ASSERT_EQ(res, NO_ERROR); ASSERT_EQ(defaultConfig, i); + ASSERT_EQ(allowGroupSwitching, false); ASSERT_EQ(primaryRefreshRateMin, configs[i].refreshRate); ASSERT_EQ(primaryRefreshRateMax, configs[i].refreshRate); ASSERT_EQ(appRequestRefreshRateMin, configs[i].refreshRate); ASSERT_EQ(appRequestRefreshRateMax, configs[i].refreshRate); } +} + +void RefreshRateRangeTest::testSetAllowGroupSwitching(bool allowGroupSwitching) { + status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 0, + allowGroupSwitching, 0.f, + 90.f, 0.f, 90.f); + ASSERT_EQ(res, NO_ERROR); + int defaultConfig; + bool newAllowGroupSwitching; + float primaryRefreshRateMin; + float primaryRefreshRateMax; + float appRequestRefreshRateMin; + float appRequestRefreshRateMax; - res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, initialDefaultConfig, - initialPrimaryMin, initialPrimaryMax, - initialAppRequestMin, - initialAppRequestMax); + res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfig, + &newAllowGroupSwitching, + &primaryRefreshRateMin, + &primaryRefreshRateMax, + &appRequestRefreshRateMin, + &appRequestRefreshRateMax); ASSERT_EQ(res, NO_ERROR); + ASSERT_EQ(defaultConfig, 0); + ASSERT_EQ(newAllowGroupSwitching, allowGroupSwitching); + ASSERT_EQ(primaryRefreshRateMin, 0.f); + ASSERT_EQ(primaryRefreshRateMax, 90.f); + ASSERT_EQ(appRequestRefreshRateMin, 0.f); + ASSERT_EQ(appRequestRefreshRateMax, 90.f); } -} // namespace android +TEST_F(RefreshRateRangeTest, setAllowGroupSwitching) { + testSetAllowGroupSwitching(true); + testSetAllowGroupSwitching(false); + testSetAllowGroupSwitching(true); +} -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/tests/EffectLayer_test.cpp b/services/surfaceflinger/tests/EffectLayer_test.cpp index 3dca3916e4..fafb49efba 100644 --- a/services/surfaceflinger/tests/EffectLayer_test.cpp +++ b/services/surfaceflinger/tests/EffectLayer_test.cpp @@ -51,7 +51,7 @@ TEST_F(EffectLayerTest, DefaultEffectLayerHasSolidBlackFill) { sp<SurfaceControl> effectLayer = mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect, - mParentLayer.get()); + mParentLayer->getHandle()); EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl"; asTransaction([&](Transaction& t) { @@ -72,7 +72,7 @@ TEST_F(EffectLayerTest, EffectLayerWithNoFill) { PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect | ISurfaceComposerClient::eNoColorFill, - mParentLayer.get()); + mParentLayer->getHandle()); EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl"; asTransaction([&](Transaction& t) { @@ -93,7 +93,7 @@ TEST_F(EffectLayerTest, EffectLayerCanSetColor) { PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect | ISurfaceComposerClient::eNoColorFill, - mParentLayer.get()); + mParentLayer->getHandle()); EXPECT_NE(nullptr, effectLayer.get()) << "failed to create SurfaceControl"; asTransaction([&](Transaction& t) { diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp index 42d1f5aa6c..152d2d26f4 100644 --- a/services/surfaceflinger/tests/InvalidHandles_test.cpp +++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + #include <binder/Binder.h> #include <gtest/gtest.h> @@ -22,6 +26,7 @@ #include <gui/SurfaceComposerClient.h> #include <private/gui/ComposerService.h> #include <ui/Rect.h> +#include "utils/ScreenshotUtils.h" namespace android { namespace { @@ -51,16 +56,16 @@ TEST_F(InvalidHandleTest, createSurfaceInvalidHandle) { auto notSc = makeNotSurfaceControl(); ASSERT_EQ(nullptr, mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0, - notSc.get()) + notSc->getHandle()) .get()); } TEST_F(InvalidHandleTest, captureLayersInvalidHandle) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<GraphicBuffer> outBuffer; + LayerCaptureArgs args; + args.layerHandle = mNotSc->getHandle(); - ASSERT_EQ(NAME_NOT_FOUND, - sf->captureLayers(mNotSc->getHandle(), &outBuffer, Rect::EMPTY_RECT, 1.0f)); + ScreenCaptureResults captureResults; + ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults)); } } // namespace diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index 83e5060830..52e1a4d967 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -179,19 +179,6 @@ TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) { } } -TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition - Transaction() - .setSize(layer, 64, 64) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .apply(); - getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED); -} - TEST_P(LayerRenderTypeTransactionTest, CreateLayer_BufferState) { uint32_t transformHint = ui::Transform::ROT_INVALID; sp<SurfaceControl> layer; @@ -211,16 +198,13 @@ void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) switch (layerType) { case ISurfaceComposerClient::eFXSurfaceBufferQueue: - Transaction() - .setPosition(layerG, 16, 16) - .setRelativeLayer(layerG, layerR->getHandle(), 1) - .apply(); + Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply(); break; case ISurfaceComposerClient::eFXSurfaceBufferState: Transaction() .setFrame(layerR, Rect(0, 0, 32, 32)) .setFrame(layerG, Rect(16, 16, 48, 48)) - .setRelativeLayer(layerG, layerR->getHandle(), 1) + .setRelativeLayer(layerG, layerR, 1) .apply(); break; default: @@ -233,7 +217,7 @@ void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN); } - Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply(); + Transaction().setRelativeLayer(layerG, layerR, -1).apply(); { SCOPED_TRACE("layerG below"); auto shot = getScreenCapture(); @@ -266,7 +250,7 @@ void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) case ISurfaceComposerClient::eFXSurfaceBufferQueue: Transaction() .setPosition(layerG, 8, 8) - .setRelativeLayer(layerG, layerR->getHandle(), 3) + .setRelativeLayer(layerG, layerR, 3) .setPosition(layerB, 16, 16) .setLayer(layerB, mLayerZBase + 2) .apply(); @@ -275,7 +259,7 @@ void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) Transaction() .setFrame(layerR, Rect(0, 0, 32, 32)) .setFrame(layerG, Rect(8, 8, 40, 40)) - .setRelativeLayer(layerG, layerR->getHandle(), 3) + .setRelativeLayer(layerG, layerR, 3) .setFrame(layerB, Rect(16, 16, 48, 48)) .setLayer(layerB, mLayerZBase + 2) .apply(); @@ -303,7 +287,7 @@ void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) } // layerR = 4, layerG = layerR - 3, layerB = 2 - Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply(); + Transaction().setRelativeLayer(layerG, layerR, -3).apply(); { SCOPED_TRACE("layerB < (layerG < layerR)"); auto shot = getScreenCapture(); @@ -810,7 +794,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) { // channel) should be less than one const uint8_t tolerance = 1; Transaction() - .reparent(colorLayer, parentLayer->getHandle()) + .reparent(colorLayer, parentLayer) .setColor(colorLayer, color) .setAlpha(parentLayer, alpha) .setLayer(parentLayer, mLayerZBase + 1) @@ -953,40 +937,6 @@ TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) { } } -TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition - Transaction() - .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f) - .setSize(layer, 64, 64) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .apply(); - getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED); -} - -TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE)); - - // XXX SCALE_CROP is not respected; calling setSize and - // setOverrideScalingMode in separate transactions does not work - // (b/69315456) - Transaction() - .setSize(layer, 64, 16) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .apply(); - { - SCOPED_TRACE("SCALE_TO_WINDOW"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE, true /* filtered */); - } -} - TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); @@ -1225,7 +1175,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) { child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); - Transaction().reparent(child, parent->getHandle()).apply(); + Transaction().reparent(child, parent).apply(); // A layer will default to the frame of its parent auto shot = getScreenCapture(); @@ -1242,7 +1192,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) { child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); - Transaction().reparent(child, parent->getHandle()).apply(); + Transaction().reparent(child, parent).apply(); // A layer will default to the frame of its parent auto shot = getScreenCapture(); @@ -1272,7 +1222,7 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) { parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); ASSERT_NO_FATAL_FAILURE( child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - Transaction().reparent(child, parent->getHandle()).apply(); + Transaction().reparent(child, parent).apply(); ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32)); Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply(); diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp new file mode 100644 index 0000000000..e66df4a2b9 --- /dev/null +++ b/services/surfaceflinger/tests/LayerState_test.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <binder/Binder.h> +#include <binder/Parcel.h> + +#include <gui/LayerState.h> + +namespace android { +namespace test { + +TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { + DisplayCaptureArgs args; + args.pixelFormat = ui::PixelFormat::RGB_565; + args.sourceCrop = Rect(0, 0, 500, 200); + args.frameScale = 2; + args.captureSecureLayers = true; + args.displayToken = new BBinder(); + args.width = 10; + args.height = 20; + args.useIdentityTransform = true; + + Parcel p; + args.write(p); + p.setDataPosition(0); + + DisplayCaptureArgs args2; + args2.read(p); + + ASSERT_EQ(args.pixelFormat, args2.pixelFormat); + ASSERT_EQ(args.sourceCrop, args2.sourceCrop); + ASSERT_EQ(args.frameScale, args2.frameScale); + ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers); + ASSERT_EQ(args.displayToken, args2.displayToken); + ASSERT_EQ(args.width, args2.width); + ASSERT_EQ(args.height, args2.height); + ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform); +} + +TEST(LayerStateTest, ParcellingLayerCaptureArgs) { + LayerCaptureArgs args; + args.pixelFormat = ui::PixelFormat::RGB_565; + args.sourceCrop = Rect(0, 0, 500, 200); + args.frameScale = 2; + args.captureSecureLayers = true; + args.layerHandle = new BBinder(); + args.excludeHandles = {new BBinder(), new BBinder()}; + args.childrenOnly = false; + + Parcel p; + args.write(p); + p.setDataPosition(0); + + LayerCaptureArgs args2; + args2.read(p); + + ASSERT_EQ(args.pixelFormat, args2.pixelFormat); + ASSERT_EQ(args.sourceCrop, args2.sourceCrop); + ASSERT_EQ(args.frameScale, args2.frameScale); + ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers); + ASSERT_EQ(args.layerHandle, args2.layerHandle); + ASSERT_EQ(args.excludeHandles, args2.excludeHandles); + ASSERT_EQ(args.childrenOnly, args2.childrenOnly); +} + +TEST(LayerStateTest, ParcellingScreenCaptureResults) { + ScreenCaptureResults results; + results.buffer = new GraphicBuffer(100, 200, PIXEL_FORMAT_RGBA_8888, 1, 0); + results.capturedSecureLayers = true; + results.capturedDataspace = ui::Dataspace::DISPLAY_P3; + results.result = BAD_VALUE; + + Parcel p; + results.write(p); + p.setDataPosition(0); + + ScreenCaptureResults results2; + results2.read(p); + + // GraphicBuffer object is reallocated so compare the data in the graphic buffer + // rather than the object itself + ASSERT_EQ(results.buffer->getWidth(), results2.buffer->getWidth()); + ASSERT_EQ(results.buffer->getHeight(), results2.buffer->getHeight()); + ASSERT_EQ(results.buffer->getPixelFormat(), results2.buffer->getPixelFormat()); + ASSERT_EQ(results.capturedSecureLayers, results2.capturedSecureLayers); + ASSERT_EQ(results.capturedDataspace, results2.capturedDataspace); + ASSERT_EQ(results.result, results2.result); +} + +} // namespace test +} // namespace android diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h index f3e11d811c..da71dad2d4 100644 --- a/services/surfaceflinger/tests/LayerTransactionTest.h +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -16,6 +16,10 @@ #pragma once +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + #include <gtest/gtest.h> #include <gui/ISurfaceComposer.h> #include <gui/SurfaceComposerClient.h> @@ -40,6 +44,8 @@ protected: sp<ISurfaceComposer> sf(ComposerService::getComposerService()); ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed)); + + mCaptureArgs.displayToken = mDisplay; } virtual void TearDown() { @@ -73,8 +79,9 @@ protected: PixelFormat format, uint32_t flags, SurfaceControl* parent = nullptr, uint32_t* outTransformHint = nullptr) { - auto layer = client->createSurface(String8(name), width, height, format, flags, parent, - LayerMetadata(), outTransformHint); + sp<IBinder> parentHandle = (parent) ? parent->getHandle() : nullptr; + auto layer = client->createSurface(String8(name), width, height, format, flags, + parentHandle, LayerMetadata(), outTransformHint); EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl"; return layer; } @@ -249,6 +256,9 @@ protected: sp<SurfaceControl> mBlackBgSurface; bool mColorManagementUsed; + DisplayCaptureArgs mCaptureArgs; + ScreenCaptureResults mCaptureResults; + private: void SetUpDisplay() { mDisplay = mClient->getInternalDisplayToken(); @@ -294,3 +304,6 @@ private: }; } // namespace android + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp index 1f8f7ed892..ef992d6a40 100644 --- a/services/surfaceflinger/tests/LayerTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp @@ -18,7 +18,6 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" -#include <private/android_filesystem_config.h> #include <thread> #include "LayerTransactionTest.h" @@ -26,43 +25,6 @@ namespace android { using android::hardware::graphics::common::V1_1::BufferUsage; -TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); - sp<GraphicBuffer> outBuffer; - Transaction() - .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) - .apply(true); - ASSERT_EQ(PERMISSION_DENIED, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); - - UIDFaker f(AID_SYSTEM); - - // By default the system can capture screenshots with secure layers but they - // will be blacked out - ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); - - { - SCOPED_TRACE("as system"); - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } - - // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able - // to receive them...we are expected to take care with the results. - bool outCapturedSecureLayers; - ASSERT_EQ(NO_ERROR, - composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0, - 0, false, ui::ROTATION_0, true)); - ASSERT_EQ(true, outCapturedSecureLayers); - ScreenCapture sc(outBuffer); - sc.expectColor(Rect(0, 0, 32, 32), Color::RED); -} - TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE( @@ -88,7 +50,7 @@ TEST_F(LayerTransactionTest, ReparentToSelf) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - Transaction().reparent(layer, layer->getHandle()).apply(); + Transaction().reparent(layer, layer).apply(); { // We expect the transaction to be silently dropped, but for SurfaceFlinger diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp index 7d4314f4df..c57ad4364a 100644 --- a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -87,10 +87,7 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) { ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); - Transaction() - .setPosition(layerG, 16, 16) - .setRelativeLayer(layerG, layerR->getHandle(), 1) - .apply(); + Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply(); Transaction().reparent(layerG, nullptr).apply(); @@ -154,10 +151,7 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) { ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); - Transaction() - .reparent(layerR, parent->getHandle()) - .reparent(layerG, parent->getHandle()) - .apply(); + Transaction().reparent(layerR, parent).reparent(layerG, parent).apply(); Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply(); { SCOPED_TRACE("layerR"); @@ -241,7 +235,7 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusRotated) { auto transaction = Transaction() .setCornerRadius(parent, cornerRadius) .setCrop_legacy(parent, Rect(0, 0, size, size)) - .reparent(child, parent->getHandle()) + .reparent(child, parent) .setPosition(child, 0, size) // Rotate by half PI .setMatrix(child, 0.0f, -1.0f, 1.0f, 0.0f); @@ -283,14 +277,14 @@ TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) { Transaction() .setCornerRadius(parent, cornerRadius) .setCrop_legacy(parent, Rect(0, 0, size, size)) - .reparent(child, parent->getHandle()) + .reparent(child, parent) .setPosition(child, 0, size / 2) .apply(); } else { Transaction() .setCornerRadius(parent, cornerRadius) .setFrame(parent, Rect(0, 0, size, size)) - .reparent(child, parent->getHandle()) + .reparent(child, parent) .setFrame(child, Rect(0, size / 2, size, size)) .apply(); } diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp index 84780ba73b..f8a0bc1124 100644 --- a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp @@ -54,15 +54,17 @@ TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) { ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32)); ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32)); - Transaction().reparent(layerB, parent->getHandle()).apply(); + Transaction().reparent(layerB, parent).apply(); // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2 - Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply(); + Transaction().setRelativeLayer(layerG, layerR, -1).setLayer(layerB, -2).apply(); std::unique_ptr<ScreenCapture> screenshot; // only layerB is in this range - sp<IBinder> parentHandle = parent->getHandle(); - ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32)); + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = parent->getHandle(); + captureArgs.sourceCrop = {0, 0, 32, 32}; + ScreenCapture::captureLayers(&screenshot, captureArgs); screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); } @@ -86,10 +88,7 @@ TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) { .setCrop_legacy(childLayer, Rect(0, 0, 20, 30)) .apply(); - Transaction() - .setRelativeLayer(childLayer, parent->getHandle(), -1) - .setLayer(childLayer, 1) - .apply(); + Transaction().setRelativeLayer(childLayer, parent, -1).setLayer(childLayer, 1).apply(); { SCOPED_TRACE("setLayer above"); @@ -99,10 +98,7 @@ TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) { screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED); } - Transaction() - .setLayer(childLayer, 1) - .setRelativeLayer(childLayer, parent->getHandle(), -1) - .apply(); + Transaction().setLayer(childLayer, 1).setRelativeLayer(childLayer, parent, -1).apply(); { SCOPED_TRACE("setRelative below"); @@ -139,7 +135,7 @@ TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) { .setLayer(relativeParent, mLayerZBase) .apply(); - Transaction().setRelativeLayer(childLayer, relativeParent->getHandle(), 1).apply(); + Transaction().setRelativeLayer(childLayer, relativeParent, 1).apply(); { SCOPED_TRACE("setLayer above"); @@ -165,17 +161,21 @@ TEST_P(LayerTypeTransactionTest, SetFlagsSecure) { ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); sp<GraphicBuffer> outBuffer; Transaction() .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) .apply(true); - ASSERT_EQ(PERMISSION_DENIED, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); + + DisplayCaptureArgs args; + args.displayToken = mDisplay; + + ScreenCaptureResults captureResults; + ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(args, captureResults)); Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true); - ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); + ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, captureResults)); } + TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) { sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp index cdd9d92063..29473f20a4 100644 --- a/services/surfaceflinger/tests/LayerUpdate_test.cpp +++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp @@ -103,41 +103,6 @@ protected: sp<SurfaceControl> mSyncSurfaceControl; }; -TEST_F(LayerUpdateTest, RelativesAreNotDetached) { - std::unique_ptr<ScreenCapture> sc; - - sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0); - TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10); - waitForPostedBuffers(); - - Transaction{} - .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1) - .setPosition(relative, 64, 64) - .apply(); - - { - // The relative should be on top of the FG control. - ScreenCapture::captureScreen(&sc); - sc->checkPixel(64, 64, 10, 10, 10); - } - Transaction{}.detachChildren(mFGSurfaceControl).apply(); - - { - // Nothing should change at this point. - ScreenCapture::captureScreen(&sc); - sc->checkPixel(64, 64, 10, 10, 10); - } - - Transaction{}.hide(relative).apply(); - - { - // Ensure that the relative was actually hidden, rather than - // being left in the detached but visible state. - ScreenCapture::captureScreen(&sc); - sc->expectFGColor(64, 64); - } -} - class GeometryLatchingTest : public LayerUpdateTest { protected: void EXPECT_INITIAL_STATE(const char* trace) { @@ -208,13 +173,13 @@ TEST_F(LayerUpdateTest, DeferredTransactionTest) { // set up two deferred transactions on different frames asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.75); - t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), + t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl, mSyncSurfaceControl->getSurface()->getNextFrameNumber()); }); asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 128, 128); - t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), + t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl, mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1); }); @@ -515,9 +480,8 @@ TEST_F(ChildLayerTest, ReparentChildren) { mCapture->expectFGColor(84, 84); } - asTransaction([&](Transaction& t) { - t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle()); - }); + asTransaction( + [&](Transaction& t) { t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl); }); { mCapture = screenshot(); @@ -551,7 +515,7 @@ TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) { mCapture->expectFGColor(64, 64); } - asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); }); + asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl); }); { SCOPED_TRACE("After reparenting grandchild"); @@ -566,9 +530,7 @@ TEST_F(ChildLayerTest, ChildrenRelativeZSurvivesParentDestruction) { TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111); // draw grand child behind the foreground surface - asTransaction([&](Transaction& t) { - t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1); - }); + asTransaction([&](Transaction& t) { t.setRelativeLayer(mGrandChild, mFGSurfaceControl, -1); }); { SCOPED_TRACE("Child visible"); @@ -578,7 +540,7 @@ TEST_F(ChildLayerTest, ChildrenRelativeZSurvivesParentDestruction) { asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); - t.reparentChildren(mChild, mFGSurfaceControl->getHandle()); + t.reparentChildren(mChild, mFGSurfaceControl); }); { @@ -588,174 +550,6 @@ TEST_F(ChildLayerTest, ChildrenRelativeZSurvivesParentDestruction) { } } -TEST_F(ChildLayerTest, DetachChildrenSameClient) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 10, 10); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); }); - - asTransaction([&](Transaction& t) { t.hide(mChild); }); - - // Since the child has the same client as the parent, it will not get - // detached and will be hidden. - { - mCapture = screenshot(); - mCapture->expectFGColor(64, 64); - mCapture->expectFGColor(74, 74); - mCapture->expectFGColor(84, 84); - } -} - -TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { - sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient; - sp<SurfaceControl> mChildNewClient = - createSurface(mNewComposerClient, "New Child Test Surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - - ASSERT_TRUE(mChildNewClient->isValid()); - - TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200); - - asTransaction([&](Transaction& t) { - t.hide(mChild); - t.show(mChildNewClient); - t.setPosition(mChildNewClient, 10, 10); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); }); - - asTransaction([&](Transaction& t) { t.hide(mChildNewClient); }); - - // Nothing should have changed. - { - mCapture = screenshot(); - mCapture->expectFGColor(64, 64); - mCapture->expectChildColor(74, 74); - mCapture->expectFGColor(84, 84); - } -} - -TEST_F(ChildLayerTest, DetachChildrenThenAttach) { - sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; - sp<SurfaceControl> childNewClient = - newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - - ASSERT_TRUE(childNewClient != nullptr); - ASSERT_TRUE(childNewClient->isValid()); - - TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200); - - Transaction() - .hide(mChild) - .show(childNewClient) - .setPosition(childNewClient, 10, 10) - .setPosition(mFGSurfaceControl, 64, 64) - .apply(); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - Transaction().detachChildren(mFGSurfaceControl).apply(); - Transaction().hide(childNewClient).apply(); - - // Nothing should have changed. - { - mCapture = screenshot(); - mCapture->expectFGColor(64, 64); - mCapture->expectChildColor(74, 74); - mCapture->expectFGColor(84, 84); - } - - sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0); - fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32, - 32); - Transaction() - .setLayer(newParentSurface, INT32_MAX - 1) - .show(newParentSurface) - .setPosition(newParentSurface, 20, 20) - .reparent(childNewClient, newParentSurface->getHandle()) - .apply(); - { - mCapture = screenshot(); - // Child is now hidden. - mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED); - } -} -TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) { - sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; - sp<SurfaceControl> childNewClient = - newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - - ASSERT_TRUE(childNewClient != nullptr); - ASSERT_TRUE(childNewClient->isValid()); - - TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200); - - Transaction() - .hide(mChild) - .show(childNewClient) - .setPosition(childNewClient, 10, 10) - .setPosition(mFGSurfaceControl, 64, 64) - .apply(); - - { - mCapture = screenshot(); - Rect rect = Rect(74, 74, 84, 84); - mCapture->expectBorder(rect, Color{195, 63, 63, 255}); - mCapture->expectColor(rect, Color{200, 200, 200, 255}); - } - - Transaction() - .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(), - mFGSurfaceControl->getSurface()->getNextFrameNumber()) - .apply(); - Transaction().detachChildren(mFGSurfaceControl).apply(); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32)); - - // BufferLayer can still dequeue buffers even though there's a detached layer with a - // deferred transaction. - { - SCOPED_TRACE("new buffer"); - mCapture = screenshot(); - Rect rect = Rect(74, 74, 84, 84); - mCapture->expectBorder(rect, Color::RED); - mCapture->expectColor(rect, Color{200, 200, 200, 255}); - } -} - TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { asTransaction([&](Transaction& t) { t.show(mChild); @@ -772,7 +566,10 @@ TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { } asTransaction([&](Transaction& t) { - t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + mFGSurfaceControl->getSurface()->setScalingMode( + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // Resubmit buffer with new scaling mode + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); // We cause scaling by 2. t.setSize(mFGSurfaceControl, 128, 128); }); @@ -879,7 +676,10 @@ TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) { } asTransaction([&](Transaction& t) { - t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + mFGSurfaceControl->getSurface()->setScalingMode( + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // Resubmit buffer with new scaling mode + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); // Set a scaling by 2. t.setSize(mFGSurfaceControl, 128, 128); }); @@ -911,7 +711,10 @@ TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) { // Change the size of the foreground to 128 * 64 so we can test rotation as well. asTransaction([&](Transaction& t) { - t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + mFGSurfaceControl->getSurface()->setScalingMode( + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // Resubmit buffer with new scaling mode + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); t.setSize(mFGSurfaceControl, 128, 64); }); sp<Surface> s = mFGSurfaceControl->getSurface(); @@ -942,7 +745,7 @@ TEST_F(ChildLayerTest, Bug36858924) { // Show the child layer in a deferred transaction asTransaction([&](Transaction& t) { - t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(), + t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl, mFGSurfaceControl->getSurface()->getNextFrameNumber()); t.show(mChild); }); @@ -979,7 +782,7 @@ TEST_F(ChildLayerTest, Reparent) { mCapture->expectFGColor(84, 84); } - asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); }); + asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl); }); { mCapture = screenshot(); @@ -1041,7 +844,7 @@ TEST_F(ChildLayerTest, ReparentFromNoParent) { mCapture->checkPixel(10, 10, 63, 195, 63); } - asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); }); + asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl); }); { mCapture = screenshot(); @@ -1072,7 +875,7 @@ TEST_F(ChildLayerTest, ChildLayerRelativeLayer) { Transaction t; t.setLayer(relative, INT32_MAX) - .setRelativeLayer(mChild, relative->getHandle(), 1) + .setRelativeLayer(mChild, relative, 1) .setPosition(mFGSurfaceControl, 0, 0) .apply(true); @@ -1225,12 +1028,13 @@ TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) { TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) { sp<SurfaceControl> boundlessLayer = mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888, - 0 /* flags */, mFGSurfaceControl.get()); + 0 /* flags */, mFGSurfaceControl->getHandle()); ASSERT_TRUE(boundlessLayer != nullptr); ASSERT_TRUE(boundlessLayer->isValid()); sp<SurfaceControl> colorLayer = mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceEffect, boundlessLayer.get()); + ISurfaceComposerClient::eFXSurfaceEffect, + boundlessLayer->getHandle()); ASSERT_TRUE(colorLayer != nullptr); ASSERT_TRUE(colorLayer->isValid()); asTransaction([&](Transaction& t) { @@ -1287,432 +1091,6 @@ TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) { } } -class ScreenCaptureTest : public LayerUpdateTest { -protected: - std::unique_ptr<ScreenCapture> mCapture; -}; - -TEST_F(ScreenCaptureTest, CaptureSingleLayer) { - auto bgHandle = mBGSurfaceControl->getHandle(); - ScreenCapture::captureLayers(&mCapture, bgHandle); - mCapture->expectBGColor(0, 0); - // Doesn't capture FG layer which is at 64, 64 - mCapture->expectBGColor(64, 64); -} - -TEST_F(ScreenCaptureTest, CaptureLayerWithChild) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - - SurfaceComposerClient::Transaction().show(child).apply(true); - - // Captures mFGSurfaceControl layer and its child. - ScreenCapture::captureLayers(&mCapture, fgHandle); - mCapture->expectFGColor(10, 10); - mCapture->expectChildColor(0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - - SurfaceComposerClient::Transaction().show(child).apply(true); - - // Captures mFGSurfaceControl's child - ScreenCapture::captureChildLayers(&mCapture, fgHandle); - mCapture->checkPixel(10, 10, 0, 0, 0); - mCapture->expectChildColor(0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureLayerExclude) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); - - SurfaceComposerClient::Transaction() - .show(child) - .show(child2) - .setLayer(child, 1) - .setLayer(child2, 2) - .apply(true); - - // Child2 would be visible but its excluded, so we should see child1 color instead. - ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()}); - mCapture->checkPixel(10, 10, 0, 0, 0); - mCapture->checkPixel(0, 0, 200, 200, 200); -} - -// Like the last test but verifies that children are also exclude. -TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); - sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, child2.get()); - TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); - - SurfaceComposerClient::Transaction() - .show(child) - .show(child2) - .show(child3) - .setLayer(child, 1) - .setLayer(child2, 2) - .apply(true); - - // Child2 would be visible but its excluded, so we should see child1 color instead. - ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()}); - mCapture->checkPixel(10, 10, 0, 0, 0); - mCapture->checkPixel(0, 0, 200, 200, 200); -} - -TEST_F(ScreenCaptureTest, CaptureTransparent) { - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - - SurfaceComposerClient::Transaction().show(child).apply(true); - - auto childHandle = child->getHandle(); - - // Captures child - ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20}); - mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255}); - // Area outside of child's bounds is transparent. - mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0}); -} - -TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - ASSERT_NE(nullptr, child.get()) << "failed to create surface"; - sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100); - - SurfaceComposerClient::Transaction() - .show(child) - // Set relative layer above fg layer so should be shown above when computing all layers. - .setRelativeLayer(relative, fgHandle, 1) - .show(relative) - .apply(true); - - // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured. - ScreenCapture::captureLayers(&mCapture, fgHandle); - mCapture->expectFGColor(10, 10); - mCapture->expectChildColor(0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureRelativeInTree) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100); - - SurfaceComposerClient::Transaction() - .show(child) - // Set relative layer below fg layer but relative to child layer so it should be shown - // above child layer. - .setLayer(relative, -1) - .setRelativeLayer(relative, child->getHandle(), 1) - .show(relative) - .apply(true); - - // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its - // relative value should be taken into account, placing it above child layer. - ScreenCapture::captureLayers(&mCapture, fgHandle); - mCapture->expectFGColor(10, 10); - // Relative layer is showing on top of child layer - mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255}); -} - -TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) { - sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get()); - SurfaceComposerClient::Transaction().show(child).apply(true); - - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<GraphicBuffer> outBuffer; - Rect sourceCrop(0, 0, 10, 10); - ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop)); - ScreenCapture sc(outBuffer); - - sc.expectColor(Rect(0, 0, 9, 9), Color::RED); -} - -TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) { - sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get()); - Rect layerCrop(0, 0, 10, 10); - SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true); - - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<GraphicBuffer> outBuffer; - Rect sourceCrop = Rect(); - ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop)); - ScreenCapture sc(outBuffer); - - sc.expectColor(Rect(0, 0, 9, 9), Color::RED); -} - -TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) { - sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get()); - SurfaceComposerClient::Transaction().show(child).apply(true); - - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<GraphicBuffer> outBuffer; - Rect sourceCrop = Rect(); - - ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop)); -} - -TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) { - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - SurfaceComposerClient::Transaction().show(child).apply(true); - - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sp<GraphicBuffer> outBuffer; - Rect sourceCrop = Rect(); - ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop)); - - TransactionUtils::fillSurfaceRGBA8(child, Color::RED); - SurfaceComposerClient::Transaction().apply(true); - ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop)); - ScreenCapture sc(outBuffer); - sc.expectColor(Rect(0, 0, 9, 9), Color::RED); -} - -// In the following tests we verify successful skipping of a parent layer, -// so we use the same verification logic and only change how we mutate -// the parent layer to verify that various properties are ignored. -class ScreenCaptureChildOnlyTest : public LayerUpdateTest { -public: - void SetUp() override { - LayerUpdateTest::SetUp(); - - mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, - mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200); - - SurfaceComposerClient::Transaction().show(mChild).apply(true); - } - - void verify(std::function<void()> verifyStartingState) { - // Verify starting state before a screenshot is taken. - verifyStartingState(); - - // Verify child layer does not inherit any of the properties of its - // parent when its screenshot is captured. - auto fgHandle = mFGSurfaceControl->getHandle(); - ScreenCapture::captureChildLayers(&mCapture, fgHandle); - mCapture->checkPixel(10, 10, 0, 0, 0); - mCapture->expectChildColor(0, 0); - - // Verify all assumptions are still true after the screenshot is taken. - verifyStartingState(); - } - - std::unique_ptr<ScreenCapture> mCapture; - sp<SurfaceControl> mChild; -}; - -// Regression test b/76099859 -TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) { - SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true); - - // Even though the parent is hidden we should still capture the child. - - // Before and after reparenting, verify child is properly hidden - // when rendering full-screen. - verify([&] { screenshot()->expectBGColor(64, 64); }); -} - -TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) { - SurfaceComposerClient::Transaction() - .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1)) - .apply(true); - - // Even though the parent is cropped out we should still capture the child. - - // Before and after reparenting, verify child is cropped by parent. - verify([&] { screenshot()->expectBGColor(65, 65); }); -} - -// Regression test b/124372894 -TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) { - SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true); - - // We should not inherit the parent scaling. - - // Before and after reparenting, verify child is properly scaled. - verify([&] { screenshot()->expectChildColor(80, 80); }); -} - -TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - - sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, - PIXEL_FORMAT_RGBA_8888, 0, child.get()); - - TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50); - SurfaceComposerClient::Transaction() - .show(child) - .setPosition(grandchild, 5, 5) - .show(grandchild) - .apply(true); - - // Captures mFGSurfaceControl, its child, and the grandchild. - ScreenCapture::captureLayers(&mCapture, fgHandle); - mCapture->expectFGColor(10, 10); - mCapture->expectChildColor(0, 0); - mCapture->checkPixel(5, 5, 50, 50, 50); -} - -TEST_F(ScreenCaptureTest, CaptureChildOnly) { - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - auto childHandle = child->getHandle(); - - SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true); - - // Captures only the child layer, and not the parent. - ScreenCapture::captureLayers(&mCapture, childHandle); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(9, 9); -} - -TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) { - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); - auto childHandle = child->getHandle(); - - sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, - PIXEL_FORMAT_RGBA_8888, 0, child.get()); - TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50); - - SurfaceComposerClient::Transaction() - .show(child) - .setPosition(grandchild, 5, 5) - .show(grandchild) - .apply(true); - - auto grandchildHandle = grandchild->getHandle(); - - // Captures only the grandchild. - ScreenCapture::captureLayers(&mCapture, grandchildHandle); - mCapture->checkPixel(0, 0, 50, 50, 50); - mCapture->checkPixel(4, 4, 50, 50, 50); -} - -TEST_F(ScreenCaptureTest, CaptureCrop) { - sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); - sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, - PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); - - SurfaceComposerClient::Transaction() - .setLayer(redLayer, INT32_MAX - 1) - .show(redLayer) - .show(blueLayer) - .apply(true); - - auto redLayerHandle = redLayer->getHandle(); - - // Capturing full screen should have both red and blue are visible. - ScreenCapture::captureLayers(&mCapture, redLayerHandle); - mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); - // red area below the blue area - mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED); - // red area to the right of the blue area - mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); - - const Rect crop = Rect(0, 0, 30, 30); - ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop); - // Capturing the cropped screen, cropping out the shown red area, should leave only the blue - // area visible. - mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); - mCapture->checkPixel(30, 30, 0, 0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureSize) { - sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); - sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, - PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); - - SurfaceComposerClient::Transaction() - .setLayer(redLayer, INT32_MAX - 1) - .show(redLayer) - .show(blueLayer) - .apply(true); - - auto redLayerHandle = redLayer->getHandle(); - - // Capturing full screen should have both red and blue are visible. - ScreenCapture::captureLayers(&mCapture, redLayerHandle); - mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); - // red area below the blue area - mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED); - // red area to the right of the blue area - mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); - - ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5); - // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area. - mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE); - // red area below the blue area - mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED); - // red area to the right of the blue area - mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED); - mCapture->checkPixel(30, 30, 0, 0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { - sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); - - auto redLayerHandle = redLayer->getHandle(); - Transaction().reparent(redLayer, nullptr).apply(); - redLayer.clear(); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - - // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0)); -} } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp index b49bd54599..16826c1f61 100644 --- a/services/surfaceflinger/tests/MirrorLayer_test.cpp +++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp @@ -68,7 +68,7 @@ TEST_F(MirrorLayerTest, MirrorColorLayer) { // Add mirrorLayer as child of mParentLayer so it's shown on the display Transaction() - .reparent(mirrorLayer, mParentLayer->getHandle()) + .reparent(mirrorLayer, mParentLayer) .setPosition(mirrorLayer, 500, 500) .show(mirrorLayer) .apply(); @@ -127,7 +127,7 @@ TEST_F(MirrorLayerTest, MirrorColorLayer) { } // Add grandchild layer to offscreen layer - Transaction().reparent(grandchild, mChildLayer->getHandle()).apply(); + Transaction().reparent(grandchild, mChildLayer).apply(); { SCOPED_TRACE("Added Grandchild Layer"); auto shot = screenshot(); @@ -138,7 +138,7 @@ TEST_F(MirrorLayerTest, MirrorColorLayer) { } // Add child layer - Transaction().reparent(mChildLayer, mParentLayer->getHandle()).apply(); + Transaction().reparent(mChildLayer, mParentLayer).apply(); { SCOPED_TRACE("Added Child Layer"); auto shot = screenshot(); @@ -157,7 +157,7 @@ TEST_F(MirrorLayerTest, MirrorBufferLayer) { sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get()); Transaction() - .reparent(mirrorLayer, mParentLayer->getHandle()) + .reparent(mirrorLayer, mParentLayer) .setPosition(mirrorLayer, 500, 500) .show(mirrorLayer) .apply(); diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp index 06e8761d43..db0c56f6aa 100644 --- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp +++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp @@ -90,7 +90,7 @@ protected: }; TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) { - createDisplay(mMainDisplayState.viewport, 1 /* layerStack */); + createDisplay(mMainDisplayState.layerStackSpaceRect, 1 /* layerStack */); createColorLayer(1 /* layerStack */); asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); @@ -111,9 +111,9 @@ TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { // Create a display and set its layer stack to the main display's layer stack so // the contents of the main display are mirrored on to the virtual display. - // Assumption here is that the new mirrored display has the same viewport as the + // Assumption here is that the new mirrored display has the same layer stack rect as the // primary display that it is mirroring. - createDisplay(mMainDisplayState.viewport, 0 /* layerStack */); + createDisplay(mMainDisplayState.layerStackSpaceRect, 0 /* layerStack */); createColorLayer(0 /* layerStack */); asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); diff --git a/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp new file mode 100644 index 0000000000..05858bf839 --- /dev/null +++ b/services/surfaceflinger/tests/RefreshRateOverlay_test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <thread> + +#include <gtest/gtest.h> + +#include <gui/SurfaceComposerClient.h> +#include <private/gui/ComposerService.h> + +static constexpr int kRefreshRateOverlayCode = 1034; +static constexpr int kRefreshRateOverlayEnable = 1; +static constexpr int kRefreshRateOverlayDisable = 0; +static constexpr int kRefreshRateOverlayQuery = 2; + +// These values must match the ones we used for developer options in +// com.android.settings.development.ShowRefreshRatePreferenceController +static_assert(kRefreshRateOverlayCode == 1034); +static_assert(kRefreshRateOverlayEnable == 1); +static_assert(kRefreshRateOverlayDisable == 0); +static_assert(kRefreshRateOverlayQuery == 2); + +namespace android { + +namespace { +void sendCommandToSf(int command, Parcel& reply) { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + Parcel request; + request.writeInterfaceToken(String16("android.ui.ISurfaceComposer")); + request.writeInt32(command); + ASSERT_EQ(NO_ERROR, + IInterface::asBinder(sf)->transact(kRefreshRateOverlayCode, request, &reply)); +} + +bool isOverlayEnabled() { + Parcel reply; + sendCommandToSf(kRefreshRateOverlayQuery, reply); + return reply.readBool(); +} + +void waitForOverlay(bool enabled) { + static constexpr auto kTimeout = std::chrono::nanoseconds(1s); + static constexpr auto kIterations = 10; + for (int i = 0; i < kIterations; i++) { + if (enabled == isOverlayEnabled()) { + return; + } + std::this_thread::sleep_for(kTimeout / kIterations); + } +} + +void toggleOverlay(bool enabled) { + if (enabled == isOverlayEnabled()) { + return; + } + + Parcel reply; + const auto command = enabled ? kRefreshRateOverlayEnable : kRefreshRateOverlayDisable; + sendCommandToSf(command, reply); + waitForOverlay(enabled); + ASSERT_EQ(enabled, isOverlayEnabled()); +} + +} // namespace + +TEST(RefreshRateOverlayTest, enableOverlay) { + toggleOverlay(true); +} + +TEST(RefreshRateOverlayTest, disableOverlay) { + toggleOverlay(false); +} + +TEST(RefreshRateOverlayTest, enableAndDisableOverlay) { + toggleOverlay(true); + toggleOverlay(false); + + toggleOverlay(true); + toggleOverlay(false); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp index 3e0b3c64e7..fde6e6eff8 100644 --- a/services/surfaceflinger/tests/RelativeZ_test.cpp +++ b/services/surfaceflinger/tests/RelativeZ_test.cpp @@ -70,10 +70,7 @@ TEST_F(RelativeZTest, LayerRemoved) { sp<SurfaceControl> childLayer = createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get()); - Transaction{} - .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1) - .show(childLayer) - .apply(); + Transaction{}.setRelativeLayer(childLayer, mForegroundLayer, 1).show(childLayer).apply(); { // The childLayer should be in front of the FG control. @@ -88,7 +85,7 @@ TEST_F(RelativeZTest, LayerRemoved) { // Background layer (RED) // Child layer (WHITE) // Foregroud layer (GREEN) - Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply(); + Transaction{}.reparent(childLayer, mBackgroundLayer).apply(); { // The relative z info for child layer should be reset, leaving FG control on top. @@ -118,7 +115,7 @@ TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) { createColorLayer("child level 3", Color::GREEN, childLevel2a.get()); Transaction{} - .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1) + .setRelativeLayer(childLevel3, childLevel2b, 1) .show(childLevel2a) .show(childLevel2b) .show(childLevel3) @@ -140,7 +137,7 @@ TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) { // child level 2 back (BLUE) // child level 3 (GREEN) (relative to child level 2b) // child level 2 front (BLACK) - Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply(); + Transaction{}.reparent(childLevel1, mForegroundLayer).apply(); { // Nothing should change at this point since relative z info was preserved. @@ -162,7 +159,7 @@ TEST_F(RelativeZTest, LayerAndRelativeRemoved) { createColorLayer("Relative layer", Color::WHITE, mForegroundLayer.get()); Transaction{} - .setRelativeLayer(childLayer, relativeToLayer->getHandle(), 1) + .setRelativeLayer(childLayer, relativeToLayer, 1) .show(childLayer) .show(relativeToLayer) .apply(); @@ -199,7 +196,7 @@ TEST_F(RelativeZTest, LayerAndRelativeRemoved) { // Background layer (RED) // Foregroud layer (GREEN) // Child layer (BLUE) - Transaction{}.reparent(childLayer, mForegroundLayer->getHandle()).apply(); + Transaction{}.reparent(childLayer, mForegroundLayer).apply(); { // The relative z info for child layer should be reset, leaving the child layer on top. @@ -230,7 +227,7 @@ TEST_F(RelativeZTest, LayerWithRelativeReparentedToOffscreen) { createColorLayer("child level 2b", Color::BLACK, childLevel1b.get()); Transaction{} - .setRelativeLayer(childLevel1a, childLevel2b->getHandle(), 1) + .setRelativeLayer(childLevel1a, childLevel2b, 1) .show(childLevel1a) .show(childLevel1b) .show(childLevel2a) @@ -250,7 +247,7 @@ TEST_F(RelativeZTest, LayerWithRelativeReparentedToOffscreen) { // // Background layer (RED) // // Foregroud layer (GREEN) - Transaction{}.reparent(childLevel1a, childLevel2a->getHandle()).apply(); + Transaction{}.reparent(childLevel1a, childLevel2a).apply(); { // The childLevel1a and childLevel1b are no longer on screen @@ -264,7 +261,7 @@ TEST_F(RelativeZTest, LayerWithRelativeReparentedToOffscreen) { // child level 2a (BLUE) // child level 1a (testLayerColor) (relative to child level 2b) // child level 2b (BLACK) - Transaction{}.reparent(childLevel1b, mForegroundLayer->getHandle()).apply(); + Transaction{}.reparent(childLevel1b, mForegroundLayer).apply(); { // Nothing should change at this point since relative z info was preserved. diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp new file mode 100644 index 0000000000..7df3711183 --- /dev/null +++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + +#include <private/android_filesystem_config.h> + +#include "LayerTransactionTest.h" + +namespace android { + +class ScreenCaptureTest : public LayerTransactionTest { +protected: + virtual void SetUp() { + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + + DisplayConfig config; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayConfig(display, &config)); + const ui::Size& resolution = config.resolution; + + // Background surface + mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(), + resolution.getHeight(), 0); + ASSERT_TRUE(mBGSurfaceControl != nullptr); + ASSERT_TRUE(mBGSurfaceControl->isValid()); + TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195); + + // Foreground surface + mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0); + + ASSERT_TRUE(mFGSurfaceControl != nullptr); + ASSERT_TRUE(mFGSurfaceControl->isValid()); + + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + + asTransaction([&](Transaction& t) { + t.setDisplayLayerStack(display, 0); + + t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl); + + t.setLayer(mFGSurfaceControl, INT32_MAX - 1) + .setPosition(mFGSurfaceControl, 64, 64) + .show(mFGSurfaceControl); + }); + } + + virtual void TearDown() { + LayerTransactionTest::TearDown(); + mBGSurfaceControl = 0; + mFGSurfaceControl = 0; + } + + sp<SurfaceControl> mBGSurfaceControl; + sp<SurfaceControl> mFGSurfaceControl; + std::unique_ptr<ScreenCapture> mCapture; +}; + +TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, + ISurfaceComposerClient::eSecure | + ISurfaceComposerClient::eFXSurfaceBufferQueue)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + Transaction().show(layer).setLayer(layer, INT32_MAX).apply(true); + + ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults)); + + UIDFaker f(AID_SYSTEM); + + // By default the system can capture screenshots with secure layers but they + // will be blacked out + ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(mCaptureArgs, mCaptureResults)); + + { + SCOPED_TRACE("as system"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } + + // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able + // to receive them...we are expected to take care with the results. + DisplayCaptureArgs args; + args.displayToken = mDisplay; + args.captureSecureLayers = true; + ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults)); + ASSERT_TRUE(mCaptureResults.capturedSecureLayers); + ScreenCapture sc(mCaptureResults.buffer); + sc.expectColor(Rect(0, 0, 32, 32), Color::RED); +} + +TEST_F(ScreenCaptureTest, CaptureChildSetParentFlagsSecureEUidSystem) { + sp<SurfaceControl> parentLayer; + ASSERT_NO_FATAL_FAILURE( + parentLayer = createLayer("parent-test", 32, 32, + ISurfaceComposerClient::eSecure | + ISurfaceComposerClient::eFXSurfaceBufferQueue)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parentLayer, Color::RED, 32, 32)); + + sp<SurfaceControl> childLayer; + ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("child-test", 10, 10, + ISurfaceComposerClient::eFXSurfaceBufferQueue, + parentLayer.get())); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(childLayer, Color::BLUE, 10, 10)); + + Transaction().show(parentLayer).setLayer(parentLayer, INT32_MAX).show(childLayer).apply(true); + + UIDFaker f(AID_SYSTEM); + + { + SCOPED_TRACE("as system"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 10, 10), Color::BLACK); + } + + // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able + // to receive them...we are expected to take care with the results. + DisplayCaptureArgs args; + args.displayToken = mDisplay; + args.captureSecureLayers = true; + ASSERT_EQ(NO_ERROR, ScreenCapture::captureDisplay(args, mCaptureResults)); + ASSERT_TRUE(mCaptureResults.capturedSecureLayers); + ScreenCapture sc(mCaptureResults.buffer); + sc.expectColor(Rect(0, 0, 10, 10), Color::BLUE); +} + +TEST_F(ScreenCaptureTest, CaptureSingleLayer) { + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mBGSurfaceControl->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectBGColor(0, 0); + // Doesn't capture FG layer which is at 64, 64 + mCapture->expectBGColor(64, 64); +} + +TEST_F(ScreenCaptureTest, CaptureLayerWithChild) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + SurfaceComposerClient::Transaction().show(child).apply(true); + + // Captures mFGSurfaceControl layer and its child. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mFGSurfaceControl->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectFGColor(10, 10); + mCapture->expectChildColor(0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + SurfaceComposerClient::Transaction().show(child).apply(true); + + // Captures mFGSurfaceControl's child + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = fgHandle; + captureArgs.childrenOnly = true; + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->checkPixel(10, 10, 0, 0, 0); + mCapture->expectChildColor(0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureLayerExclude) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); + + SurfaceComposerClient::Transaction() + .show(child) + .show(child2) + .setLayer(child, 1) + .setLayer(child2, 2) + .apply(true); + + // Child2 would be visible but its excluded, so we should see child1 color instead. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = fgHandle; + captureArgs.childrenOnly = true; + captureArgs.excludeHandles = {child2->getHandle()}; + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->checkPixel(10, 10, 0, 0, 0); + mCapture->checkPixel(0, 0, 200, 200, 200); +} + +// Like the last test but verifies that children are also exclude. +TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); + sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, child2.get()); + TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); + + SurfaceComposerClient::Transaction() + .show(child) + .show(child2) + .show(child3) + .setLayer(child, 1) + .setLayer(child2, 2) + .apply(true); + + // Child2 would be visible but its excluded, so we should see child1 color instead. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = fgHandle; + captureArgs.childrenOnly = true; + captureArgs.excludeHandles = {child2->getHandle()}; + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->checkPixel(10, 10, 0, 0, 0); + mCapture->checkPixel(0, 0, 200, 200, 200); +} + +TEST_F(ScreenCaptureTest, CaptureTransparent) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + SurfaceComposerClient::Transaction().show(child).apply(true); + + // Captures child + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = child->getHandle(); + captureArgs.sourceCrop = {0, 0, 10, 20}; + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255}); + // Area outside of child's bounds is transparent. + mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0}); +} + +TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + ASSERT_NE(nullptr, child.get()) << "failed to create surface"; + sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100); + + SurfaceComposerClient::Transaction() + .show(child) + // Set relative layer above fg layer so should be shown above when computing all layers. + .setRelativeLayer(relative, mFGSurfaceControl, 1) + .show(relative) + .apply(true); + + // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mFGSurfaceControl->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectFGColor(10, 10); + mCapture->expectChildColor(0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureRelativeInTree) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100); + + SurfaceComposerClient::Transaction() + .show(child) + // Set relative layer below fg layer but relative to child layer so it should be shown + // above child layer. + .setLayer(relative, -1) + .setRelativeLayer(relative, child, 1) + .show(relative) + .apply(true); + + // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its + // relative value should be taken into account, placing it above child layer. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mFGSurfaceControl->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectFGColor(10, 10); + // Relative layer is showing on top of child layer + mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255}); +} + +TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) { + sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get()); + SurfaceComposerClient::Transaction().show(child).apply(true); + + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = child->getHandle(); + captureArgs.sourceCrop = {0, 0, 10, 10}; + ScreenCapture::captureLayers(&mCapture, captureArgs); + + mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED); +} + +TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) { + sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get()); + Rect layerCrop(0, 0, 10, 10); + SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true); + + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = child->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + + mCapture->expectColor(Rect(0, 0, 9, 9), Color::RED); +} + +TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) { + sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get()); + SurfaceComposerClient::Transaction().show(child).apply(true); + + LayerCaptureArgs args; + args.layerHandle = child->getHandle(); + + ScreenCaptureResults captureResults; + ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults)); +} + +TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + SurfaceComposerClient::Transaction().show(child).apply(true); + sp<GraphicBuffer> outBuffer; + + LayerCaptureArgs args; + args.layerHandle = child->getHandle(); + args.childrenOnly = false; + + ScreenCaptureResults captureResults; + ASSERT_EQ(BAD_VALUE, ScreenCapture::captureLayers(args, captureResults)); + + TransactionUtils::fillSurfaceRGBA8(child, Color::RED); + SurfaceComposerClient::Transaction().apply(true); + ASSERT_EQ(NO_ERROR, ScreenCapture::captureLayers(args, captureResults)); + ScreenCapture sc(captureResults.buffer); + sc.expectColor(Rect(0, 0, 9, 9), Color::RED); +} + +TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, + PIXEL_FORMAT_RGBA_8888, 0, child.get()); + + TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50); + SurfaceComposerClient::Transaction() + .show(child) + .setPosition(grandchild, 5, 5) + .show(grandchild) + .apply(true); + + // Captures mFGSurfaceControl, its child, and the grandchild. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mFGSurfaceControl->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectFGColor(10, 10); + mCapture->expectChildColor(0, 0); + mCapture->checkPixel(5, 5, 50, 50, 50); +} + +TEST_F(ScreenCaptureTest, CaptureChildOnly) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true); + + // Captures only the child layer, and not the parent. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = child->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(9, 9); +} + +TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + auto childHandle = child->getHandle(); + + sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, + PIXEL_FORMAT_RGBA_8888, 0, child.get()); + TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50); + + SurfaceComposerClient::Transaction() + .show(child) + .setPosition(grandchild, 5, 5) + .show(grandchild) + .apply(true); + + // Captures only the grandchild. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = grandchild->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->checkPixel(0, 0, 50, 50, 50); + mCapture->checkPixel(4, 4, 50, 50, 50); +} + +TEST_F(ScreenCaptureTest, CaptureCrop) { + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, + PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); + + SurfaceComposerClient::Transaction() + .setLayer(redLayer, INT32_MAX - 1) + .show(redLayer) + .show(blueLayer) + .apply(true); + + // Capturing full screen should have both red and blue are visible. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = redLayer->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); + // red area below the blue area + mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED); + // red area to the right of the blue area + mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); + + captureArgs.sourceCrop = {0, 0, 30, 30}; + ScreenCapture::captureLayers(&mCapture, captureArgs); + // Capturing the cropped screen, cropping out the shown red area, should leave only the blue + // area visible. + mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); + mCapture->checkPixel(30, 30, 0, 0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureSize) { + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, + PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); + + SurfaceComposerClient::Transaction() + .setLayer(redLayer, INT32_MAX - 1) + .show(redLayer) + .show(blueLayer) + .apply(true); + + // Capturing full screen should have both red and blue are visible. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = redLayer->getHandle(); + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); + // red area below the blue area + mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED); + // red area to the right of the blue area + mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); + + captureArgs.frameScale = 0.5f; + ScreenCapture::captureLayers(&mCapture, captureArgs); + // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area. + mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE); + // red area below the blue area + mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED); + // red area to the right of the blue area + mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED); + mCapture->checkPixel(30, 30, 0, 0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + + auto redLayerHandle = redLayer->getHandle(); + Transaction().reparent(redLayer, nullptr).apply(); + redLayer.clear(); + SurfaceComposerClient::Transaction().apply(true); + + LayerCaptureArgs args; + args.layerHandle = redLayerHandle; + + ScreenCaptureResults captureResults; + // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND + ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults)); +} + +TEST_F(ScreenCaptureTest, CaputureSecureLayer) { + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + sp<SurfaceControl> secureLayer = + createLayer(String8("Secure surface"), 30, 30, + ISurfaceComposerClient::eSecure | + ISurfaceComposerClient::eFXSurfaceBufferQueue, + redLayer.get()); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(secureLayer, Color::BLUE, 30, 30)); + + auto redLayerHandle = redLayer->getHandle(); + Transaction() + .show(redLayer) + .show(secureLayer) + .setLayerStack(redLayer, 0) + .setLayer(redLayer, INT32_MAX) + .apply(); + + LayerCaptureArgs args; + args.layerHandle = redLayerHandle; + args.childrenOnly = false; + ScreenCaptureResults captureResults; + + // Call from outside system with secure layers will result in permission denied + ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(args, captureResults)); + + UIDFaker f(AID_SYSTEM); + + // From system request, only red layer will be screenshot since the blue layer is secure. + // Black will be present where the secure layer is. + ScreenCapture::captureLayers(&mCapture, args); + mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLACK); + mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED); + + // Passing flag secure so the blue layer should be screenshot too. + args.captureSecureLayers = true; + ScreenCapture::captureLayers(&mCapture, args); + mCapture->expectColor(Rect(0, 0, 30, 30), Color::BLUE); + mCapture->expectColor(Rect(30, 30, 60, 60), Color::RED); +} + +TEST_F(ScreenCaptureTest, CaptureDisplayWithUid) { + uid_t fakeUid = 12345; + + DisplayCaptureArgs captureArgs; + captureArgs.displayToken = mDisplay; + + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32, + ISurfaceComposerClient::eFXSurfaceBufferQueue, + mBGSurfaceControl.get())); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + Transaction().show(layer).setLayer(layer, INT32_MAX).apply(); + + // Make sure red layer with the background layer is screenshot. + ScreenCapture::captureDisplay(&mCapture, captureArgs); + mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED); + mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255}); + + // From non system uid, can't request screenshot without a specified uid. + UIDFaker f(fakeUid); + ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureDisplay(captureArgs, mCaptureResults)); + + // Make screenshot request with current uid set. No layers were created with the current + // uid so screenshot will be black. + captureArgs.uid = fakeUid; + ScreenCapture::captureDisplay(&mCapture, captureArgs); + mCapture->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + mCapture->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + + sp<SurfaceControl> layerWithFakeUid; + // Create a new layer with the current uid + ASSERT_NO_FATAL_FAILURE(layerWithFakeUid = + createLayer("new test layer", 32, 32, + ISurfaceComposerClient::eFXSurfaceBufferQueue, + mBGSurfaceControl.get())); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32)); + Transaction() + .show(layerWithFakeUid) + .setLayer(layerWithFakeUid, INT32_MAX) + .setPosition(layerWithFakeUid, 128, 128) + .apply(); + + // Screenshot from the fakeUid caller with the uid requested allows the layer + // with that uid to be screenshotted. Everything else is black + ScreenCapture::captureDisplay(&mCapture, captureArgs); + mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN); + mCapture->expectBorder(Rect(128, 128, 160, 160), Color::BLACK); +} + +TEST_F(ScreenCaptureTest, CaptureDisplayPrimaryDisplayOnly) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect)); + + const Color layerColor = Color::RED; + const Rect bounds = Rect(10, 10, 40, 40); + + Transaction() + .show(layer) + .hide(mFGSurfaceControl) + .setLayerStack(layer, 0) + .setLayer(layer, INT32_MAX) + .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255}) + .setCrop_legacy(layer, bounds) + .apply(); + + DisplayCaptureArgs captureArgs; + captureArgs.displayToken = mDisplay; + + { + ScreenCapture::captureDisplay(&mCapture, captureArgs); + mCapture->expectColor(bounds, layerColor); + mCapture->expectBorder(bounds, {63, 63, 195, 255}); + } + + Transaction() + .setFlags(layer, layer_state_t::eLayerSkipScreenshot, + layer_state_t::eLayerSkipScreenshot) + .apply(); + + { + // Can't screenshot test layer since it now has flag + // eLayerSkipScreenshot + ScreenCapture::captureDisplay(&mCapture, captureArgs); + mCapture->expectColor(bounds, {63, 63, 195, 255}); + mCapture->expectBorder(bounds, {63, 63, 195, 255}); + } +} + +TEST_F(ScreenCaptureTest, CaptureDisplayChildPrimaryDisplayOnly) { + sp<SurfaceControl> layer; + sp<SurfaceControl> childLayer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test layer", 0, 0, ISurfaceComposerClient::eFXSurfaceEffect)); + ASSERT_NO_FATAL_FAILURE(childLayer = createLayer("test layer", 0, 0, + ISurfaceComposerClient::eFXSurfaceEffect, + layer.get())); + + const Color layerColor = Color::RED; + const Color childColor = Color::BLUE; + const Rect bounds = Rect(10, 10, 40, 40); + const Rect childBounds = Rect(20, 20, 30, 30); + + Transaction() + .show(layer) + .show(childLayer) + .hide(mFGSurfaceControl) + .setLayerStack(layer, 0) + .setLayer(layer, INT32_MAX) + .setColor(layer, {layerColor.r / 255, layerColor.g / 255, layerColor.b / 255}) + .setColor(childLayer, {childColor.r / 255, childColor.g / 255, childColor.b / 255}) + .setCrop_legacy(layer, bounds) + .setCrop_legacy(childLayer, childBounds) + .apply(); + + DisplayCaptureArgs captureArgs; + captureArgs.displayToken = mDisplay; + + { + ScreenCapture::captureDisplay(&mCapture, captureArgs); + mCapture->expectColor(childBounds, childColor); + mCapture->expectBorder(childBounds, layerColor); + mCapture->expectBorder(bounds, {63, 63, 195, 255}); + } + + Transaction() + .setFlags(layer, layer_state_t::eLayerSkipScreenshot, + layer_state_t::eLayerSkipScreenshot) + .apply(); + + { + // Can't screenshot child layer since the parent has the flag + // eLayerSkipScreenshot + ScreenCapture::captureDisplay(&mCapture, captureArgs); + mCapture->expectColor(childBounds, {63, 63, 195, 255}); + mCapture->expectBorder(childBounds, {63, 63, 195, 255}); + mCapture->expectBorder(bounds, {63, 63, 195, 255}); + } +} + +TEST_F(ScreenCaptureTest, CaptureLayerWithUid) { + uid_t fakeUid = 12345; + + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32, + ISurfaceComposerClient::eFXSurfaceBufferQueue, + mBGSurfaceControl.get())); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + Transaction().show(layer).setLayer(layer, INT32_MAX).apply(); + + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mBGSurfaceControl->getHandle(); + captureArgs.childrenOnly = false; + + // Make sure red layer with the background layer is screenshot. + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED); + mCapture->expectBorder(Rect(0, 0, 32, 32), {63, 63, 195, 255}); + + // From non system uid, can't request screenshot without a specified uid. + std::unique_ptr<UIDFaker> uidFaker = std::make_unique<UIDFaker>(fakeUid); + + ASSERT_EQ(PERMISSION_DENIED, ScreenCapture::captureLayers(captureArgs, mCaptureResults)); + + // Make screenshot request with current uid set. No layers were created with the current + // uid so screenshot will be black. + captureArgs.uid = fakeUid; + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(0, 0, 32, 32), Color::TRANSPARENT); + mCapture->expectBorder(Rect(0, 0, 32, 32), Color::TRANSPARENT); + + sp<SurfaceControl> layerWithFakeUid; + // Create a new layer with the current uid + ASSERT_NO_FATAL_FAILURE(layerWithFakeUid = + createLayer("new test layer", 32, 32, + ISurfaceComposerClient::eFXSurfaceBufferQueue, + mBGSurfaceControl.get())); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerWithFakeUid, Color::GREEN, 32, 32)); + Transaction() + .show(layerWithFakeUid) + .setLayer(layerWithFakeUid, INT32_MAX) + .setPosition(layerWithFakeUid, 128, 128) + // reparent a layer that was created with a different uid to the new layer. + .reparent(layer, layerWithFakeUid) + .apply(); + + // Screenshot from the fakeUid caller with the uid requested allows the layer + // with that uid to be screenshotted. The child layer is skipped since it was created + // from a different uid. + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN); + mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT); + + // Clear fake calling uid so it's back to system. + uidFaker = nullptr; + // Screenshot from the test caller with the uid requested allows the layer + // with that uid to be screenshotted. The child layer is skipped since it was created + // from a different uid. + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(128, 128, 160, 160), Color::GREEN); + mCapture->expectBorder(Rect(128, 128, 160, 160), Color::TRANSPARENT); + + // Screenshot from the fakeUid caller with no uid requested allows everything to be screenshot. + captureArgs.uid = -1; + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(128, 128, 160, 160), Color::RED); + mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255}); +} + +// In the following tests we verify successful skipping of a parent layer, +// so we use the same verification logic and only change how we mutate +// the parent layer to verify that various properties are ignored. +class ScreenCaptureChildOnlyTest : public ScreenCaptureTest { +public: + void SetUp() override { + ScreenCaptureTest::SetUp(); + + mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, + mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200); + + SurfaceComposerClient::Transaction().show(mChild).apply(true); + } + + void verify(std::function<void()> verifyStartingState) { + // Verify starting state before a screenshot is taken. + verifyStartingState(); + + // Verify child layer does not inherit any of the properties of its + // parent when its screenshot is captured. + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mFGSurfaceControl->getHandle(); + captureArgs.childrenOnly = true; + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->checkPixel(10, 10, 0, 0, 0); + mCapture->expectChildColor(0, 0); + + // Verify all assumptions are still true after the screenshot is taken. + verifyStartingState(); + } + + std::unique_ptr<ScreenCapture> mCapture; + sp<SurfaceControl> mChild; +}; + +// Regression test b/76099859 +TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) { + SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true); + + // Even though the parent is hidden we should still capture the child. + + // Before and after reparenting, verify child is properly hidden + // when rendering full-screen. + verify([&] { screenshot()->expectBGColor(64, 64); }); +} + +TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) { + SurfaceComposerClient::Transaction() + .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1)) + .apply(true); + + // Even though the parent is cropped out we should still capture the child. + + // Before and after reparenting, verify child is cropped by parent. + verify([&] { screenshot()->expectBGColor(65, 65); }); +} + +// Regression test b/124372894 +TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) { + SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true); + + // We should not inherit the parent scaling. + + // Before and after reparenting, verify child is properly scaled. + verify([&] { screenshot()->expectChildColor(80, 80); }); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp index 8d97f275ba..81e648aaef 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -39,7 +39,6 @@ using SurfaceChange = surfaceflinger::SurfaceChange; using Trace = surfaceflinger::Trace; using Increment = surfaceflinger::Increment; -constexpr int32_t SCALING_UPDATE = 1; constexpr uint32_t BUFFER_UPDATES = 18; constexpr uint32_t LAYER_UPDATE = INT_MAX - 2; constexpr uint32_t SIZE_UPDATE = 134; @@ -52,6 +51,7 @@ constexpr int BACKGROUND_BLUR_RADIUS_UPDATE = 24; constexpr float POSITION_UPDATE = 121; const Rect CROP_UPDATE(16, 16, 32, 32); const float SHADOW_RADIUS_UPDATE = 35.0f; +std::vector<BlurRegion> BLUR_REGIONS_UPDATE; const String8 DISPLAY_NAME("SurfaceInterceptor Display Test"); constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface"; @@ -182,6 +182,7 @@ public: bool cornerRadiusUpdateFound(const SurfaceChange& change, bool foundCornerRadius); bool backgroundBlurRadiusUpdateFound(const SurfaceChange& change, bool foundBackgroundBlurRadius); + bool blurRegionsUpdateFound(const SurfaceChange& change, bool foundBlurRegions); bool matrixUpdateFound(const SurfaceChange& change, bool foundMatrix); bool scalingModeUpdateFound(const SurfaceChange& change, bool foundScalingMode); bool transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion); @@ -220,8 +221,8 @@ public: void cropUpdate(Transaction&); void cornerRadiusUpdate(Transaction&); void backgroundBlurRadiusUpdate(Transaction&); + void blurRegionsUpdate(Transaction&); void matrixUpdate(Transaction&); - void overrideScalingModeUpdate(Transaction&); void transparentRegionHintUpdate(Transaction&); void layerStackUpdate(Transaction&); void hiddenFlagUpdate(Transaction&); @@ -359,6 +360,12 @@ void SurfaceInterceptorTest::backgroundBlurRadiusUpdate(Transaction& t) { t.setBackgroundBlurRadius(mBGSurfaceControl, BACKGROUND_BLUR_RADIUS_UPDATE); } +void SurfaceInterceptorTest::blurRegionsUpdate(Transaction& t) { + BLUR_REGIONS_UPDATE.empty(); + BLUR_REGIONS_UPDATE.push_back(BlurRegion()); + t.setBlurRegions(mBGSurfaceControl, BLUR_REGIONS_UPDATE); +} + void SurfaceInterceptorTest::layerUpdate(Transaction& t) { t.setLayer(mBGSurfaceControl, LAYER_UPDATE); } @@ -371,10 +378,6 @@ void SurfaceInterceptorTest::matrixUpdate(Transaction& t) { t.setMatrix(mBGSurfaceControl, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2); } -void SurfaceInterceptorTest::overrideScalingModeUpdate(Transaction& t) { - t.setOverrideScalingMode(mBGSurfaceControl, SCALING_UPDATE); -} - void SurfaceInterceptorTest::transparentRegionHintUpdate(Transaction& t) { Region region(CROP_UPDATE); t.setTransparentRegionHint(mBGSurfaceControl, region); @@ -397,16 +400,15 @@ void SurfaceInterceptorTest::secureFlagUpdate(Transaction& t) { } void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) { - t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl->getHandle(), - DEFERRED_UPDATE); + t.deferTransactionUntil_legacy(mBGSurfaceControl, mBGSurfaceControl, DEFERRED_UPDATE); } void SurfaceInterceptorTest::reparentUpdate(Transaction& t) { - t.reparent(mBGSurfaceControl, mFGSurfaceControl->getHandle()); + t.reparent(mBGSurfaceControl, mFGSurfaceControl); } void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) { - t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl->getHandle(), RELATIVE_Z); + t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl, RELATIVE_Z); } void SurfaceInterceptorTest::detachChildrenUpdate(Transaction& t) { @@ -414,7 +416,7 @@ void SurfaceInterceptorTest::detachChildrenUpdate(Transaction& t) { } void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) { - t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle()); + t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl); } void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) { @@ -422,7 +424,7 @@ void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) { } void SurfaceInterceptorTest::displayCreation(Transaction&) { - sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true); + sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false); SurfaceComposerClient::destroyDisplay(testDisplay); } @@ -437,10 +439,10 @@ void SurfaceInterceptorTest::runAllUpdates() { runInTransaction(&SurfaceInterceptorTest::alphaUpdate); runInTransaction(&SurfaceInterceptorTest::cornerRadiusUpdate); runInTransaction(&SurfaceInterceptorTest::backgroundBlurRadiusUpdate); + runInTransaction(&SurfaceInterceptorTest::blurRegionsUpdate); runInTransaction(&SurfaceInterceptorTest::layerUpdate); runInTransaction(&SurfaceInterceptorTest::cropUpdate); runInTransaction(&SurfaceInterceptorTest::matrixUpdate); - runInTransaction(&SurfaceInterceptorTest::overrideScalingModeUpdate); runInTransaction(&SurfaceInterceptorTest::transparentRegionHintUpdate); runInTransaction(&SurfaceInterceptorTest::layerStackUpdate); runInTransaction(&SurfaceInterceptorTest::hiddenFlagUpdate); @@ -526,6 +528,17 @@ bool SurfaceInterceptorTest::backgroundBlurRadiusUpdateFound(const SurfaceChange return foundBackgroundBlur; } +bool SurfaceInterceptorTest::blurRegionsUpdateFound(const SurfaceChange& change, + bool foundBlurRegions) { + bool hasBlurRegions(change.blur_regions().blur_regions_size() == BLUR_REGIONS_UPDATE.size()); + if (hasBlurRegions && !foundBlurRegions) { + foundBlurRegions = true; + } else if (hasBlurRegions && foundBlurRegions) { + []() { FAIL(); }(); + } + return foundBlurRegions; +} + bool SurfaceInterceptorTest::layerUpdateFound(const SurfaceChange& change, bool foundLayer) { bool hasLayer(change.layer().layer() == LAYER_UPDATE); if (hasLayer && !foundLayer) { @@ -562,17 +575,6 @@ bool SurfaceInterceptorTest::matrixUpdateFound(const SurfaceChange& change, bool return foundMatrix; } -bool SurfaceInterceptorTest::scalingModeUpdateFound(const SurfaceChange& change, - bool foundScalingMode) { - bool hasScalingUpdate(change.override_scaling_mode().override_scaling_mode() == SCALING_UPDATE); - if (hasScalingUpdate && !foundScalingMode) { - foundScalingMode = true; - } else if (hasScalingUpdate && foundScalingMode) { - [] () { FAIL(); }(); - } - return foundScalingMode; -} - bool SurfaceInterceptorTest::transparentRegionHintUpdateFound(const SurfaceChange& change, bool foundTransparentRegion) { auto traceRegion = change.transparent_region_hint().region(0); @@ -725,12 +727,12 @@ bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace, case SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius: foundUpdate = backgroundBlurRadiusUpdateFound(change, foundUpdate); break; + case SurfaceChange::SurfaceChangeCase::kBlurRegions: + foundUpdate = blurRegionsUpdateFound(change, foundUpdate); + break; case SurfaceChange::SurfaceChangeCase::kMatrix: foundUpdate = matrixUpdateFound(change, foundUpdate); break; - case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode: - foundUpdate = scalingModeUpdateFound(change, foundUpdate); - break; case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint: foundUpdate = transparentRegionHintUpdateFound(change, foundUpdate); break; @@ -781,7 +783,6 @@ void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) { ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayer)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kCrop)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kMatrix)); - ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOverrideScalingMode)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kTransparentRegionHint)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kLayerStack)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kHiddenFlag)); @@ -819,7 +820,7 @@ bool SurfaceInterceptorTest::surfaceDeletionFound(const Increment& increment, bool SurfaceInterceptorTest::displayCreationFound(const Increment& increment, bool foundDisplay) { bool isMatch(increment.display_creation().name() == DISPLAY_NAME.string() && - increment.display_creation().is_secure()); + !increment.display_creation().is_secure()); if (isMatch && !foundDisplay) { foundDisplay = true; } else if (isMatch && foundDisplay) { @@ -912,13 +913,13 @@ TEST_F(SurfaceInterceptorTest, InterceptBackgroundBlurRadiusUpdateWorks) { SurfaceChange::SurfaceChangeCase::kBackgroundBlurRadius); } -TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) { - captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix); +TEST_F(SurfaceInterceptorTest, InterceptBlurRegionsUpdateWorks) { + captureTest(&SurfaceInterceptorTest::blurRegionsUpdate, + SurfaceChange::SurfaceChangeCase::kBlurRegions); } -TEST_F(SurfaceInterceptorTest, InterceptOverrideScalingModeUpdateWorks) { - captureTest(&SurfaceInterceptorTest::overrideScalingModeUpdate, - SurfaceChange::SurfaceChangeCase::kOverrideScalingMode); +TEST_F(SurfaceInterceptorTest, InterceptMatrixUpdateWorks) { + captureTest(&SurfaceInterceptorTest::matrixUpdate, SurfaceChange::SurfaceChangeCase::kMatrix); } TEST_F(SurfaceInterceptorTest, InterceptTransparentRegionHintUpdateWorks) { diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index f0af363469..01badf4ad9 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -65,7 +65,7 @@ public: t.setDisplaySurface(vDisplay, producer); t.setDisplayLayerStack(vDisplay, 0); t.setDisplayProjection(vDisplay, displayState.orientation, - Rect(displayState.viewport), Rect(resolution)); + Rect(displayState.layerStackSpaceRect), Rect(resolution)); t.apply(); SurfaceComposerClient::Transaction().apply(true); BufferItem item; diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp index a03fd89297..0a70f5c504 100644 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -72,6 +72,9 @@ using Attribute = V2_4::IComposerClient::Attribute; /////////////////////////////////////////////// +constexpr PhysicalDisplayId kPrimaryDisplayId = PhysicalDisplayId::fromPort(PRIMARY_DISPLAY); +constexpr PhysicalDisplayId kExternalDisplayId = PhysicalDisplayId::fromPort(EXTERNAL_DISPLAY); + struct TestColor { public: uint8_t r; @@ -272,6 +275,10 @@ protected: mFakeComposerClient->runVSyncAndWait(); } + bool waitForHotplugEvent(Display displayId, bool connected) { + return waitForHotplugEvent(PhysicalDisplayId(displayId), connected); + } + bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) { int waitCount = 20; while (waitCount--) { @@ -280,9 +287,8 @@ protected: mReceivedDisplayEvents.pop_front(); ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG, - "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT - ", connected %d\t", - event.header.displayId, event.hotplug.connected); + "event hotplug: displayId %s, connected %d\t", + to_string(event.header.displayId).c_str(), event.hotplug.connected); if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG && event.header.displayId == displayId && event.hotplug.connected == connected) { @@ -295,7 +301,8 @@ protected: return false; } - bool waitForConfigChangedEvent(PhysicalDisplayId displayId, int32_t configId) { + bool waitForConfigChangedEvent(Display display, int32_t configId) { + PhysicalDisplayId displayId(display); int waitCount = 20; while (waitCount--) { while (!mReceivedDisplayEvents.empty()) { @@ -303,9 +310,8 @@ protected: mReceivedDisplayEvents.pop_front(); ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, - "event config: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT - ", configId %d\t", - event.header.displayId, event.config.configId); + "event config: displayId %s, configId %d\t", + to_string(event.header.displayId).c_str(), event.config.configId); if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED && event.header.displayId == displayId && event.config.configId == configId) { @@ -335,7 +341,7 @@ protected: EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true)); { - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); EXPECT_FALSE(display == nullptr); DisplayConfig config; @@ -367,7 +373,7 @@ protected: EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false)); { - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); EXPECT_TRUE(display == nullptr); DisplayConfig config; @@ -396,7 +402,7 @@ protected: waitForDisplayTransaction(); EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true)); - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); EXPECT_FALSE(display == nullptr); DisplayConfig config; @@ -441,7 +447,7 @@ protected: const auto& config = configs[i]; if (config.resolution.getWidth() == 800) { EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, + SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false, config.refreshRate, config.refreshRate, config.refreshRate, @@ -503,7 +509,7 @@ protected: waitForDisplayTransaction(); EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true)); - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); EXPECT_FALSE(display == nullptr); DisplayConfig config; @@ -547,7 +553,7 @@ protected: const auto& config = configs[i]; if (config.refreshRate == 1e9f / 11'111'111) { EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, + SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false, config.refreshRate, config.refreshRate, config.refreshRate, @@ -619,7 +625,7 @@ protected: waitForDisplayTransaction(); EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true)); - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kExternalDisplayId); EXPECT_FALSE(display == nullptr); DisplayConfig config; @@ -664,7 +670,8 @@ protected: if (config.resolution.getWidth() == 800 && config.refreshRate == 1e9f / 11'111'111) { EXPECT_EQ(NO_ERROR, SurfaceComposerClient:: - setDesiredDisplayConfigSpecs(display, i, configs[i].refreshRate, + setDesiredDisplayConfigSpecs(display, i, false, + configs[i].refreshRate, configs[i].refreshRate, configs[i].refreshRate, configs[i].refreshRate)); @@ -710,7 +717,7 @@ protected: const auto& config = configs[i]; if (config.refreshRate == 1e9f / 8'333'333) { EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, + SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false, config.refreshRate, config.refreshRate, config.refreshRate, @@ -757,7 +764,7 @@ protected: const auto& config = configs[i]; if (config.resolution.getWidth() == 1600 && config.refreshRate == 1e9f / 11'111'111) { EXPECT_EQ(NO_ERROR, - SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, + SurfaceComposerClient::setDesiredDisplayConfigSpecs(display, i, false, config.refreshRate, config.refreshRate, config.refreshRate, @@ -808,7 +815,7 @@ protected: EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false)); { - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId); EXPECT_TRUE(display == nullptr); DisplayConfig config; @@ -834,7 +841,7 @@ protected: EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true)); { - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId); EXPECT_FALSE(display == nullptr); DisplayConfig config; @@ -988,7 +995,7 @@ protected: ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); ALOGI("TransactionTest::SetUp - display"); - const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY); + const auto display = SurfaceComposerClient::getPhysicalDisplayToken(kPrimaryDisplayId); ASSERT_FALSE(display == nullptr); DisplayConfig config; @@ -1316,7 +1323,7 @@ protected: { TransactionScope ts(*sFakeComposer); ts.setAlpha(mFGSurfaceControl, 0.75); - ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(), + ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl, syncSurfaceControl->getSurface()->getNextFrameNumber()); } EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); @@ -1324,7 +1331,7 @@ protected: { TransactionScope ts(*sFakeComposer); ts.setPosition(mFGSurfaceControl, 128, 128); - ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(), + ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl, syncSurfaceControl->getSurface()->getNextFrameNumber() + 1); } @@ -1370,7 +1377,7 @@ protected: TransactionScope ts(*sFakeComposer); ts.setPosition(relativeSurfaceControl, 64, 64); ts.show(relativeSurfaceControl); - ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1); + ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl, 1); } auto referenceFrame = mBaseFrame; // NOTE: All three layers will be visible as the surfaces are @@ -1463,7 +1470,7 @@ protected: Base::SetUp(); mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, - Base::mFGSurfaceControl.get()); + Base::mFGSurfaceControl->getHandle()); fillSurfaceRGBA8(mChild, LIGHT_GRAY); Base::sFakeComposer->runVSyncAndWait(); @@ -1600,7 +1607,7 @@ protected: { TransactionScope ts(*Base::sFakeComposer); - ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl->getHandle()); + ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl); } auto referenceFrame2 = referenceFrame; @@ -1647,7 +1654,7 @@ protected: sp<SurfaceControl> childNewClient = newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, 0, - Base::mFGSurfaceControl.get()); + Base::mFGSurfaceControl->getHandle()); ASSERT_TRUE(childNewClient != nullptr); ASSERT_TRUE(childNewClient->isValid()); fillSurfaceRGBA8(childNewClient, LIGHT_GRAY); @@ -1685,30 +1692,6 @@ protected: EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); } - void Test_InheritNonTransformScalingFromParent() { - { - TransactionScope ts(*Base::sFakeComposer); - ts.show(mChild); - ts.setPosition(mChild, 0, 0); - ts.setPosition(Base::mFGSurfaceControl, 0, 0); - } - - { - TransactionScope ts(*Base::sFakeComposer); - ts.setOverrideScalingMode(Base::mFGSurfaceControl, - NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - // We cause scaling by 2. - ts.setSize(Base::mFGSurfaceControl, 128, 128); - } - - auto referenceFrame = Base::mBaseFrame; - referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128}; - referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f}; - referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20}; - referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f}; - EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame())); - } - // Regression test for b/37673612 void Test_ChildrenWithParentBufferTransform() { { @@ -1750,12 +1733,12 @@ protected: mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden, - Base::mFGSurfaceControl.get()); + Base::mFGSurfaceControl->getHandle()); // Show the child layer in a deferred transaction { TransactionScope ts(*Base::sFakeComposer); - ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl->getHandle(), + ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl, Base::mFGSurfaceControl->getSurface() ->getNextFrameNumber()); ts.show(mChild); @@ -1817,10 +1800,6 @@ TEST_F(ChildLayerTest_2_1, DISABLED_DetachChildrenDifferentClient) { Test_DetachChildrenDifferentClient(); } -TEST_F(ChildLayerTest_2_1, DISABLED_InheritNonTransformScalingFromParent) { - Test_InheritNonTransformScalingFromParent(); -} - // Regression test for b/37673612 TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) { Test_ChildrenWithParentBufferTransform(); @@ -1841,7 +1820,7 @@ protected: Base::mComposerClient->createSurface(String8("Child surface"), 0, 0, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect, - Base::mFGSurfaceControl.get()); + Base::mFGSurfaceControl->getHandle()); { TransactionScope ts(*Base::sFakeComposer); ts.setColor(Base::mChild, diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 1bbc3f8b41..8097a88f30 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -39,16 +39,29 @@ cc_test { "CompositionTest.cpp", "DispSyncSourceTest.cpp", "DisplayIdentificationTest.cpp", + "DisplayIdGeneratorTest.cpp", "DisplayTransactionTest.cpp", - "EventControlThreadTest.cpp", + "DisplayDevice_GetBestColorModeTest.cpp", + "DisplayDevice_SetProjectionTest.cpp", "EventThreadTest.cpp", + "FrameTimelineTest.cpp", "HWComposerTest.cpp", "OneShotTimerTest.cpp", "LayerHistoryTest.cpp", "LayerHistoryTestV2.cpp", "LayerMetadataTest.cpp", - "PhaseOffsetsTest.cpp", + "MessageQueueTest.cpp", "PromiseTest.cpp", + "SurfaceFlinger_CreateDisplayTest.cpp", + "SurfaceFlinger_DestroyDisplayTest.cpp", + "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp", + "SurfaceFlinger_HandleTransactionLockedTest.cpp", + "SurfaceFlinger_NotifyPowerBoostTest.cpp", + "SurfaceFlinger_OnHotplugReceivedTest.cpp", + "SurfaceFlinger_OnInitializeDisplaysTest.cpp", + "SurfaceFlinger_SetDisplayStateTest.cpp", + "SurfaceFlinger_SetPowerModeInternalTest.cpp", + "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp", "SchedulerTest.cpp", "SchedulerUtilsTest.cpp", "SetFrameRateTest.cpp", @@ -63,26 +76,28 @@ cc_test { "StrongTypingTest.cpp", "VSyncDispatchTimerQueueTest.cpp", "VSyncDispatchRealtimeTest.cpp", - "VSyncModulatorTest.cpp", + "VsyncModulatorTest.cpp", "VSyncPredictorTest.cpp", "VSyncReactorTest.cpp", + "VsyncConfigurationTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplay.cpp", "mock/DisplayHardware/MockPowerAdvisor.cpp", - "mock/MockDispSync.cpp", - "mock/MockEventControlThread.cpp", "mock/MockEventThread.cpp", + "mock/MockFrameTracer.cpp", "mock/MockMessageQueue.cpp", "mock/MockNativeWindowSurface.cpp", "mock/MockSurfaceInterceptor.cpp", "mock/MockTimeStats.cpp", - "mock/MockFrameTracer.cpp", + "mock/MockVsyncController.cpp", + "mock/MockVSyncTracker.cpp", "mock/system/window/MockNativeWindow.cpp", ], static_libs: [ "libgmock", "libcompositionengine", "libcompositionengine_mocks", + "libframetimeline", "libgui_mocks", "libperfetto_client_experimental", "librenderengine_mocks", @@ -91,7 +106,6 @@ cc_test { shared_libs: [ "libprotoutil", "libstatssocket", - "libsurfaceflinger", "libtimestats", "libtimestats_proto", ], diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 32d722e9c9..091171266f 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -35,16 +35,17 @@ #include <utils/String8.h> #include "BufferQueueLayer.h" +#include "ContainerLayer.h" +#include "DisplayRenderArea.h" #include "EffectLayer.h" #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockDispSync.h" -#include "mock/MockEventControlThread.h" #include "mock/MockEventThread.h" #include "mock/MockMessageQueue.h" #include "mock/MockTimeStats.h" +#include "mock/MockVsyncController.h" #include "mock/system/window/MockNativeWindow.h" namespace android { @@ -80,7 +81,7 @@ constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DIS constexpr hal::HWLayerId HWC_LAYER = 5000; constexpr Transform DEFAULT_TRANSFORM = static_cast<Transform>(0); -constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42}; +constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(42); constexpr int DEFAULT_DISPLAY_WIDTH = 1920; constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; @@ -133,25 +134,33 @@ public: EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) .WillOnce(Return( - new EventThreadConnection(eventThread.get(), ResyncCallback(), + new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) .WillOnce(Return( - new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); - auto primaryDispSync = std::make_unique<mock::DispSync>(); + auto vsyncController = std::make_unique<mock::VsyncController>(); + auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); - EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0)); - EXPECT_CALL(*primaryDispSync, getPeriod()) + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); - EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); - mFlinger.setupScheduler(std::move(primaryDispSync), - std::make_unique<mock::EventControlThread>(), - std::move(eventThread), std::move(sfEventThread)); + constexpr ISchedulerCallback* kCallback = nullptr; + constexpr bool kHasMultipleConfigs = true; + mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), + std::move(eventThread), std::move(sfEventThread), kCallback, + kHasMultipleConfigs); + + // Layer history should be created if there are multiple configs. + ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory()); } void setupForceGeometryDirty() { @@ -182,6 +191,7 @@ public: sp<compositionengine::mock::DisplaySurface> mDisplaySurface = new compositionengine::mock::DisplaySurface(); mock::NativeWindow* mNativeWindow = new mock::NativeWindow(); + std::vector<sp<Layer>> mAuxiliaryLayers; sp<GraphicBuffer> mBuffer = new GraphicBuffer(); ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer(); @@ -229,28 +239,27 @@ void CompositionTest::captureScreenComposition() { LayerCase::setupForScreenCapture(this); const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); - constexpr bool useIdentityTransform = true; constexpr bool forSystem = true; constexpr bool regionSampling = false; - DisplayRenderArea renderArea(mDisplay, sourceCrop, DEFAULT_DISPLAY_WIDTH, - DEFAULT_DISPLAY_HEIGHT, ui::Dataspace::V0_SRGB, - ui::Transform::ROT_0); + auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(), + ui::Dataspace::V0_SRGB, ui::Transform::ROT_0); auto traverseLayers = [this](const LayerVector::Visitor& visitor) { - return mFlinger.traverseLayersInDisplay(mDisplay, visitor); + return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(), + CaptureArgs::UNSET_UID, visitor); }; // TODO: Eliminate expensive/real allocation if possible. const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; - mCaptureScreenBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(), + mCaptureScreenBuffer = new GraphicBuffer(renderArea->getReqWidth(), renderArea->getReqHeight(), HAL_PIXEL_FORMAT_RGBA_8888, 1, usage, "screenshot"); int fd = -1; status_t result = - mFlinger.captureScreenImplLocked(renderArea, traverseLayers, mCaptureScreenBuffer.get(), - useIdentityTransform, forSystem, &fd, regionSampling); + mFlinger.renderScreenImplLocked(*renderArea, traverseLayers, mCaptureScreenBuffer.get(), + forSystem, &fd, regionSampling); if (fd >= 0) { close(fd); } @@ -334,8 +343,6 @@ struct BaseDisplayVariant { EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1); EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1); - EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); - EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1); EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1); @@ -348,7 +355,7 @@ struct BaseDisplayVariant { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>&, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -397,7 +404,7 @@ struct BaseDisplayVariant { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>&, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -444,8 +451,6 @@ struct PoweredOffDisplaySetupVariant : public BaseDisplayVariant<PoweredOffDispl template <typename Case> static void setupCommonCompositionCallExpectations(CompositionTest* test) { - EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); - // TODO: This seems like an unnecessary call if display is powered off. EXPECT_CALL(*test->mComposer, setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY)) @@ -460,8 +465,6 @@ struct PoweredOffDisplaySetupVariant : public BaseDisplayVariant<PoweredOffDispl static void setupHwcForcedClientCompositionCallExpectations(CompositionTest*) {} static void setupRECompositionCallExpectations(CompositionTest* test) { - EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); - // TODO: This seems like an unnecessary call if display is powered off. EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence()) .WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence)); @@ -544,7 +547,6 @@ struct BaseLayerProperties { enqueueBuffer(test, layer); Mock::VerifyAndClearExpectations(test->mMessageQueue); - EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); bool ignoredRecomputeVisibleRegions; layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0); Mock::VerifyAndClear(test->mRenderEngine); @@ -577,8 +579,6 @@ struct BaseLayerProperties { .Times(1); // TODO: Coverage of other values EXPECT_CALL(*test->mComposer, setLayerZOrder(HWC_DISPLAY, HWC_LAYER, 0u)).Times(1); - // TODO: Coverage of other values - EXPECT_CALL(*test->mComposer, setLayerInfo(HWC_DISPLAY, HWC_LAYER, 0u, 0u)).Times(1); // These expectations retire on saturation as the code path these // expectations are for appears to make an extra call to them. @@ -648,7 +648,7 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>& layerSettings, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -697,7 +697,7 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>& layerSettings, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -766,16 +766,15 @@ struct SidebandLayerProperties : public BaseLayerProperties<SidebandLayerPropert static void setupREBufferCompositionCommonCallExpectations(CompositionTest* /*test*/) {} }; -struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> { - using Base = BaseLayerProperties<SecureLayerProperties>; - - static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure; +template <typename LayerProperties> +struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> { + using Base = BaseLayerProperties<LayerProperties>; static void setupInsecureREBufferCompositionCommonCallExpectations(CompositionTest* test) { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<const renderengine::LayerSettings*>& layerSettings, - ANativeWindowBuffer*, const bool, base::unique_fd&&, + const sp<GraphicBuffer>&, const bool, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), @@ -810,6 +809,13 @@ struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> } }; +struct ParentSecureLayerProperties + : public CommonSecureLayerProperties<ParentSecureLayerProperties> {}; + +struct SecureLayerProperties : public CommonSecureLayerProperties<SecureLayerProperties> { + static constexpr uint32_t LAYER_FLAGS = ISurfaceComposerClient::eSecure; +}; + struct CursorLayerProperties : public BaseLayerProperties<CursorLayerProperties> { using Base = BaseLayerProperties<CursorLayerProperties>; @@ -845,6 +851,13 @@ struct BaseLayerVariant { Mock::VerifyAndClear(test->mRenderEngine); Mock::VerifyAndClearExpectations(test->mMessageQueue); + initLayerDrawingStateAndComputeBounds(test, layer); + + return layer; + } + + template <typename L> + static void initLayerDrawingStateAndComputeBounds(CompositionTest* test, sp<L> layer) { auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); layerDrawingState.layerStack = DEFAULT_LAYER_STACK; layerDrawingState.active.w = 100; @@ -852,8 +865,6 @@ struct BaseLayerVariant { layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], LayerProperties::COLOR[2], LayerProperties::COLOR[3]); layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform(), 0.f /* shadowRadius */); - - return layer; } static void injectLayer(CompositionTest* test, sp<Layer> layer) { @@ -891,7 +902,7 @@ struct EffectLayerVariant : public BaseLayerVariant<LayerProperties> { static FlingerLayerType createLayer(CompositionTest* test) { FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() { return new EffectLayer( - LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), "test-layer", + LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer", LayerProperties::WIDTH, LayerProperties::HEIGHT, LayerProperties::LAYER_FLAGS, LayerMetadata())); }); @@ -930,8 +941,7 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { FlingerLayerType layer = Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() { - sp<Client> client; - LayerCreationArgs args(test->mFlinger.mFlinger.get(), client, "test-layer", + LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer", LayerProperties::WIDTH, LayerProperties::HEIGHT, LayerProperties::LAYER_FLAGS, LayerMetadata()); args.textureName = test->mFlinger.mutableTexturePool().back(); @@ -975,6 +985,49 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { } }; +template <typename LayerProperties> +struct ContainerLayerVariant : public BaseLayerVariant<LayerProperties> { + using Base = BaseLayerVariant<LayerProperties>; + using FlingerLayerType = sp<ContainerLayer>; + + static FlingerLayerType createLayer(CompositionTest* test) { + LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer", + LayerProperties::WIDTH, LayerProperties::HEIGHT, + LayerProperties::LAYER_FLAGS, LayerMetadata()); + FlingerLayerType layer = new ContainerLayer(args); + Base::template initLayerDrawingStateAndComputeBounds(test, layer); + return layer; + } +}; + +template <typename LayerVariant, typename ParentLayerVariant> +struct ChildLayerVariant : public LayerVariant { + using Base = LayerVariant; + using FlingerLayerType = typename LayerVariant::FlingerLayerType; + using ParentBase = ParentLayerVariant; + + static FlingerLayerType createLayer(CompositionTest* test) { + // Need to create child layer first. Otherwise layer history size will be 2. + FlingerLayerType layer = Base::createLayer(test); + + typename ParentBase::FlingerLayerType parentLayer = ParentBase::createLayer(test); + parentLayer->addChild(layer); + test->mFlinger.setLayerDrawingParent(layer, parentLayer); + + test->mAuxiliaryLayers.push_back(parentLayer); + + return layer; + } + + static void cleanupInjectedLayers(CompositionTest* test) { + // Clear auxiliary layers first so that child layer can be successfully destroyed in the + // following call. + test->mAuxiliaryLayers.clear(); + + Base::cleanupInjectedLayers(test); + } +}; + /* ------------------------------------------------------------------------ * Variants to control how the composition type is changed */ @@ -1364,6 +1417,38 @@ TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) { } /* ------------------------------------------------------------------------ + * Layers with a parent layer with ISurfaceComposerClient::eSecure, on a non-secure display + */ + +TEST_F(CompositionTest, + HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<InsecureDisplaySetupVariant, + ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>, + ContainerLayerVariant<SecureLayerProperties>>, + KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>, + ForcedClientCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, + HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<InsecureDisplaySetupVariant, + ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>, + ContainerLayerVariant<SecureLayerProperties>>, + KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>, + ForcedClientCompositionResultVariant>>(); +} + +TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) { + captureScreenComposition< + CompositionCase<InsecureDisplaySetupVariant, + ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>, + ContainerLayerVariant<SecureLayerProperties>>, + NoCompositionTypeVariant, REScreenshotResultVariant>>(); +} + +/* ------------------------------------------------------------------------ * Cursor layers */ diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp index afebc40aa9..54f4c7c018 100644 --- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp +++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp @@ -27,13 +27,99 @@ #include "AsyncCallRecorder.h" #include "Scheduler/DispSyncSource.h" -#include "mock/MockDispSync.h" +#include "Scheduler/VSyncDispatch.h" namespace android { namespace { using namespace std::chrono_literals; -using testing::Return; +using namespace testing; + +class MockVSyncDispatch : public scheduler::VSyncDispatch { +public: + MOCK_METHOD2(registerCallback, + CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string)); + MOCK_METHOD1(unregisterCallback, void(CallbackToken)); + MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming)); + MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token)); + MOCK_CONST_METHOD1(dump, void(std::string&)); + + MockVSyncDispatch() { + ON_CALL(*this, registerCallback) + .WillByDefault( + [this](std::function<void(nsecs_t, nsecs_t, nsecs_t)> const& callback, + std::string) { + CallbackToken token(mNextToken); + mNextToken++; + + mCallbacks.emplace(token, CallbackData(callback)); + ALOGD("registerCallback: %zu", token.value()); + return token; + }); + + ON_CALL(*this, unregisterCallback).WillByDefault([this](CallbackToken token) { + ALOGD("unregisterCallback: %zu", token.value()); + mCallbacks.erase(token); + }); + + ON_CALL(*this, schedule).WillByDefault([this](CallbackToken token, ScheduleTiming timing) { + ALOGD("schedule: %zu", token.value()); + if (mCallbacks.count(token) == 0) { + ALOGD("schedule: callback %zu not registered", token.value()); + return scheduler::ScheduleResult::Error; + } + + auto& callback = mCallbacks.at(token); + callback.scheduled = true; + callback.vsyncTime = timing.earliestVsync; + callback.targetWakeupTime = + timing.earliestVsync - timing.workDuration - timing.readyDuration; + ALOGD("schedule: callback %zu scheduled", token.value()); + return scheduler::ScheduleResult::Scheduled; + }); + + ON_CALL(*this, cancel).WillByDefault([this](CallbackToken token) { + ALOGD("cancel: %zu", token.value()); + if (mCallbacks.count(token) == 0) { + ALOGD("cancel: callback %zu is not registered", token.value()); + return scheduler::CancelResult::Error; + } + + auto& callback = mCallbacks.at(token); + callback.scheduled = false; + ALOGD("cancel: callback %zu cancelled", token.value()); + return scheduler::CancelResult::Cancelled; + }); + } + + void triggerCallbacks() { + ALOGD("triggerCallbacks"); + for (auto& [token, callback] : mCallbacks) { + if (callback.scheduled) { + ALOGD("triggerCallbacks: callback %zu", token.value()); + callback.scheduled = false; + callback.func(callback.vsyncTime, callback.targetWakeupTime, callback.readyTime); + } else { + ALOGD("triggerCallbacks: callback %zu is not scheduled", token.value()); + } + } + } + +private: + struct CallbackData { + explicit CallbackData(std::function<void(nsecs_t, nsecs_t, nsecs_t)> func) + : func(std::move(func)) {} + + std::function<void(nsecs_t, nsecs_t, nsecs_t)> func; + bool scheduled = false; + nsecs_t vsyncTime = 0; + nsecs_t targetWakeupTime = 0; + nsecs_t readyTime = 0; + }; + + std::unordered_map<CallbackToken, CallbackData> mCallbacks; + size_t mNextToken; +}; class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback { protected: @@ -43,15 +129,19 @@ protected: void createDispSync(); void createDispSyncSource(); - void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp) override; + void onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp, + nsecs_t deadlineTimestamp) override; - std::unique_ptr<mock::DispSync> mDispSync; - std::unique_ptr<DispSyncSource> mDispSyncSource; + std::unique_ptr<MockVSyncDispatch> mVSyncDispatch; + std::unique_ptr<scheduler::DispSyncSource> mDispSyncSource; - AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t, nsecs_t, nsecs_t)> mVSyncEventCallRecorder; - static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms; + static constexpr std::chrono::nanoseconds mWorkDuration = 20ms; + static constexpr std::chrono::nanoseconds mReadyDuration = 10ms; static constexpr int mIterations = 100; + const scheduler::VSyncDispatch::CallbackToken mFakeToken{2398}; + const std::string mName = "DispSyncSourceTest"; }; DispSyncSourceTest::DispSyncSourceTest() { @@ -66,20 +156,21 @@ DispSyncSourceTest::~DispSyncSourceTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } -void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) { +void DispSyncSourceTest::onVSyncEvent(nsecs_t when, nsecs_t expectedVSyncTimestamp, + nsecs_t deadlineTimestamp) { ALOGD("onVSyncEvent: %" PRId64, when); - mVSyncEventCallRecorder.recordCall(when); + mVSyncEventCallRecorder.recordCall(when, expectedVSyncTimestamp, deadlineTimestamp); } void DispSyncSourceTest::createDispSync() { - mDispSync = std::make_unique<mock::DispSync>(); + mVSyncDispatch = std::make_unique<MockVSyncDispatch>(); } void DispSyncSourceTest::createDispSyncSource() { - createDispSync(); - mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true, - "DispSyncSourceTest"); + mDispSyncSource = + std::make_unique<scheduler::DispSyncSource>(*mVSyncDispatch, mWorkDuration, + mReadyDuration, true, mName.c_str()); mDispSyncSource->setCallback(this); } @@ -89,57 +180,119 @@ void DispSyncSourceTest::createDispSyncSource() { TEST_F(DispSyncSourceTest, createDispSync) { createDispSync(); - EXPECT_TRUE(mDispSync); + EXPECT_TRUE(mVSyncDispatch); } TEST_F(DispSyncSourceTest, createDispSyncSource) { + createDispSync(); + + InSequence seq; + EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).WillOnce(Return(mFakeToken)); + EXPECT_CALL(*mVSyncDispatch, cancel(mFakeToken)) + .WillOnce(Return(scheduler::CancelResult::Cancelled)); + EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mFakeToken)).WillOnce(Return()); createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); } TEST_F(DispSyncSourceTest, noCallbackAfterInit) { + createDispSync(); + + InSequence seq; + EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1); + EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1); + EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1); createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); // DispSyncSource starts with Vsync disabled - mDispSync->triggerCallback(); + mVSyncDispatch->triggerCallbacks(); EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value()); } TEST_F(DispSyncSourceTest, waitForCallbacks) { + createDispSync(); + + InSequence seq; + EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1); + EXPECT_CALL(*mVSyncDispatch, + schedule(_, Truly([&](auto timings) { + return timings.workDuration == mWorkDuration.count() && + timings.readyDuration == mReadyDuration.count(); + }))) + .Times(mIterations + 1); + EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1); + EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1); createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); mDispSyncSource->setVSyncEnabled(true); - EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count()); - for (int i = 0; i < mIterations; i++) { - mDispSync->triggerCallback(); - EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + mVSyncDispatch->triggerCallbacks(); + const auto callbackData = mVSyncEventCallRecorder.waitForCall(); + ASSERT_TRUE(callbackData.has_value()); + const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value(); + EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count()); } } -TEST_F(DispSyncSourceTest, waitForCallbacksWithPhaseChange) { +TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) { + createDispSync(); + + InSequence seq; + EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1); + EXPECT_CALL(*mVSyncDispatch, + schedule(_, Truly([&](auto timings) { + return timings.workDuration == mWorkDuration.count() && + timings.readyDuration == mReadyDuration.count(); + }))) + .Times(1); + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); mDispSyncSource->setVSyncEnabled(true); - EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count()); - + EXPECT_CALL(*mVSyncDispatch, + schedule(_, Truly([&](auto timings) { + return timings.workDuration == mWorkDuration.count() && + timings.readyDuration == mReadyDuration.count(); + }))) + .Times(mIterations); for (int i = 0; i < mIterations; i++) { - mDispSync->triggerCallback(); - EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + mVSyncDispatch->triggerCallbacks(); + const auto callbackData = mVSyncEventCallRecorder.waitForCall(); + ASSERT_TRUE(callbackData.has_value()); + const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value(); + EXPECT_EQ(when, expectedVSyncTimestamp - mWorkDuration.count() - mReadyDuration.count()); } - EXPECT_CALL(*mDispSync, getPeriod()).Times(1).WillOnce(Return(16666666)); - mDispSyncSource->setPhaseOffset((mPhaseOffset / 2).count()); - - EXPECT_EQ(mDispSync->getCallbackPhase(), (mPhaseOffset / 2).count()); - + const auto newDuration = mWorkDuration / 2; + EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) { + return timings.workDuration == newDuration.count() && + timings.readyDuration == 0; + }))) + .Times(1); + mDispSyncSource->setDuration(newDuration, 0ns); + + EXPECT_CALL(*mVSyncDispatch, schedule(_, Truly([&](auto timings) { + return timings.workDuration == newDuration.count() && + timings.readyDuration == 0; + }))) + .Times(mIterations); for (int i = 0; i < mIterations; i++) { - mDispSync->triggerCallback(); - EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + mVSyncDispatch->triggerCallbacks(); + const auto callbackData = mVSyncEventCallRecorder.waitForCall(); + ASSERT_TRUE(callbackData.has_value()); + const auto [when, expectedVSyncTimestamp, deadlineTimestamp] = callbackData.value(); + EXPECT_EQ(when, expectedVSyncTimestamp - newDuration.count()); } + + EXPECT_CALL(*mVSyncDispatch, cancel(_)).Times(1); + EXPECT_CALL(*mVSyncDispatch, unregisterCallback(_)).Times(1); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp new file mode 100644 index 0000000000..2e53cd158d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_GetBestColorModeTest.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android { +namespace { + +using hal::RenderIntent; + +using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; + +class GetBestColorModeTest : public DisplayTransactionTest { +public: + void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; } + + void addHwcColorModesMapping(ui::ColorMode colorMode, + std::vector<ui::RenderIntent> renderIntents) { + mHwcColorModes[colorMode] = renderIntents; + } + + void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; } + + void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; } + + void getBestColorMode() { + auto displayDevice = + injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) { + injector.setHwcColorModes(mHwcColorModes); + injector.setHasWideColorGamut(mHasWideColorGamut); + injector.setNativeWindow(mNativeWindow); + }); + + displayDevice->getCompositionDisplay() + ->getDisplayColorProfile() + ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace, + &mOutColorMode, &mOutRenderIntent); + } + + ui::Dataspace mOutDataspace; + ui::ColorMode mOutColorMode; + ui::RenderIntent mOutRenderIntent; + +private: + ui::Dataspace mInputDataspace; + ui::RenderIntent mInputRenderIntent; + bool mHasWideColorGamut = false; + std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes; +}; + +TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) { + addHwcColorModesMapping(ui::ColorMode::SRGB, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + setInputDataspace(ui::Dataspace::DISPLAY_P3); + setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); + setHasWideColorGamut(true); + + getBestColorMode(); + + ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace); + ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode); + ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); +} + +TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) { + addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + addHwcColorModesMapping(ui::ColorMode::SRGB, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + setInputDataspace(ui::Dataspace::DISPLAY_P3); + setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); + setHasWideColorGamut(true); + + getBestColorMode(); + + ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace); + ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode); + ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); +} + +TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) { + addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020, + std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); + setInputDataspace(ui::Dataspace::DISPLAY_P3); + setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); + setHasWideColorGamut(true); + + getBestColorMode(); + + ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace); + ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode); + ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp new file mode 100644 index 0000000000..9fe30f8f72 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetProjectionTest.cpp @@ -0,0 +1,250 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android { +namespace { + +using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; + +class DisplayDeviceSetProjectionTest : public DisplayTransactionTest { +public: + static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary + static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary + + static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0; + static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90; + static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V; + static constexpr int32_t TRANSFORM_FLAGS_ROT_270 = + HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90; + + DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize, + ui::Rotation physicalOrientation) + : mFlingerDisplaySize(flingerDisplaySize), + mHardwareDisplaySize(hardwareDisplaySize), + mPhysicalOrientation(physicalOrientation), + mDisplayDevice(createDisplayDevice()) {} + + sp<DisplayDevice> createDisplayDevice() { + return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) { + injector.setPhysicalOrientation(mPhysicalOrientation); + }); + } + + ui::Size swapWH(const ui::Size size) const { return ui::Size(size.height, size.width); } + + void setProjectionForRotation0() { + // A logical rotation of 0 uses the SurfaceFlinger display size + mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize), + Rect(mFlingerDisplaySize)); + } + + void setProjectionForRotation90() { + // A logical rotation of 90 uses the SurfaceFlinger display size with + // the width/height swapped. + mDisplayDevice->setProjection(ui::ROTATION_90, Rect(swapWH(mFlingerDisplaySize)), + Rect(swapWH(mFlingerDisplaySize))); + } + + void setProjectionForRotation180() { + // A logical rotation of 180 uses the SurfaceFlinger display size + mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize), + Rect(mFlingerDisplaySize)); + } + + void setProjectionForRotation270() { + // A logical rotation of 270 uses the SurfaceFlinger display size with + // the width/height swapped. + mDisplayDevice->setProjection(ui::ROTATION_270, Rect(swapWH(mFlingerDisplaySize)), + Rect(swapWH(mFlingerDisplaySize))); + } + + void expectStateForHardwareTransform0() { + const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState(); + EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width, + mHardwareDisplaySize.height), + compositionState.transform); + EXPECT_EQ(ui::ROTATION_0, compositionState.displaySpace.orientation); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content); + EXPECT_EQ(false, compositionState.needsFiltering); + } + + void expectStateForHardwareTransform90() { + const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState(); + EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width, + mHardwareDisplaySize.height), + compositionState.transform); + EXPECT_EQ(ui::ROTATION_90, compositionState.displaySpace.orientation); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content); + // For 90, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display + // size width and height swapped + EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), + compositionState.orientedDisplaySpace.content); + EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content); + EXPECT_EQ(false, compositionState.needsFiltering); + } + + void expectStateForHardwareTransform180() { + const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState(); + EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width, + mHardwareDisplaySize.height), + compositionState.transform); + EXPECT_EQ(ui::ROTATION_180, compositionState.displaySpace.orientation); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.orientedDisplaySpace.content); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.layerStackSpace.content); + EXPECT_EQ(false, compositionState.needsFiltering); + } + + void expectStateForHardwareTransform270() { + const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState(); + EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width, + mHardwareDisplaySize.height), + compositionState.transform); + EXPECT_EQ(ui::ROTATION_270, compositionState.displaySpace.orientation); + EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.displaySpace.content); + // For 270, the orientedDisplaySpaceRect and layerStackSpaceRect have the hardware display + // size width and height swapped + EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), + compositionState.orientedDisplaySpace.content); + EXPECT_EQ(Rect(swapWH(mHardwareDisplaySize)), compositionState.layerStackSpace.content); + EXPECT_EQ(false, compositionState.needsFiltering); + } + + const ui::Size mFlingerDisplaySize; + const ui::Size mHardwareDisplaySize; + const ui::Rotation mPhysicalOrientation; + const sp<DisplayDevice> mDisplayDevice; +}; + +struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest { + DisplayDeviceSetProjectionTest_Installed0() + : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::ROTATION_0) {} +}; + +TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) { + setProjectionForRotation0(); + expectStateForHardwareTransform0(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) { + setProjectionForRotation90(); + expectStateForHardwareTransform90(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) { + setProjectionForRotation180(); + expectStateForHardwareTransform180(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) { + setProjectionForRotation270(); + expectStateForHardwareTransform270(); +} + +struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest { + DisplayDeviceSetProjectionTest_Installed90() + : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH), + ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::ROTATION_90) {} +}; + +TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) { + setProjectionForRotation0(); + expectStateForHardwareTransform90(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) { + setProjectionForRotation90(); + expectStateForHardwareTransform180(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) { + setProjectionForRotation180(); + expectStateForHardwareTransform270(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) { + setProjectionForRotation270(); + expectStateForHardwareTransform0(); +} + +struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest { + DisplayDeviceSetProjectionTest_Installed180() + : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::ROTATION_180) {} +}; + +TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) { + setProjectionForRotation0(); + expectStateForHardwareTransform180(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) { + setProjectionForRotation90(); + expectStateForHardwareTransform270(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) { + setProjectionForRotation180(); + expectStateForHardwareTransform0(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) { + setProjectionForRotation270(); + expectStateForHardwareTransform90(); +} + +struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest { + DisplayDeviceSetProjectionTest_Installed270() + : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH), + ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), + ui::ROTATION_270) {} +}; + +TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) { + setProjectionForRotation0(); + expectStateForHardwareTransform270(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) { + setProjectionForRotation90(); + expectStateForHardwareTransform0(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) { + setProjectionForRotation180(); + expectStateForHardwareTransform90(); +} + +TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) { + setProjectionForRotation270(); + expectStateForHardwareTransform180(); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp new file mode 100644 index 0000000000..be7609a1a0 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DisplayIdGeneratorTest.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <vector> + +#include <ui/DisplayId.h> +#include "DisplayIdGenerator.h" + +namespace android { + +template <typename T> +void testNextId(DisplayIdGenerator<T>& generator) { + constexpr int kNumIds = 5; + std::vector<T> ids; + for (int i = 0; i < kNumIds; i++) { + const auto id = generator.nextId(); + ASSERT_TRUE(id); + ids.push_back(*id); + } + + // All IDs should be different. + for (size_t i = 0; i < kNumIds; i++) { + for (size_t j = i + 1; j < kNumIds; j++) { + EXPECT_NE(ids[i], ids[j]); + } + } +} + +TEST(DisplayIdGeneratorTest, nextIdGpuVirtual) { + RandomDisplayIdGenerator<GpuVirtualDisplayId> generator; + testNextId(generator); +} + +TEST(DisplayIdGeneratorTest, nextIdHalVirtual) { + RandomDisplayIdGenerator<HalVirtualDisplayId> generator; + testNextId(generator); +} + +TEST(DisplayIdGeneratorTest, markUnused) { + constexpr size_t kMaxIdsCount = 5; + RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount); + + const auto id = generator.nextId(); + EXPECT_TRUE(id); + + for (int i = 1; i < kMaxIdsCount; i++) { + EXPECT_TRUE(generator.nextId()); + } + + EXPECT_FALSE(generator.nextId()); + + generator.markUnused(*id); + EXPECT_TRUE(generator.nextId()); +} + +TEST(DisplayIdGeneratorTest, maxIdsCount) { + constexpr size_t kMaxIdsCount = 5; + RandomDisplayIdGenerator<GpuVirtualDisplayId> generator(kMaxIdsCount); + + for (int i = 0; i < kMaxIdsCount; i++) { + EXPECT_TRUE(generator.nextId()); + } + + EXPECT_FALSE(generator.nextId()); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp index 2a0e913e7a..02ce07904e 100644 --- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp @@ -22,6 +22,8 @@ #include "DisplayHardware/DisplayIdentification.h" +using ::testing::ElementsAre; + namespace android { namespace { @@ -312,93 +314,92 @@ TEST(DisplayIdentificationTest, deviceProductInfo) { using ManufactureYear = DeviceProductInfo::ManufactureYear; using ManufactureWeekAndYear = DeviceProductInfo::ManufactureWeekAndYear; using ModelYear = DeviceProductInfo::ModelYear; - using RelativeAddress = DeviceProductInfo::RelativeAddress; { const auto displayIdInfo = parseDisplayIdentificationData(0, getInternalEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("", info.name.data()); + EXPECT_EQ("", info.name); EXPECT_STREQ("SEC", info.manufacturerPnpId.data()); - EXPECT_STREQ("12610", info.productId.data()); + EXPECT_EQ("12610", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate)); EXPECT_EQ(2011, std::get<ManufactureYear>(info.manufactureOrModelDate).year); - EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress); + EXPECT_TRUE(info.relativeAddress.empty()); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("HP ZR30w", info.name.data()); + EXPECT_EQ("HP ZR30w", info.name); EXPECT_STREQ("HWP", info.manufacturerPnpId.data()); - EXPECT_STREQ("10348", info.productId.data()); + EXPECT_EQ("10348", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate)); const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate); EXPECT_EQ(2012, date.year); EXPECT_EQ(2, date.week); - EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress); + EXPECT_TRUE(info.relativeAddress.empty()); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getExternalEedid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("SAMSUNG", info.name.data()); + EXPECT_EQ("SAMSUNG", info.name); EXPECT_STREQ("SAM", info.manufacturerPnpId.data()); - EXPECT_STREQ("2302", info.productId.data()); + EXPECT_EQ("2302", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate)); const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate); EXPECT_EQ(2011, date.year); EXPECT_EQ(41, date.week); - EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress); + EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0)); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getPanasonicTvEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("Panasonic-TV", info.name.data()); + EXPECT_EQ("Panasonic-TV", info.name); EXPECT_STREQ("MEI", info.manufacturerPnpId.data()); - EXPECT_STREQ("41622", info.productId.data()); + EXPECT_EQ("41622", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate)); const auto& date = std::get<ManufactureYear>(info.manufactureOrModelDate); EXPECT_EQ(2019, date.year); - EXPECT_EQ((RelativeAddress{{2, 0, 0, 0}}), info.relativeAddress); + EXPECT_THAT(info.relativeAddress, ElementsAre(2, 0, 0, 0)); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getHisenseTvEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("Hisense", info.name.data()); + EXPECT_EQ("Hisense", info.name); EXPECT_STREQ("HEC", info.manufacturerPnpId.data()); - EXPECT_STREQ("0", info.productId.data()); + EXPECT_EQ("0", info.productId); ASSERT_TRUE(std::holds_alternative<ManufactureWeekAndYear>(info.manufactureOrModelDate)); const auto& date = std::get<ManufactureWeekAndYear>(info.manufactureOrModelDate); EXPECT_EQ(2019, date.year); EXPECT_EQ(18, date.week); - EXPECT_EQ((RelativeAddress{{1, 2, 3, 4}}), info.relativeAddress); + EXPECT_THAT(info.relativeAddress, ElementsAre(1, 2, 3, 4)); } { const auto displayIdInfo = parseDisplayIdentificationData(0, getCtlDisplayEdid()); ASSERT_TRUE(displayIdInfo); ASSERT_TRUE(displayIdInfo->deviceProductInfo); const auto& info = *displayIdInfo->deviceProductInfo; - EXPECT_STREQ("LP2361", info.name.data()); + EXPECT_EQ("LP2361", info.name); EXPECT_STREQ("CTL", info.manufacturerPnpId.data()); - EXPECT_STREQ("9373", info.productId.data()); + EXPECT_EQ("9373", info.productId); ASSERT_TRUE(std::holds_alternative<ModelYear>(info.manufactureOrModelDate)); EXPECT_EQ(2013, std::get<ModelYear>(info.manufactureOrModelDate).year); - EXPECT_EQ(DeviceProductInfo::NO_RELATIVE_ADDRESS, info.relativeAddress); + EXPECT_TRUE(info.relativeAddress.empty()); } } -TEST(DisplayIdentificationTest, getFallbackDisplayId) { +TEST(DisplayIdentificationTest, fromPort) { // Manufacturer ID should be invalid. - ASSERT_FALSE(getPnpId(getFallbackDisplayId(0))); - ASSERT_FALSE(getPnpId(getFallbackDisplayId(0xffu))); + ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0))); + ASSERT_FALSE(getPnpId(PhysicalDisplayId::fromPort(0xffu))); } TEST(DisplayIdentificationTest, getVirtualDisplayId) { diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 06bdcdc666..f0311bdaab 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -14,154 +14,22 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include <type_traits> - -#include <compositionengine/Display.h> -#include <compositionengine/DisplayColorProfile.h> -#include <compositionengine/impl/Display.h> -#include <compositionengine/impl/OutputCompositionState.h> -#include <compositionengine/mock/Display.h> -#include <compositionengine/mock/DisplayColorProfile.h> -#include <compositionengine/mock/DisplaySurface.h> -#include <compositionengine/mock/RenderSurface.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <gui/mock/GraphicBufferConsumer.h> -#include <gui/mock/GraphicBufferProducer.h> -#include <log/log.h> -#include <renderengine/mock/RenderEngine.h> -#include <ui/DebugUtils.h> - -#include "DisplayIdentificationTest.h" -#include "TestableScheduler.h" -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockDispSync.h" -#include "mock/MockEventControlThread.h" -#include "mock/MockEventThread.h" -#include "mock/MockMessageQueue.h" -#include "mock/MockNativeWindowSurface.h" -#include "mock/MockSurfaceInterceptor.h" -#include "mock/system/window/MockNativeWindow.h" +#include "DisplayTransactionTestHelpers.h" namespace android { -namespace { - -namespace hal = android::hardware::graphics::composer::hal; -using testing::_; using testing::AnyNumber; using testing::DoAll; using testing::Mock; -using testing::ResultOf; using testing::Return; -using testing::ReturnRefOfCopy; using testing::SetArgPointee; -using testing::StrictMock; -using hal::ColorMode; -using hal::Connection; -using hal::DisplayCapability; -using hal::DisplayType; -using hal::Error; -using hal::Hdr; -using hal::HWDisplayId; -using hal::IComposer; -using hal::IComposerClient; -using hal::PerFrameMetadataKey; -using hal::PowerMode; -using hal::RenderIntent; +using android::hardware::graphics::composer::hal::HWDisplayId; using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; -using HotplugEvent = TestableSurfaceFlinger::HotplugEvent; -using HWC2Display = TestableSurfaceFlinger::HWC2Display; - -constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667; -constexpr int32_t DEFAULT_DPI = 320; -constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565; - -constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value - -/* ------------------------------------------------------------------------ - * Boolean avoidance - * - * To make calls and template instantiations more readable, we define some - * local enums along with an implicit bool conversion. - */ - -#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true }; - -BOOL_SUBSTITUTE(Async); -BOOL_SUBSTITUTE(Critical); -BOOL_SUBSTITUTE(Primary); -BOOL_SUBSTITUTE(Secure); -BOOL_SUBSTITUTE(Virtual); - -/* ------------------------------------------------------------------------ - * - */ - -class DisplayTransactionTest : public testing::Test { -public: - DisplayTransactionTest(); - ~DisplayTransactionTest() override; - - // -------------------------------------------------------------------- - // Mock/Fake injection - - void injectMockScheduler(); - void injectMockComposer(int virtualDisplayCount); - void injectFakeBufferQueueFactory(); - void injectFakeNativeWindowSurfaceFactory(); - sp<DisplayDevice> injectDefaultInternalDisplay(std::function<void(FakeDisplayDeviceInjector&)>); - - // -------------------------------------------------------------------- - // Postcondition helpers - - bool hasPhysicalHwcDisplay(HWDisplayId hwcDisplayId); - bool hasTransactionFlagSet(int flag); - bool hasDisplayDevice(sp<IBinder> displayToken); - sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken); - bool hasCurrentDisplayState(sp<IBinder> displayToken); - const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken); - bool hasDrawingDisplayState(sp<IBinder> displayToken); - const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken); - - // -------------------------------------------------------------------- - // Test instances - - TestableSurfaceFlinger mFlinger; - sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow(); - sp<GraphicBuffer> mBuffer = new GraphicBuffer(); - Hwc2::mock::PowerAdvisor mPowerAdvisor; - - // These mocks are created by the test, but are destroyed by SurfaceFlinger - // by virtue of being stored into a std::unique_ptr. However we still need - // to keep a reference to them for use in setting up call expectations. - renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); - Hwc2::mock::Composer* mComposer = nullptr; - mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); - mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor(); - - mock::DispSync* mPrimaryDispSync = new mock::DispSync; - mock::EventControlThread* mEventControlThread = new mock::EventControlThread; - mock::EventThread* mEventThread = new mock::EventThread; - mock::EventThread* mSFEventThread = new mock::EventThread; - - // These mocks are created only when expected to be created via a factory. - sp<mock::GraphicBufferConsumer> mConsumer; - sp<mock::GraphicBufferProducer> mProducer; - surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr; -}; DisplayTransactionTest::DisplayTransactionTest() { const ::testing::TestInfo* const test_info = @@ -188,7 +56,7 @@ DisplayTransactionTest::DisplayTransactionTest() { injectMockScheduler(); mFlinger.mutableEventQueue().reset(mMessageQueue); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); - mFlinger.mutableInterceptor().reset(mSurfaceInterceptor); + mFlinger.mutableInterceptor() = mSurfaceInterceptor; injectMockComposer(0); } @@ -202,18 +70,20 @@ DisplayTransactionTest::~DisplayTransactionTest() { void DisplayTransactionTest::injectMockScheduler() { EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*mEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(mEventThread, /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*mSFEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(mSFEventThread, /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); - mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync), - std::unique_ptr<EventControlThread>(mEventControlThread), + mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController), + std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker), std::unique_ptr<EventThread>(mEventThread), - std::unique_ptr<EventThread>(mSFEventThread)); + std::unique_ptr<EventThread>(mSFEventThread), &mSchedulerCallback); } void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) { @@ -250,7 +120,7 @@ void DisplayTransactionTest::injectFakeNativeWindowSurfaceFactory() { sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay( std::function<void(FakeDisplayDeviceInjector&)> injectExtra) { - constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777}; + constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID(777); constexpr int DEFAULT_DISPLAY_WIDTH = 1080; constexpr int DEFAULT_DISPLAY_HEIGHT = 1920; constexpr HWDisplayId DEFAULT_DISPLAY_HWC_DISPLAY_ID = 0; @@ -324,3310 +194,4 @@ const DisplayDeviceState& DisplayTransactionTest::getDrawingDisplayState(sp<IBin return mFlinger.mutableDrawingState().displays.valueFor(displayToken); } -/* ------------------------------------------------------------------------ - * - */ - -template <typename PhysicalDisplay> -struct PhysicalDisplayId {}; - -template <DisplayId::Type displayId> -using VirtualDisplayId = std::integral_constant<DisplayId::Type, displayId>; - -struct NoDisplayId {}; - -template <typename> -struct IsPhysicalDisplayId : std::bool_constant<false> {}; - -template <typename PhysicalDisplay> -struct IsPhysicalDisplayId<PhysicalDisplayId<PhysicalDisplay>> : std::bool_constant<true> {}; - -template <typename> -struct DisplayIdGetter; - -template <typename PhysicalDisplay> -struct DisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> { - static std::optional<DisplayId> get() { - if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) { - return getFallbackDisplayId(static_cast<bool>(PhysicalDisplay::PRIMARY) - ? LEGACY_DISPLAY_TYPE_PRIMARY - : LEGACY_DISPLAY_TYPE_EXTERNAL); - } - - const auto info = - parseDisplayIdentificationData(PhysicalDisplay::PORT, - PhysicalDisplay::GET_IDENTIFICATION_DATA()); - return info ? std::make_optional(info->id) : std::nullopt; - } -}; - -template <DisplayId::Type displayId> -struct DisplayIdGetter<VirtualDisplayId<displayId>> { - static std::optional<DisplayId> get() { return DisplayId{displayId}; } -}; - -template <> -struct DisplayIdGetter<NoDisplayId> { - static std::optional<DisplayId> get() { return {}; } -}; - -template <typename> -struct DisplayConnectionTypeGetter { - static constexpr std::optional<DisplayConnectionType> value; -}; - -template <typename PhysicalDisplay> -struct DisplayConnectionTypeGetter<PhysicalDisplayId<PhysicalDisplay>> { - static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE; -}; - -template <typename> -struct HwcDisplayIdGetter { - static constexpr std::optional<HWDisplayId> value; -}; - -constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010; - -template <DisplayId::Type displayId> -struct HwcDisplayIdGetter<VirtualDisplayId<displayId>> { - static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID; -}; - -template <typename PhysicalDisplay> -struct HwcDisplayIdGetter<PhysicalDisplayId<PhysicalDisplay>> { - static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID; -}; - -// DisplayIdType can be: -// 1) PhysicalDisplayId<...> for generated ID of physical display backed by HWC. -// 2) VirtualDisplayId<...> for hard-coded ID of virtual display backed by HWC. -// 3) NoDisplayId for virtual display without HWC backing. -template <typename DisplayIdType, int width, int height, Critical critical, Async async, - Secure secure, Primary primary, int grallocUsage> -struct DisplayVariant { - using DISPLAY_ID = DisplayIdGetter<DisplayIdType>; - using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>; - using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>; - - // The display width and height - static constexpr int WIDTH = width; - static constexpr int HEIGHT = height; - - static constexpr int GRALLOC_USAGE = grallocUsage; - - // Whether the display is virtual or physical - static constexpr Virtual VIRTUAL = - IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE; - - // When creating native window surfaces for the framebuffer, whether those should be critical - static constexpr Critical CRITICAL = critical; - - // When creating native window surfaces for the framebuffer, whether those should be async - static constexpr Async ASYNC = async; - - // Whether the display should be treated as secure - static constexpr Secure SECURE = secure; - - // Whether the display is primary - static constexpr Primary PRIMARY = primary; - - static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) { - auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder(); - if (auto displayId = DISPLAY_ID::get()) { - ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal}); - } else { - ceDisplayArgs.setUseHwcVirtualDisplays(false); - } - ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor).build(); - - auto compositionDisplay = - compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), - ceDisplayArgs.build()); - - auto injector = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay, - CONNECTION_TYPE::value, HWC_DISPLAY_ID_OPT::value, - static_cast<bool>(PRIMARY)); - - injector.setSecure(static_cast<bool>(SECURE)); - injector.setNativeWindow(test->mNativeWindow); - - // Creating a DisplayDevice requires getting default dimensions from the - // native window along with some other initial setup. - EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0))); - EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0))); - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)) - .WillRepeatedly(Return(0)); - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)) - .WillRepeatedly(Return(0)); - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)) - .WillRepeatedly(Return(0)); - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)) - .WillRepeatedly(Return(0)); - - return injector; - } - - // Called by tests to set up any native window creation call expectations. - static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow()) - .WillOnce(Return(test->mNativeWindow)); - - EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0))); - EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0))); - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)) - .WillRepeatedly(Return(0)); - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)) - .WillRepeatedly(Return(0)); - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)) - .WillRepeatedly(Return(0)); - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)) - .WillRepeatedly(Return(0)); - } - - static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR)); - EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR)); - EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE)) - .WillRepeatedly(Return(NO_ERROR)); - EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT)) - .WillRepeatedly(Return(NO_ERROR)); - EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_)) - .WillRepeatedly(Return(NO_ERROR)); - } - - static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return()); - } -}; - -template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant, - typename PhysicalDisplay = void> -struct HwcDisplayVariant { - // The display id supplied by the HWC - static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId; - - // The HWC display type - static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType; - - // The HWC active configuration id - static constexpr int HWC_ACTIVE_CONFIG_ID = 2001; - static constexpr PowerMode INIT_POWER_MODE = PowerMode::ON; - - static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) { - test->mFlinger.mutablePendingHotplugEvents().emplace_back( - HotplugEvent{HWC_DISPLAY_ID, connection}); - } - - // Called by tests to inject a HWC display setup - static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) { - const auto displayId = DisplayVariant::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - FakeHwcDisplayInjector(*displayId, HWC_DISPLAY_TYPE, - static_cast<bool>(DisplayVariant::PRIMARY)) - .setHwcDisplayId(HWC_DISPLAY_ID) - .setWidth(DisplayVariant::WIDTH) - .setHeight(DisplayVariant::HEIGHT) - .setActiveConfig(HWC_ACTIVE_CONFIG_ID) - .setPowerMode(INIT_POWER_MODE) - .inject(&test->mFlinger, test->mComposer); - } - - // Called by tests to inject a HWC display setup - static void injectHwcDisplay(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), - Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE)) - .WillOnce(Return(Error::NONE)); - injectHwcDisplayWithNoDefaultCapabilities(test); - } - - static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( - DisplayTransactionTest* test) { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - - auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() - .setPhysical({*DisplayVariant::DISPLAY_ID::get(), - PhysicalDisplay::CONNECTION_TYPE}) - .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT}) - .setIsSecure(static_cast<bool>(DisplayVariant::SECURE)) - .setPowerAdvisor(&test->mPowerAdvisor) - .setName(std::string("Injected display for ") + - test_info->test_case_name() + "." + test_info->name()) - .build(); - - return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), - ceDisplayArgs); - } - - static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) { - constexpr auto CONNECTION_TYPE = - PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal - ? IComposerClient::DisplayConnectionType::INTERNAL - : IComposerClient::DisplayConnectionType::EXTERNAL; - - EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE))); - - EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)) - .WillOnce(Return(hal::Error::NONE)); - EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}), - Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, - getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, - IComposerClient::Attribute::WIDTH, _)) - .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, - getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, - IComposerClient::Attribute::HEIGHT, _)) - .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, - getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, - IComposerClient::Attribute::VSYNC_PERIOD, _)) - .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, - getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, - IComposerClient::Attribute::DPI_X, _)) - .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, - getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, - IComposerClient::Attribute::DPI_Y, _)) - .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, - getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, - IComposerClient::Attribute::CONFIG_GROUP, _)) - .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE))); - - if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) { - EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT), - SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()), - Return(Error::NONE))); - } else { - EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _)) - .WillOnce(Return(Error::UNSUPPORTED)); - } - } - - // Called by tests to set up HWC call expectations - static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE))); - } -}; - -// Physical displays are expected to be synchronous, secure, and have a HWC display for output. -constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY = - GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB; - -template <typename PhysicalDisplay, int width, int height, Critical critical> -struct PhysicalDisplayVariant - : DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, critical, Async::FALSE, - Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>, - HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL, - DisplayVariant<PhysicalDisplayId<PhysicalDisplay>, width, height, - critical, Async::FALSE, Secure::TRUE, - PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>, - PhysicalDisplay> {}; - -template <bool hasIdentificationData> -struct PrimaryDisplay { - static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal; - static constexpr Primary PRIMARY = Primary::TRUE; - static constexpr uint8_t PORT = 255; - static constexpr HWDisplayId HWC_DISPLAY_ID = 1001; - static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData; - static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid; -}; - -template <bool hasIdentificationData> -struct ExternalDisplay { - static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External; - static constexpr Primary PRIMARY = Primary::FALSE; - static constexpr uint8_t PORT = 254; - static constexpr HWDisplayId HWC_DISPLAY_ID = 1002; - static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData; - static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid; -}; - -struct TertiaryDisplay { - static constexpr Primary PRIMARY = Primary::FALSE; - static constexpr uint8_t PORT = 253; - static constexpr HWDisplayId HWC_DISPLAY_ID = 1003; - static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid; -}; - -// A primary display is a physical display that is critical -using PrimaryDisplayVariant = - PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>; - -// An external display is physical display that is not critical. -using ExternalDisplayVariant = - PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>; - -using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>; - -// A virtual display not supported by the HWC. -constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0; - -template <int width, int height, Secure secure> -struct NonHwcVirtualDisplayVariant - : DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure, - Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> { - using Base = DisplayVariant<NoDisplayId, width, height, Critical::FALSE, Async::TRUE, secure, - Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>; - - static void injectHwcDisplay(DisplayTransactionTest*) {} - - static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( - DisplayTransactionTest* test) { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - - auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() - .setPixels({Base::WIDTH, Base::HEIGHT}) - .setIsSecure(static_cast<bool>(Base::SECURE)) - .setPowerAdvisor(&test->mPowerAdvisor) - .setName(std::string("Injected display for ") + - test_info->test_case_name() + "." + test_info->name()) - .build(); - - return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), - ceDisplayArgs); - } - - static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0); - } - - static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) { - Base::setupNativeWindowSurfaceCreationCallExpectations(test); - EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1); - } -}; - -// A virtual display supported by the HWC. -constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER; - -template <int width, int height, Secure secure> -struct HwcVirtualDisplayVariant - : DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, secure, - Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>, - HwcDisplayVariant< - HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL, - DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, - secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> { - using Base = DisplayVariant<VirtualDisplayId<42>, width, height, Critical::FALSE, Async::TRUE, - secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>; - using Self = HwcVirtualDisplayVariant<width, height, secure>; - - static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( - DisplayTransactionTest* test) { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - - auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() - .setUseHwcVirtualDisplays(false) - .setPixels({Base::WIDTH, Base::HEIGHT}) - .setIsSecure(static_cast<bool>(Base::SECURE)) - .setPowerAdvisor(&test->mPowerAdvisor) - .setName(std::string("Injected display for ") + - test_info->test_case_name() + "." + test_info->name()) - .build(); - - auto compositionDisplay = - compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), - ceDisplayArgs); - compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get()); - - // Insert display data so that the HWC thinks it created the virtual display. - if (const auto displayId = Base::DISPLAY_ID::get()) { - test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId); - } - - return compositionDisplay; - } - - static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) { - Base::setupNativeWindowSurfaceCreationCallExpectations(test); - EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1); - } - - static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _)) - .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE)); - } -}; - -// For this variant, SurfaceFlinger should not configure itself with wide -// display support, so the display should not be configured for wide-color -// support. -struct WideColorSupportNotConfiguredVariant { - static constexpr bool WIDE_COLOR_SUPPORTED = false; - - static void injectConfigChange(DisplayTransactionTest* test) { - test->mFlinger.mutableHasWideColorDisplay() = false; - test->mFlinger.mutableUseColorManagement() = false; - test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; - } - - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0); - EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0); - EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0); - } -}; - -// For this variant, SurfaceFlinger should configure itself with wide display -// support, and the display should respond with an non-empty list of supported -// color modes. Wide-color support should be configured. -template <typename Display> -struct WideColorP3ColorimetricSupportedVariant { - static constexpr bool WIDE_COLOR_SUPPORTED = true; - - static void injectConfigChange(DisplayTransactionTest* test) { - test->mFlinger.mutableUseColorManagement() = true; - test->mFlinger.mutableHasWideColorDisplay() = true; - test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; - } - - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1); - - EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})), - Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, - getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _)) - .WillOnce(DoAll(SetArgPointee<2>( - std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})), - Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, - setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB, - RenderIntent::COLORIMETRIC)) - .WillOnce(Return(Error::NONE)); - } -}; - -// For this variant, SurfaceFlinger should configure itself with wide display -// support, but the display should respond with an empty list of supported color -// modes. Wide-color support for the display should not be configured. -template <typename Display> -struct WideColorNotSupportedVariant { - static constexpr bool WIDE_COLOR_SUPPORTED = false; - - static void injectConfigChange(DisplayTransactionTest* test) { - test->mFlinger.mutableUseColorManagement() = true; - test->mFlinger.mutableHasWideColorDisplay() = true; - } - - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE))); - EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0); - } -}; - -// For this variant, the display is not a HWC display, so no HDR support should -// be configured. -struct NonHwcDisplayHdrSupportVariant { - static constexpr bool HDR10_PLUS_SUPPORTED = false; - static constexpr bool HDR10_SUPPORTED = false; - static constexpr bool HDR_HLG_SUPPORTED = false; - static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0); - } -}; - -template <typename Display> -struct Hdr10PlusSupportedVariant { - static constexpr bool HDR10_PLUS_SUPPORTED = true; - static constexpr bool HDR10_SUPPORTED = true; - static constexpr bool HDR_HLG_SUPPORTED = false; - static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({ - Hdr::HDR10_PLUS, - Hdr::HDR10, - })), - Return(Error::NONE))); - } -}; - -// For this variant, the composer should respond with a non-empty list of HDR -// modes containing HDR10, so HDR10 support should be configured. -template <typename Display> -struct Hdr10SupportedVariant { - static constexpr bool HDR10_PLUS_SUPPORTED = false; - static constexpr bool HDR10_SUPPORTED = true; - static constexpr bool HDR_HLG_SUPPORTED = false; - static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})), - Return(Error::NONE))); - } -}; - -// For this variant, the composer should respond with a non-empty list of HDR -// modes containing HLG, so HLG support should be configured. -template <typename Display> -struct HdrHlgSupportedVariant { - static constexpr bool HDR10_PLUS_SUPPORTED = false; - static constexpr bool HDR10_SUPPORTED = false; - static constexpr bool HDR_HLG_SUPPORTED = true; - static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _)) - .WillOnce( - DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE))); - } -}; - -// For this variant, the composer should respond with a non-empty list of HDR -// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured. -template <typename Display> -struct HdrDolbyVisionSupportedVariant { - static constexpr bool HDR10_PLUS_SUPPORTED = false; - static constexpr bool HDR10_SUPPORTED = false; - static constexpr bool HDR_HLG_SUPPORTED = false; - static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})), - Return(Error::NONE))); - } -}; - -// For this variant, the composer should respond with am empty list of HDR -// modes, so no HDR support should be configured. -template <typename Display> -struct HdrNotSupportedVariant { - static constexpr bool HDR10_PLUS_SUPPORTED = false; - static constexpr bool HDR10_SUPPORTED = false; - static constexpr bool HDR_HLG_SUPPORTED = false; - static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE))); - } -}; - -struct NonHwcPerFrameMetadataSupportVariant { - static constexpr int PER_FRAME_METADATA_KEYS = 0; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0); - } -}; - -template <typename Display> -struct NoPerFrameMetadataSupportVariant { - static constexpr int PER_FRAME_METADATA_KEYS = 0; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) - .WillOnce(Return(std::vector<PerFrameMetadataKey>())); - } -}; - -template <typename Display> -struct Smpte2086PerFrameMetadataSupportVariant { - static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) - .WillOnce(Return(std::vector<PerFrameMetadataKey>({ - PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X, - PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, - PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X, - PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y, - PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X, - PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y, - PerFrameMetadataKey::WHITE_POINT_X, - PerFrameMetadataKey::WHITE_POINT_Y, - PerFrameMetadataKey::MAX_LUMINANCE, - PerFrameMetadataKey::MIN_LUMINANCE, - }))); - } -}; - -template <typename Display> -struct Cta861_3_PerFrameMetadataSupportVariant { - static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) - .WillOnce(Return(std::vector<PerFrameMetadataKey>({ - PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL, - PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, - }))); - } -}; - -template <typename Display> -struct Hdr10_Plus_PerFrameMetadataSupportVariant { - static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS; - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) - .WillOnce(Return(std::vector<PerFrameMetadataKey>({ - PerFrameMetadataKey::HDR10_PLUS_SEI, - }))); - } -}; -/* ------------------------------------------------------------------------ - * Typical display configurations to test - */ - -template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy, - typename PerFrameMetadataSupportPolicy> -struct Case { - using Display = DisplayPolicy; - using WideColorSupport = WideColorSupportPolicy; - using HdrSupport = HdrSupportPolicy; - using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy; -}; - -using SimplePrimaryDisplayCase = - Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - HdrNotSupportedVariant<PrimaryDisplayVariant>, - NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using SimpleExternalDisplayCase = - Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>, - HdrNotSupportedVariant<ExternalDisplayVariant>, - NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>; -using SimpleTertiaryDisplayCase = - Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>, - HdrNotSupportedVariant<TertiaryDisplayVariant>, - NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>; -using NonHwcVirtualDisplayCase = - Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>, - WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant, - NonHwcPerFrameMetadataSupportVariant>; -using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>; -using HwcVirtualDisplayCase = - Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant, - HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>, - NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>; -using WideColorP3ColorimetricDisplayCase = - Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>, - HdrNotSupportedVariant<PrimaryDisplayVariant>, - NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using Hdr10PlusDisplayCase = - Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - Hdr10SupportedVariant<PrimaryDisplayVariant>, - Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using Hdr10DisplayCase = - Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - Hdr10SupportedVariant<PrimaryDisplayVariant>, - NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using HdrHlgDisplayCase = - Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - HdrHlgSupportedVariant<PrimaryDisplayVariant>, - NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using HdrDolbyVisionDisplayCase = - Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>, - NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using HdrSmpte2086DisplayCase = - Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - HdrNotSupportedVariant<PrimaryDisplayVariant>, - Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; -using HdrCta861_3_DisplayCase = - Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, - HdrNotSupportedVariant<PrimaryDisplayVariant>, - Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; - -/* ------------------------------------------------------------------------ - * - * SurfaceFlinger::onHotplugReceived - */ - -TEST_F(DisplayTransactionTest, hotplugEnqueuesEventsForDisplayTransaction) { - constexpr int currentSequenceId = 123; - constexpr HWDisplayId hwcDisplayId1 = 456; - constexpr HWDisplayId hwcDisplayId2 = 654; - - // -------------------------------------------------------------------- - // Preconditions - - // Set the current sequence id for accepted events - mFlinger.mutableComposerSequenceId() = currentSequenceId; - - // Set the main thread id so that the current thread does not appear to be - // the main thread. - mFlinger.mutableMainThreadId() = std::thread::id(); - - // -------------------------------------------------------------------- - // Call Expectations - - // We expect invalidate() to be invoked once to trigger display transaction - // processing. - EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - // Simulate two hotplug events (a connect and a disconnect) - mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED); - mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED); - - // -------------------------------------------------------------------- - // Postconditions - - // The display transaction needed flag should be set. - EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); - - // All events should be in the pending event queue. - const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents(); - ASSERT_EQ(2u, pendingEvents.size()); - EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId); - EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection); - EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId); - EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection); -} - -TEST_F(DisplayTransactionTest, hotplugDiscardsUnexpectedEvents) { - constexpr int currentSequenceId = 123; - constexpr int otherSequenceId = 321; - constexpr HWDisplayId displayId = 456; - - // -------------------------------------------------------------------- - // Preconditions - - // Set the current sequence id for accepted events - mFlinger.mutableComposerSequenceId() = currentSequenceId; - - // Set the main thread id so that the current thread does not appear to be - // the main thread. - mFlinger.mutableMainThreadId() = std::thread::id(); - - // -------------------------------------------------------------------- - // Call Expectations - - // We do not expect any calls to invalidate(). - EXPECT_CALL(*mMessageQueue, invalidate()).Times(0); - - // -------------------------------------------------------------------- - // Invocation - - // Call with an unexpected sequence id - mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID); - - // -------------------------------------------------------------------- - // Postconditions - - // The display transaction needed flag should not be set - EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded)); - - // There should be no pending events - EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty()); -} - -TEST_F(DisplayTransactionTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) { - constexpr int currentSequenceId = 123; - constexpr HWDisplayId displayId1 = 456; - - // -------------------------------------------------------------------- - // Note: - // -------------------------------------------------------------------- - // This test case is a bit tricky. We want to verify that - // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we - // don't really want to provide coverage for everything the later function - // does as there are specific tests for it. - // -------------------------------------------------------------------- - - // -------------------------------------------------------------------- - // Preconditions - - // Set the current sequence id for accepted events - mFlinger.mutableComposerSequenceId() = currentSequenceId; - - // Set the main thread id so that the current thread does appear to be the - // main thread. - mFlinger.mutableMainThreadId() = std::this_thread::get_id(); - - // -------------------------------------------------------------------- - // Call Expectations - - // We expect invalidate() to be invoked once to trigger display transaction - // processing. - EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - // Simulate a disconnect on a display id that is not connected. This should - // be enqueued by onHotplugReceived(), and dequeued by - // processDisplayHotplugEventsLocked(), but then ignored as invalid. - mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED); - - // -------------------------------------------------------------------- - // Postconditions - - // The display transaction needed flag should be set. - EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); - - // There should be no event queued on return, as it should have been - // processed. - EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty()); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::createDisplay - */ - -TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForNonsecureDisplay) { - const String8 name("virtual.test"); - - // -------------------------------------------------------------------- - // Call Expectations - - // The call should notify the interceptor that a display was created. - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - sp<IBinder> displayToken = mFlinger.createDisplay(name, false); - - // -------------------------------------------------------------------- - // Postconditions - - // The display should have been added to the current state - ASSERT_TRUE(hasCurrentDisplayState(displayToken)); - const auto& display = getCurrentDisplayState(displayToken); - EXPECT_TRUE(display.isVirtual()); - EXPECT_FALSE(display.isSecure); - EXPECT_EQ(name.string(), display.displayName); - - // -------------------------------------------------------------------- - // Cleanup conditions - - // Destroying the display invalidates the display state. - EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); -} - -TEST_F(DisplayTransactionTest, createDisplaySetsCurrentStateForSecureDisplay) { - const String8 name("virtual.test"); - - // -------------------------------------------------------------------- - // Call Expectations - - // The call should notify the interceptor that a display was created. - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - sp<IBinder> displayToken = mFlinger.createDisplay(name, true); - - // -------------------------------------------------------------------- - // Postconditions - - // The display should have been added to the current state - ASSERT_TRUE(hasCurrentDisplayState(displayToken)); - const auto& display = getCurrentDisplayState(displayToken); - EXPECT_TRUE(display.isVirtual()); - EXPECT_TRUE(display.isSecure); - EXPECT_EQ(name.string(), display.displayName); - - // -------------------------------------------------------------------- - // Cleanup conditions - - // Destroying the display invalidates the display state. - EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::destroyDisplay - */ - -TEST_F(DisplayTransactionTest, destroyDisplayClearsCurrentStateForDisplay) { - using Case = NonHwcVirtualDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A virtual display exists - auto existing = Case::Display::makeFakeExistingDisplayInjector(this); - existing.inject(); - - // -------------------------------------------------------------------- - // Call Expectations - - // The call should notify the interceptor that a display was created. - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); - - // Destroying the display invalidates the display state. - EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.destroyDisplay(existing.token()); - - // -------------------------------------------------------------------- - // Postconditions - - // The display should have been removed from the current state - EXPECT_FALSE(hasCurrentDisplayState(existing.token())); - - // Ths display should still exist in the drawing state - EXPECT_TRUE(hasDrawingDisplayState(existing.token())); - - // The display transaction needed flasg should be set - EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); -} - -TEST_F(DisplayTransactionTest, destroyDisplayHandlesUnknownDisplay) { - // -------------------------------------------------------------------- - // Preconditions - - sp<BBinder> displayToken = new BBinder(); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.destroyDisplay(displayToken); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::resetDisplayState - */ - -TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { - using Case = NonHwcVirtualDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // vsync is enabled and available - mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = true; - mFlinger.scheduler()->mutableHWVsyncAvailable() = true; - - // A display exists - auto existing = Case::Display::makeFakeExistingDisplayInjector(this); - existing.inject(); - - // -------------------------------------------------------------------- - // Call Expectations - - // The call disable vsyncs - EXPECT_CALL(*mEventControlThread, setVsyncEnabled(false)).Times(1); - - // The call ends any display resyncs - EXPECT_CALL(*mPrimaryDispSync, endResync()).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.resetDisplayState(); - - // -------------------------------------------------------------------- - // Postconditions - - // vsyncs should be off and not available. - EXPECT_FALSE(mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled()); - EXPECT_FALSE(mFlinger.scheduler()->mutableHWVsyncAvailable()); - - // The display should have been removed from the display map. - EXPECT_FALSE(hasDisplayDevice(existing.token())); - - // The display should still exist in the current state - EXPECT_TRUE(hasCurrentDisplayState(existing.token())); - - // The display should have been removed from the drawing state - EXPECT_FALSE(hasDrawingDisplayState(existing.token())); -} - -/* ------------------------------------------------------------------------ - * DisplayDevice::GetBestColorMode - */ -class GetBestColorModeTest : public DisplayTransactionTest { -public: - void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; } - - void addHwcColorModesMapping(ui::ColorMode colorMode, - std::vector<ui::RenderIntent> renderIntents) { - mHwcColorModes[colorMode] = renderIntents; - } - - void setInputDataspace(ui::Dataspace dataspace) { mInputDataspace = dataspace; } - - void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; } - - void getBestColorMode() { - auto displayDevice = - injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) { - injector.setHwcColorModes(mHwcColorModes); - injector.setHasWideColorGamut(mHasWideColorGamut); - injector.setNativeWindow(mNativeWindow); - }); - - displayDevice->getCompositionDisplay() - ->getDisplayColorProfile() - ->getBestColorMode(mInputDataspace, mInputRenderIntent, &mOutDataspace, - &mOutColorMode, &mOutRenderIntent); - } - - ui::Dataspace mOutDataspace; - ui::ColorMode mOutColorMode; - ui::RenderIntent mOutRenderIntent; - -private: - ui::Dataspace mInputDataspace; - ui::RenderIntent mInputRenderIntent; - bool mHasWideColorGamut = false; - std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes; -}; - -TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) { - addHwcColorModesMapping(ui::ColorMode::SRGB, - std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); - setInputDataspace(ui::Dataspace::DISPLAY_P3); - setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); - setHasWideColorGamut(true); - - getBestColorMode(); - - ASSERT_EQ(ui::Dataspace::V0_SRGB, mOutDataspace); - ASSERT_EQ(ui::ColorMode::SRGB, mOutColorMode); - ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); -} - -TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDisplayP3) { - addHwcColorModesMapping(ui::ColorMode::DISPLAY_P3, - std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); - addHwcColorModesMapping(ui::ColorMode::SRGB, - std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); - addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020, - std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); - setInputDataspace(ui::Dataspace::DISPLAY_P3); - setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); - setHasWideColorGamut(true); - - getBestColorMode(); - - ASSERT_EQ(ui::Dataspace::DISPLAY_P3, mOutDataspace); - ASSERT_EQ(ui::ColorMode::DISPLAY_P3, mOutColorMode); - ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); -} - -TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeDISPLAY_BT2020) { - addHwcColorModesMapping(ui::ColorMode::DISPLAY_BT2020, - std::vector<ui::RenderIntent>(1, RenderIntent::COLORIMETRIC)); - setInputDataspace(ui::Dataspace::DISPLAY_P3); - setInputRenderIntent(ui::RenderIntent::COLORIMETRIC); - setHasWideColorGamut(true); - - getBestColorMode(); - - ASSERT_EQ(ui::Dataspace::DISPLAY_BT2020, mOutDataspace); - ASSERT_EQ(ui::ColorMode::DISPLAY_BT2020, mOutColorMode); - ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mOutRenderIntent); -} - -/* ------------------------------------------------------------------------ - * DisplayDevice::setProjection - */ - -class DisplayDeviceSetProjectionTest : public DisplayTransactionTest { -public: - static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary - static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary - - static constexpr int32_t TRANSFORM_FLAGS_ROT_0 = 0; - static constexpr int32_t TRANSFORM_FLAGS_ROT_90 = HAL_TRANSFORM_ROT_90; - static constexpr int32_t TRANSFORM_FLAGS_ROT_180 = HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V; - static constexpr int32_t TRANSFORM_FLAGS_ROT_270 = - HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90; - - DisplayDeviceSetProjectionTest(ui::Size flingerDisplaySize, ui::Size hardwareDisplaySize, - ui::Rotation physicalOrientation) - : mFlingerDisplaySize(flingerDisplaySize), - mHardwareDisplaySize(hardwareDisplaySize), - mPhysicalOrientation(physicalOrientation), - mDisplayDevice(createDisplayDevice()) {} - - sp<DisplayDevice> createDisplayDevice() { - return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) { - injector.setPhysicalOrientation(mPhysicalOrientation); - }); - } - - ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); } - - void setProjectionForRotation0() { - // A logical rotation of 0 uses the SurfaceFlinger display size - mDisplayDevice->setProjection(ui::ROTATION_0, Rect(mFlingerDisplaySize), - Rect(mFlingerDisplaySize)); - } - - void setProjectionForRotation90() { - // A logical rotation of 90 uses the SurfaceFlinger display size with - // the width/height swapped. - mDisplayDevice->setProjection(ui::ROTATION_90, Rect(SwapWH(mFlingerDisplaySize)), - Rect(SwapWH(mFlingerDisplaySize))); - } - - void setProjectionForRotation180() { - // A logical rotation of 180 uses the SurfaceFlinger display size - mDisplayDevice->setProjection(ui::ROTATION_180, Rect(mFlingerDisplaySize), - Rect(mFlingerDisplaySize)); - } - - void setProjectionForRotation270() { - // A logical rotation of 270 uses the SurfaceFlinger display size with - // the width/height swapped. - mDisplayDevice->setProjection(ui::ROTATION_270, Rect(SwapWH(mFlingerDisplaySize)), - Rect(SwapWH(mFlingerDisplaySize))); - } - - void expectStateForHardwareTransform0() { - const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState(); - EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_0, mHardwareDisplaySize.width, - mHardwareDisplaySize.height), - compositionState.transform); - EXPECT_EQ(TRANSFORM_FLAGS_ROT_0, compositionState.orientation); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport); - EXPECT_EQ(false, compositionState.needsFiltering); - } - - void expectStateForHardwareTransform90() { - const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState(); - EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_90, mHardwareDisplaySize.width, - mHardwareDisplaySize.height), - compositionState.transform); - EXPECT_EQ(TRANSFORM_FLAGS_ROT_90, compositionState.orientation); - EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip); - // For 90, the frame and viewport have the hardware display size width and height swapped - EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame); - EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport); - EXPECT_EQ(false, compositionState.needsFiltering); - } - - void expectStateForHardwareTransform180() { - const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState(); - EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_180, mHardwareDisplaySize.width, - mHardwareDisplaySize.height), - compositionState.transform); - EXPECT_EQ(TRANSFORM_FLAGS_ROT_180, compositionState.orientation); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.sourceClip); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.frame); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.viewport); - EXPECT_EQ(false, compositionState.needsFiltering); - } - - void expectStateForHardwareTransform270() { - const auto& compositionState = mDisplayDevice->getCompositionDisplay()->getState(); - EXPECT_EQ(ui::Transform(TRANSFORM_FLAGS_ROT_270, mHardwareDisplaySize.width, - mHardwareDisplaySize.height), - compositionState.transform); - EXPECT_EQ(TRANSFORM_FLAGS_ROT_270, compositionState.orientation); - EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.sourceClip); - EXPECT_EQ(Rect(mHardwareDisplaySize), compositionState.destinationClip); - // For 270, the frame and viewport have the hardware display size width and height swapped - EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.frame); - EXPECT_EQ(Rect(SwapWH(mHardwareDisplaySize)), compositionState.viewport); - EXPECT_EQ(false, compositionState.needsFiltering); - } - - const ui::Size mFlingerDisplaySize; - const ui::Size mHardwareDisplaySize; - const ui::Rotation mPhysicalOrientation; - const sp<DisplayDevice> mDisplayDevice; -}; - -struct DisplayDeviceSetProjectionTest_Installed0 : public DisplayDeviceSetProjectionTest { - DisplayDeviceSetProjectionTest_Installed0() - : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), - ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), - ui::ROTATION_0) {} -}; - -TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith0OutputRotation) { - setProjectionForRotation0(); - expectStateForHardwareTransform0(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith90OutputRotation) { - setProjectionForRotation90(); - expectStateForHardwareTransform90(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith180OutputRotation) { - setProjectionForRotation180(); - expectStateForHardwareTransform180(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed0, checkWith270OutputRotation) { - setProjectionForRotation270(); - expectStateForHardwareTransform270(); -} - -struct DisplayDeviceSetProjectionTest_Installed90 : public DisplayDeviceSetProjectionTest { - DisplayDeviceSetProjectionTest_Installed90() - : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH), - ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), - ui::ROTATION_90) {} -}; - -TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith0OutputRotation) { - setProjectionForRotation0(); - expectStateForHardwareTransform90(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith90OutputRotation) { - setProjectionForRotation90(); - expectStateForHardwareTransform180(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith180OutputRotation) { - setProjectionForRotation180(); - expectStateForHardwareTransform270(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed90, checkWith270OutputRotation) { - setProjectionForRotation270(); - expectStateForHardwareTransform0(); -} - -struct DisplayDeviceSetProjectionTest_Installed180 : public DisplayDeviceSetProjectionTest { - DisplayDeviceSetProjectionTest_Installed180() - : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), - ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), - ui::ROTATION_180) {} -}; - -TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith0OutputRotation) { - setProjectionForRotation0(); - expectStateForHardwareTransform180(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith90OutputRotation) { - setProjectionForRotation90(); - expectStateForHardwareTransform270(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith180OutputRotation) { - setProjectionForRotation180(); - expectStateForHardwareTransform0(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed180, checkWith270OutputRotation) { - setProjectionForRotation270(); - expectStateForHardwareTransform90(); -} - -struct DisplayDeviceSetProjectionTest_Installed270 : public DisplayDeviceSetProjectionTest { - DisplayDeviceSetProjectionTest_Installed270() - : DisplayDeviceSetProjectionTest(ui::Size(DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH), - ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), - ui::ROTATION_270) {} -}; - -TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith0OutputRotation) { - setProjectionForRotation0(); - expectStateForHardwareTransform270(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith90OutputRotation) { - setProjectionForRotation90(); - expectStateForHardwareTransform0(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith180OutputRotation) { - setProjectionForRotation180(); - expectStateForHardwareTransform90(); -} - -TEST_F(DisplayDeviceSetProjectionTest_Installed270, checkWith270OutputRotation) { - setProjectionForRotation270(); - expectStateForHardwareTransform180(); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::getDisplayNativePrimaries - */ - -class GetDisplayNativePrimaries : public DisplayTransactionTest { -public: - GetDisplayNativePrimaries(); - void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries); - void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries); - -private: - static constexpr float mStartingTestValue = 1.0f; -}; - -GetDisplayNativePrimaries::GetDisplayNativePrimaries() { - SimplePrimaryDisplayCase::Display::injectHwcDisplay(this); - injectFakeNativeWindowSurfaceFactory(); -} - -void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries( - ui::DisplayPrimaries& primaries) { - float startingVal = mStartingTestValue; - primaries.red.X = startingVal++; - primaries.red.Y = startingVal++; - primaries.red.Z = startingVal++; - primaries.green.X = startingVal++; - primaries.green.Y = startingVal++; - primaries.green.Z = startingVal++; - primaries.blue.X = startingVal++; - primaries.blue.Y = startingVal++; - primaries.blue.Z = startingVal++; - primaries.white.X = startingVal++; - primaries.white.Y = startingVal++; - primaries.white.Z = startingVal++; -} - -void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries( - const ui::DisplayPrimaries& primaries) { - float startingVal = mStartingTestValue; - EXPECT_EQ(primaries.red.X, startingVal++); - EXPECT_EQ(primaries.red.Y, startingVal++); - EXPECT_EQ(primaries.red.Z, startingVal++); - EXPECT_EQ(primaries.green.X, startingVal++); - EXPECT_EQ(primaries.green.Y, startingVal++); - EXPECT_EQ(primaries.green.Z, startingVal++); - EXPECT_EQ(primaries.blue.X, startingVal++); - EXPECT_EQ(primaries.blue.Y, startingVal++); - EXPECT_EQ(primaries.blue.Z, startingVal++); - EXPECT_EQ(primaries.white.X, startingVal++); - EXPECT_EQ(primaries.white.Y, startingVal++); - EXPECT_EQ(primaries.white.Z, startingVal++); -} - -TEST_F(GetDisplayNativePrimaries, nullDisplayToken) { - ui::DisplayPrimaries primaries; - EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries)); -} - -TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) { - auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this); - injector.inject(); - auto internalDisplayToken = injector.token(); - - ui::DisplayPrimaries expectedPrimaries; - populateDummyDisplayNativePrimaries(expectedPrimaries); - mFlinger.setInternalDisplayPrimaries(expectedPrimaries); - - ui::DisplayPrimaries primaries; - EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries)); - - checkDummyDisplayNativePrimaries(primaries); -} - -TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) { - sp<BBinder> notInternalDisplayToken = new BBinder(); - - ui::DisplayPrimaries primaries; - populateDummyDisplayNativePrimaries(primaries); - EXPECT_EQ(NAME_NOT_FOUND, - mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries)); - - // Check primaries argument wasn't modified in case of failure - checkDummyDisplayNativePrimaries(primaries); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::setupNewDisplayDeviceInternal - */ - -class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest { -public: - template <typename T> - void setupNewDisplayDeviceInternalTest(); -}; - -template <typename Case> -void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { - const sp<BBinder> displayToken = new BBinder(); - const sp<compositionengine::mock::DisplaySurface> displaySurface = - new compositionengine::mock::DisplaySurface(); - const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer(); - - // -------------------------------------------------------------------- - // Preconditions - - // Wide color displays support is configured appropriately - Case::WideColorSupport::injectConfigChange(this); - - // The display is setup with the HWC. - Case::Display::injectHwcDisplay(this); - - // SurfaceFlinger will use a test-controlled factory for native window - // surfaces. - injectFakeNativeWindowSurfaceFactory(); - - // A compositionengine::Display has already been created - auto compositionDisplay = Case::Display::injectCompositionDisplay(this); - - // -------------------------------------------------------------------- - // Call Expectations - - // Various native window calls will be made. - Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this); - Case::Display::setupHwcGetActiveConfigCallExpectations(this); - Case::WideColorSupport::setupComposerCallExpectations(this); - Case::HdrSupport::setupComposerCallExpectations(this); - Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); - - // -------------------------------------------------------------------- - // Invocation - - DisplayDeviceState state; - if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) { - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; - ASSERT_TRUE(hwcDisplayId); - state.physical = {*displayId, *connectionType, *hwcDisplayId}; - } - - state.isSecure = static_cast<bool>(Case::Display::SECURE); - - auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state, - displaySurface, producer); - - // -------------------------------------------------------------------- - // Postconditions - - ASSERT_TRUE(device != nullptr); - EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId()); - EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType()); - EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual()); - EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure()); - EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary()); - EXPECT_EQ(Case::Display::WIDTH, device->getWidth()); - EXPECT_EQ(Case::Display::HEIGHT, device->getHeight()); - EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut()); - EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport()); - EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support()); - EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport()); - EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport()); - // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are - // remapped, and the test only ever sets up one config. If there were an error - // looking up the remapped index, device->getActiveConfig() would be -1 instead. - EXPECT_EQ(0, device->getActiveConfig().value()); - EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS, - device->getSupportedPerFrameMetadata()); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) { - setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) { - setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) { - setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) { - setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) { - setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) { - setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) { - setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) { - setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) { - setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) { - setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>(); -} - -TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) { - setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>(); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::handleTransactionLocked(eDisplayTransactionNeeded) - */ - -class HandleTransactionLockedTest : public DisplayTransactionTest { -public: - template <typename Case> - void setupCommonPreconditions(); - - template <typename Case, bool connected> - static void expectHotplugReceived(mock::EventThread*); - - template <typename Case> - void setupCommonCallExpectationsForConnectProcessing(); - - template <typename Case> - void setupCommonCallExpectationsForDisconnectProcessing(); - - template <typename Case> - void processesHotplugConnectCommon(); - - template <typename Case> - void ignoresHotplugConnectCommon(); - - template <typename Case> - void processesHotplugDisconnectCommon(); - - template <typename Case> - void verifyDisplayIsConnected(const sp<IBinder>& displayToken); - - template <typename Case> - void verifyPhysicalDisplayIsConnected(); - - void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken); -}; - -template <typename Case> -void HandleTransactionLockedTest::setupCommonPreconditions() { - // Wide color displays support is configured appropriately - Case::WideColorSupport::injectConfigChange(this); - - // SurfaceFlinger will use a test-controlled factory for BufferQueues - injectFakeBufferQueueFactory(); - - // SurfaceFlinger will use a test-controlled factory for native window - // surfaces. - injectFakeNativeWindowSurfaceFactory(); -} - -template <typename Case, bool connected> -void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) { - const auto convert = [](auto physicalDisplayId) { - return std::make_optional(DisplayId{physicalDisplayId}); - }; - - EXPECT_CALL(*eventThread, - onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected)) - .Times(1); -} - -template <typename Case> -void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() { - Case::Display::setupHwcHotplugCallExpectations(this); - - Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this); - Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this); - Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this); - Case::Display::setupHwcGetActiveConfigCallExpectations(this); - - Case::WideColorSupport::setupComposerCallExpectations(this); - Case::HdrSupport::setupComposerCallExpectations(this); - Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); - - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - expectHotplugReceived<Case, true>(mEventThread); - expectHotplugReceived<Case, true>(mSFEventThread); -} - -template <typename Case> -void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() { - EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); - - expectHotplugReceived<Case, false>(mEventThread); - expectHotplugReceived<Case, false>(mSFEventThread); -} - -template <typename Case> -void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) { - // The display device should have been set up in the list of displays. - ASSERT_TRUE(hasDisplayDevice(displayToken)); - const auto& device = getDisplayDevice(displayToken); - EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure()); - EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary()); - - std::optional<DisplayDeviceState::Physical> expectedPhysical; - if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) { - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; - ASSERT_TRUE(hwcDisplayId); - expectedPhysical = {*displayId, *connectionType, *hwcDisplayId}; - } - - // The display should have been set up in the current display state - ASSERT_TRUE(hasCurrentDisplayState(displayToken)); - const auto& current = getCurrentDisplayState(displayToken); - EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual()); - EXPECT_EQ(expectedPhysical, current.physical); - - // The display should have been set up in the drawing display state - ASSERT_TRUE(hasDrawingDisplayState(displayToken)); - const auto& draw = getDrawingDisplayState(displayToken); - EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual()); - EXPECT_EQ(expectedPhysical, draw.physical); -} - -template <typename Case> -void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() { - // HWComposer should have an entry for the display - EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); - - // SF should have a display token. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1); - auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[*displayId]; - - verifyDisplayIsConnected<Case>(displayToken); -} - -void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) { - EXPECT_FALSE(hasDisplayDevice(displayToken)); - EXPECT_FALSE(hasCurrentDisplayState(displayToken)); - EXPECT_FALSE(hasDrawingDisplayState(displayToken)); -} - -template <typename Case> -void HandleTransactionLockedTest::processesHotplugConnectCommon() { - // -------------------------------------------------------------------- - // Preconditions - - setupCommonPreconditions<Case>(); - - // A hotplug connect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); - - // -------------------------------------------------------------------- - // Call Expectations - - EXPECT_CALL(*mComposer, isUsingVrComposer()).WillOnce(Return(false)); - - setupCommonCallExpectationsForConnectProcessing<Case>(); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - verifyPhysicalDisplayIsConnected<Case>(); - - // -------------------------------------------------------------------- - // Cleanup conditions - - EXPECT_CALL(*mComposer, - setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) - .WillOnce(Return(Error::NONE)); - EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); -} - -template <typename Case> -void HandleTransactionLockedTest::ignoresHotplugConnectCommon() { - // -------------------------------------------------------------------- - // Preconditions - - setupCommonPreconditions<Case>(); - - // A hotplug connect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - // HWComposer should not have an entry for the display - EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); -} - -template <typename Case> -void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { - // -------------------------------------------------------------------- - // Preconditions - - setupCommonPreconditions<Case>(); - - // A hotplug disconnect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); - - // The display is already completely set up. - Case::Display::injectHwcDisplay(this); - auto existing = Case::Display::makeFakeExistingDisplayInjector(this); - existing.inject(); - - // -------------------------------------------------------------------- - // Call Expectations - - EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false)); - EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _)) - .Times(0); - - setupCommonCallExpectationsForDisconnectProcessing<Case>(); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - // HWComposer should not have an entry for the display - EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); - - // SF should not have a display token. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0); - - // The existing token should have been removed - verifyDisplayIsNotConnected(existing.token()); -} - -TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) { - processesHotplugConnectCommon<SimplePrimaryDisplayCase>(); -} - -TEST_F(HandleTransactionLockedTest, - processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) { - // Inject an external display. - ExternalDisplayVariant::injectHwcDisplay(this); - - processesHotplugConnectCommon<SimplePrimaryDisplayCase>(); -} - -TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) { - // Inject a primary display. - PrimaryDisplayVariant::injectHwcDisplay(this); - - processesHotplugConnectCommon<SimpleExternalDisplayCase>(); -} - -TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) { - // Inject both a primary and external display. - PrimaryDisplayVariant::injectHwcDisplay(this); - ExternalDisplayVariant::injectHwcDisplay(this); - - // TODO: This is an unnecessary call. - EXPECT_CALL(*mComposer, - getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT), - SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()), - Return(Error::NONE))); - - EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false)); - - ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>(); -} - -TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfExternalForVrComposer) { - // Inject a primary display. - PrimaryDisplayVariant::injectHwcDisplay(this); - - EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(true)); - - ignoresHotplugConnectCommon<SimpleExternalDisplayCase>(); -} - -TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) { - processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(); -} - -TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) { - processesHotplugDisconnectCommon<SimpleExternalDisplayCase>(); -} - -TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - setupCommonPreconditions<Case>(); - - // A hotplug connect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); - // A hotplug disconnect event is also enqueued for the same display - Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); - - // -------------------------------------------------------------------- - // Call Expectations - - EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false)); - - setupCommonCallExpectationsForConnectProcessing<Case>(); - setupCommonCallExpectationsForDisconnectProcessing<Case>(); - - EXPECT_CALL(*mComposer, - setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) - .WillOnce(Return(Error::NONE)); - EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - // HWComposer should not have an entry for the display - EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); - - // SF should not have a display token. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 0); -} - -TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - setupCommonPreconditions<Case>(); - - // The display is already completely set up. - Case::Display::injectHwcDisplay(this); - auto existing = Case::Display::makeFakeExistingDisplayInjector(this); - existing.inject(); - - // A hotplug disconnect event is enqueued for a display - Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); - // A hotplug connect event is also enqueued for the same display - Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); - - // -------------------------------------------------------------------- - // Call Expectations - - EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false)); - - setupCommonCallExpectationsForConnectProcessing<Case>(); - setupCommonCallExpectationsForDisconnectProcessing<Case>(); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - // The existing token should have been removed - verifyDisplayIsNotConnected(existing.token()); - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(*displayId) == 1); - EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[*displayId]); - - // A new display should be connected in its place - - verifyPhysicalDisplayIsConnected<Case>(); - - // -------------------------------------------------------------------- - // Cleanup conditions - - EXPECT_CALL(*mComposer, - setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) - .WillOnce(Return(Error::NONE)); - EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); -} - -TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { - using Case = HwcVirtualDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // The HWC supports at least one virtual display - injectMockComposer(1); - - setupCommonPreconditions<Case>(); - - // A virtual display was added to the current state, and it has a - // surface(producer) - sp<BBinder> displayToken = new BBinder(); - - DisplayDeviceState state; - state.isSecure = static_cast<bool>(Case::Display::SECURE); - - sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()}; - state.surface = surface; - mFlinger.mutableCurrentState().displays.add(displayToken, state); - - // -------------------------------------------------------------------- - // Call Expectations - - Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this); - Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this); - - EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR))); - EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR))); - EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT), - Return(NO_ERROR))); - EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); - - EXPECT_CALL(*surface, setAsyncMode(true)).Times(1); - - EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1); - EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1); - - Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this); - Case::WideColorSupport::setupComposerCallExpectations(this); - Case::HdrSupport::setupComposerCallExpectations(this); - Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - // The display device should have been set up in the list of displays. - verifyDisplayIsConnected<Case>(displayToken); - - // -------------------------------------------------------------------- - // Cleanup conditions - - EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID)) - .WillOnce(Return(Error::NONE)); - EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); - - // Cleanup - mFlinger.mutableCurrentState().displays.removeItem(displayToken); - mFlinger.mutableDrawingState().displays.removeItem(displayToken); -} - -TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { - using Case = HwcVirtualDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // The HWC supports at least one virtual display - injectMockComposer(1); - - setupCommonPreconditions<Case>(); - - // A virtual display was added to the current state, but it does not have a - // surface. - sp<BBinder> displayToken = new BBinder(); - - DisplayDeviceState state; - state.isSecure = static_cast<bool>(Case::Display::SECURE); - - mFlinger.mutableCurrentState().displays.add(displayToken, state); - - // -------------------------------------------------------------------- - // Call Expectations - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - // There will not be a display device set up. - EXPECT_FALSE(hasDisplayDevice(displayToken)); - - // The drawing display state will be set from the current display state. - ASSERT_TRUE(hasDrawingDisplayState(displayToken)); - const auto& draw = getDrawingDisplayState(displayToken); - EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual()); -} - -TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) { - using Case = HwcVirtualDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A virtual display is set up but is removed from the current state. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - mFlinger.mutableHwcDisplayData().try_emplace(*displayId); - Case::Display::injectHwcDisplay(this); - auto existing = Case::Display::makeFakeExistingDisplayInjector(this); - existing.inject(); - mFlinger.mutableCurrentState().displays.removeItem(existing.token()); - - // -------------------------------------------------------------------- - // Call Expectations - - EXPECT_CALL(*mComposer, isUsingVrComposer()).WillRepeatedly(Return(false)); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - // The existing token should have been removed - verifyDisplayIsNotConnected(existing.token()); -} - -TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) { - using Case = NonHwcVirtualDisplayCase; - - constexpr uint32_t oldLayerStack = 0u; - constexpr uint32_t newLayerStack = 123u; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // There is a change to the layerStack state - display.mutableDrawingDisplayState().layerStack = oldLayerStack; - display.mutableCurrentDisplayState().layerStack = newLayerStack; - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack()); -} - -TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) { - using Case = NonHwcVirtualDisplayCase; - - constexpr ui::Rotation oldTransform = ui::ROTATION_0; - constexpr ui::Rotation newTransform = ui::ROTATION_180; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // There is a change to the orientation state - display.mutableDrawingDisplayState().orientation = oldTransform; - display.mutableCurrentDisplayState().orientation = newTransform; - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation()); -} - -TEST_F(HandleTransactionLockedTest, processesDisplayViewportChanges) { - using Case = NonHwcVirtualDisplayCase; - - const Rect oldViewport(0, 0, 0, 0); - const Rect newViewport(0, 0, 123, 456); - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // There is a change to the viewport state - display.mutableDrawingDisplayState().viewport = oldViewport; - display.mutableCurrentDisplayState().viewport = newViewport; - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - EXPECT_EQ(newViewport, display.mutableDisplayDevice()->getViewport()); -} - -TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) { - using Case = NonHwcVirtualDisplayCase; - - const Rect oldFrame(0, 0, 0, 0); - const Rect newFrame(0, 0, 123, 456); - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // There is a change to the viewport state - display.mutableDrawingDisplayState().frame = oldFrame; - display.mutableCurrentDisplayState().frame = newFrame; - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); - - // -------------------------------------------------------------------- - // Postconditions - - EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getFrame()); -} - -TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { - using Case = NonHwcVirtualDisplayCase; - - constexpr int oldWidth = 0; - constexpr int oldHeight = 10; - constexpr int newWidth = 123; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto nativeWindow = new mock::NativeWindow(); - auto displaySurface = new compositionengine::mock::DisplaySurface(); - sp<GraphicBuffer> buf = new GraphicBuffer(); - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.setNativeWindow(nativeWindow); - display.setDisplaySurface(displaySurface); - // Setup injection expections - EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _)) - .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0))); - EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) - .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0))); - EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1); - EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1); - EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1); - EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1); - display.inject(); - - // There is a change to the viewport state - display.mutableDrawingDisplayState().width = oldWidth; - display.mutableDrawingDisplayState().height = oldHeight; - display.mutableCurrentDisplayState().width = newWidth; - display.mutableCurrentDisplayState().height = oldHeight; - - // -------------------------------------------------------------------- - // Call Expectations - - EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); -} - -TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { - using Case = NonHwcVirtualDisplayCase; - - constexpr int oldWidth = 0; - constexpr int oldHeight = 10; - constexpr int newHeight = 123; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto nativeWindow = new mock::NativeWindow(); - auto displaySurface = new compositionengine::mock::DisplaySurface(); - sp<GraphicBuffer> buf = new GraphicBuffer(); - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.setNativeWindow(nativeWindow); - display.setDisplaySurface(displaySurface); - // Setup injection expections - EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _)) - .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0))); - EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) - .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0))); - EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1); - EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1); - EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1); - EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1); - display.inject(); - - // There is a change to the viewport state - display.mutableDrawingDisplayState().width = oldWidth; - display.mutableDrawingDisplayState().height = oldHeight; - display.mutableCurrentDisplayState().width = oldWidth; - display.mutableCurrentDisplayState().height = newHeight; - - // -------------------------------------------------------------------- - // Call Expectations - - EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::setDisplayStateLocked - */ - -TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) { - // -------------------------------------------------------------------- - // Preconditions - - // We have an unknown display token not associated with a known display - sp<BBinder> displayToken = new BBinder(); - - // The requested display state references the unknown display. - DisplayState state; - state.what = DisplayState::eLayerStackChanged; - state.token = displayToken; - state.layerStack = 456; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags are empty - EXPECT_EQ(0u, flags); - - // The display token still doesn't match anything known. - EXPECT_FALSE(hasCurrentDisplayState(displayToken)); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingWhenNoChanges) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is already set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // No changes are made to the display - DisplayState state; - state.what = 0; - state.token = display.token(); - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags are empty - EXPECT_EQ(0u, flags); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is already set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // There is a surface that can be set. - sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer(); - - // The current display state has the surface set - display.mutableCurrentDisplayState().surface = surface; - - // The incoming request sets the same surface - DisplayState state; - state.what = DisplayState::eSurfaceChanged; - state.token = display.token(); - state.surface = surface; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags are empty - EXPECT_EQ(0u, flags); - - // The current display state is unchanged. - EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get()); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is already set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // There is a surface that can be set. - sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer(); - - // The current display state does not have a surface - display.mutableCurrentDisplayState().surface = nullptr; - - // The incoming request sets a surface - DisplayState state; - state.what = DisplayState::eSurfaceChanged; - state.token = display.token(); - state.surface = surface; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags indicate a transaction is needed - EXPECT_EQ(eDisplayTransactionNeeded, flags); - - // The current display layer stack state is set to the new value - EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get()); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is already set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The display has a layer stack set - display.mutableCurrentDisplayState().layerStack = 456u; - - // The incoming request sets the same layer stack - DisplayState state; - state.what = DisplayState::eLayerStackChanged; - state.token = display.token(); - state.layerStack = 456u; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags are empty - EXPECT_EQ(0u, flags); - - // The current display state is unchanged - EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The display has a layer stack set - display.mutableCurrentDisplayState().layerStack = 654u; - - // The incoming request sets a different layer stack - DisplayState state; - state.what = DisplayState::eLayerStackChanged; - state.token = display.token(); - state.layerStack = 456u; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags indicate a transaction is needed - EXPECT_EQ(eDisplayTransactionNeeded, flags); - - // The desired display state has been set to the new value. - EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) { - using Case = SimplePrimaryDisplayCase; - constexpr ui::Rotation initialOrientation = ui::ROTATION_180; - const Rect initialFrame = {1, 2, 3, 4}; - const Rect initialViewport = {5, 6, 7, 8}; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The current display state projection state is all set - display.mutableCurrentDisplayState().orientation = initialOrientation; - display.mutableCurrentDisplayState().frame = initialFrame; - display.mutableCurrentDisplayState().viewport = initialViewport; - - // The incoming request sets the same projection state - DisplayState state; - state.what = DisplayState::eDisplayProjectionChanged; - state.token = display.token(); - state.orientation = initialOrientation; - state.frame = initialFrame; - state.viewport = initialViewport; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags are empty - EXPECT_EQ(0u, flags); - - // The current display state is unchanged - EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation); - - EXPECT_EQ(initialFrame, display.getCurrentDisplayState().frame); - EXPECT_EQ(initialViewport, display.getCurrentDisplayState().viewport); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) { - using Case = SimplePrimaryDisplayCase; - constexpr ui::Rotation initialOrientation = ui::ROTATION_90; - constexpr ui::Rotation desiredOrientation = ui::ROTATION_180; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The current display state has an orientation set - display.mutableCurrentDisplayState().orientation = initialOrientation; - - // The incoming request sets a different orientation - DisplayState state; - state.what = DisplayState::eDisplayProjectionChanged; - state.token = display.token(); - state.orientation = desiredOrientation; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags indicate a transaction is needed - EXPECT_EQ(eDisplayTransactionNeeded, flags); - - // The current display state has the new value. - EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) { - using Case = SimplePrimaryDisplayCase; - const Rect initialFrame = {0, 0, 0, 0}; - const Rect desiredFrame = {5, 6, 7, 8}; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The current display state does not have a frame - display.mutableCurrentDisplayState().frame = initialFrame; - - // The incoming request sets a frame - DisplayState state; - state.what = DisplayState::eDisplayProjectionChanged; - state.token = display.token(); - state.frame = desiredFrame; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags indicate a transaction is needed - EXPECT_EQ(eDisplayTransactionNeeded, flags); - - // The current display state has the new value. - EXPECT_EQ(desiredFrame, display.getCurrentDisplayState().frame); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfViewportChanged) { - using Case = SimplePrimaryDisplayCase; - const Rect initialViewport = {0, 0, 0, 0}; - const Rect desiredViewport = {5, 6, 7, 8}; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The current display state does not have a viewport - display.mutableCurrentDisplayState().viewport = initialViewport; - - // The incoming request sets a viewport - DisplayState state; - state.what = DisplayState::eDisplayProjectionChanged; - state.token = display.token(); - state.viewport = desiredViewport; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags indicate a transaction is needed - EXPECT_EQ(eDisplayTransactionNeeded, flags); - - // The current display state has the new value. - EXPECT_EQ(desiredViewport, display.getCurrentDisplayState().viewport); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) { - using Case = SimplePrimaryDisplayCase; - constexpr uint32_t initialWidth = 1024; - constexpr uint32_t initialHeight = 768; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The current display state has a size set - display.mutableCurrentDisplayState().width = initialWidth; - display.mutableCurrentDisplayState().height = initialHeight; - - // The incoming request sets the same display size - DisplayState state; - state.what = DisplayState::eDisplaySizeChanged; - state.token = display.token(); - state.width = initialWidth; - state.height = initialHeight; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags are empty - EXPECT_EQ(0u, flags); - - // The current display state is unchanged - EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width); - EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) { - using Case = SimplePrimaryDisplayCase; - constexpr uint32_t initialWidth = 0; - constexpr uint32_t desiredWidth = 1024; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The display does not yet have a width - display.mutableCurrentDisplayState().width = initialWidth; - - // The incoming request sets a display width - DisplayState state; - state.what = DisplayState::eDisplaySizeChanged; - state.token = display.token(); - state.width = desiredWidth; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags indicate a transaction is needed - EXPECT_EQ(eDisplayTransactionNeeded, flags); - - // The current display state has the new value. - EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width); -} - -TEST_F(DisplayTransactionTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) { - using Case = SimplePrimaryDisplayCase; - constexpr uint32_t initialHeight = 0; - constexpr uint32_t desiredHeight = 768; - - // -------------------------------------------------------------------- - // Preconditions - - // A display is set up - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The display does not yet have a height - display.mutableCurrentDisplayState().height = initialHeight; - - // The incoming request sets a display height - DisplayState state; - state.what = DisplayState::eDisplaySizeChanged; - state.token = display.token(); - state.height = desiredHeight; - - // -------------------------------------------------------------------- - // Invocation - - uint32_t flags = mFlinger.setDisplayStateLocked(state); - - // -------------------------------------------------------------------- - // Postconditions - - // The returned flags indicate a transaction is needed - EXPECT_EQ(eDisplayTransactionNeeded, flags); - - // The current display state has the new value. - EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::onInitializeDisplays - */ - -TEST_F(DisplayTransactionTest, onInitializeDisplaysSetsUpPrimaryDisplay) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A primary display is set up - Case::Display::injectHwcDisplay(this); - auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this); - primaryDisplay.inject(); - - // -------------------------------------------------------------------- - // Call Expectations - - // We expect the surface interceptor to possibly be used, but we treat it as - // disabled since it is called as a side effect rather than directly by this - // function. - EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false)); - - // We expect a call to get the active display config. - Case::Display::setupHwcGetActiveConfigCallExpectations(this); - - // We expect invalidate() to be invoked once to trigger display transaction - // processing. - EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); - - EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0)); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.onInitializeDisplays(); - - // -------------------------------------------------------------------- - // Postconditions - - // The primary display should have a current state - ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token())); - const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token()); - // The layer stack state should be set to zero - EXPECT_EQ(0u, primaryDisplayState.layerStack); - // The orientation state should be set to zero - EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation); - - // The frame state should be set to INVALID - EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.frame); - - // The viewport state should be set to INVALID - EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.viewport); - - // The width and height should both be zero - EXPECT_EQ(0u, primaryDisplayState.width); - EXPECT_EQ(0u, primaryDisplayState.height); - - // The display should be set to PowerMode::ON - ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token())); - auto displayDevice = primaryDisplay.mutableDisplayDevice(); - EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode()); - - // The display refresh period should be set in the frame tracker. - FrameStats stats; - mFlinger.getAnimFrameTracker().getStats(&stats); - EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano); - - // The display transaction needed flag should be set. - EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); - - // The compositor timing should be set to default values - const auto& compositorTiming = mFlinger.getCompositorTiming(); - EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline); - EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval); - EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency); -} - -/* ------------------------------------------------------------------------ - * SurfaceFlinger::setPowerModeInternal - */ - -// Used when we simulate a display that supports doze. -template <typename Display> -struct DozeIsSupportedVariant { - static constexpr bool DOZE_SUPPORTED = true; - static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE = - IComposerClient::PowerMode::DOZE; - static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND = - IComposerClient::PowerMode::DOZE_SUSPEND; - - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>( - std::vector<DisplayCapability>({DisplayCapability::DOZE})), - Return(Error::NONE))); - } -}; - -template <typename Display> -// Used when we simulate a display that does not support doze. -struct DozeNotSupportedVariant { - static constexpr bool DOZE_SUPPORTED = false; - static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE = - IComposerClient::PowerMode::ON; - static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND = - IComposerClient::PowerMode::ON; - - static void setupComposerCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), - Return(Error::NONE))); - } -}; - -struct EventThreadBaseSupportedVariant { - static void setupEventAndEventControlThreadNoCallExpectations(DisplayTransactionTest* test) { - // The event control thread should not be notified. - EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(_)).Times(0); - - // The event thread should not be notified. - EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0); - EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0); - } -}; - -struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant { - static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) { - // These calls are only expected for the primary display. - - // Instead expect no calls. - setupEventAndEventControlThreadNoCallExpectations(test); - } - - static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) { - // These calls are only expected for the primary display. - - // Instead expect no calls. - setupEventAndEventControlThreadNoCallExpectations(test); - } -}; - -struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { - static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) { - // The event control thread should be notified to enable vsyncs - EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(true)).Times(1); - - // The event thread should be notified that the screen was acquired. - EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1); - } - - static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) { - // There should be a call to setVsyncEnabled(false) - EXPECT_CALL(*test->mEventControlThread, setVsyncEnabled(false)).Times(1); - - // The event thread should not be notified that the screen was released. - EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1); - } -}; - -struct DispSyncIsSupportedVariant { - static void setupBeginResyncCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mPrimaryDispSync, setPeriod(DEFAULT_REFRESH_RATE)).Times(1); - EXPECT_CALL(*test->mPrimaryDispSync, beginResync()).Times(1); - } - - static void setupEndResyncCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mPrimaryDispSync, endResync()).Times(1); - } -}; - -struct DispSyncNotSupportedVariant { - static void setupBeginResyncCallExpectations(DisplayTransactionTest* /* test */) {} - - static void setupEndResyncCallExpectations(DisplayTransactionTest* /* test */) {} -}; - -// -------------------------------------------------------------------- -// Note: -// -// There are a large number of transitions we could test, however we only test a -// selected subset which provides complete test coverage of the implementation. -// -------------------------------------------------------------------- - -template <PowerMode initialPowerMode, PowerMode targetPowerMode> -struct TransitionVariantCommon { - static constexpr auto INITIAL_POWER_MODE = initialPowerMode; - static constexpr auto TARGET_POWER_MODE = targetPowerMode; - - static void verifyPostconditions(DisplayTransactionTest*) {} -}; - -struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); - Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); - Case::DispSync::setupBeginResyncCallExpectations(test); - Case::setupRepaintEverythingCallExpectations(test); - } - - static void verifyPostconditions(DisplayTransactionTest* test) { - EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); - EXPECT_TRUE(test->mFlinger.getHasPoweredOff()); - } -}; - -struct TransitionOffToDozeSuspendVariant - : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); - Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); - Case::setupRepaintEverythingCallExpectations(test); - } - - static void verifyPostconditions(DisplayTransactionTest* test) { - EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); - EXPECT_TRUE(test->mFlinger.getHasPoweredOff()); - } -}; - -struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); - Case::DispSync::setupEndResyncCallExpectations(test); - Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); - } - - static void verifyPostconditions(DisplayTransactionTest* test) { - EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); - } -}; - -struct TransitionDozeSuspendToOffVariant - : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); - Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); - } - - static void verifyPostconditions(DisplayTransactionTest* test) { - EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); - } -}; - -struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); - Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); - } -}; - -struct TransitionDozeSuspendToDozeVariant - : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); - Case::DispSync::setupBeginResyncCallExpectations(test); - Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); - } -}; - -struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); - Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); - } -}; - -struct TransitionDozeSuspendToOnVariant - : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); - Case::DispSync::setupBeginResyncCallExpectations(test); - Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); - } -}; - -struct TransitionOnToDozeSuspendVariant - : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); - Case::DispSync::setupEndResyncCallExpectations(test); - Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); - } -}; - -struct TransitionOnToUnknownVariant - : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> { - template <typename Case> - static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupEventAndEventControlThreadNoCallExpectations(test); - Case::setupNoComposerPowerModeCallExpectations(test); - } -}; - -// -------------------------------------------------------------------- -// Note: -// -// Rather than testing the cartesian product of of -// DozeIsSupported/DozeNotSupported with all other options, we use one for one -// display type, and the other for another display type. -// -------------------------------------------------------------------- - -template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant, - typename DispSyncVariant, typename TransitionVariant> -struct DisplayPowerCase { - using Display = DisplayVariant; - using Doze = DozeVariant; - using EventThread = EventThreadVariant; - using DispSync = DispSyncVariant; - using Transition = TransitionVariant; - - static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) { - Display::injectHwcDisplayWithNoDefaultCapabilities(test); - auto display = Display::makeFakeExistingDisplayInjector(test); - display.inject(); - display.mutableDisplayDevice()->setPowerMode(mode); - return display; - } - - static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) { - test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled; - } - - static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1); - } - - static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test, - PowerMode mode) { - EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true)); - EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode))) - .Times(1); - } - - static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) { - // Any calls to get the active config will return a default value. - EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID), - Return(Error::NONE))); - - // Any calls to get whether the display supports dozing will return the value set by the - // policy variant. - EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _)) - .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE))); - - EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1); - } - - static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0); - } -}; - -// A sample configuration for the primary display. -// In addition to having event thread support, we emulate doze support. -template <typename TransitionVariant> -using PrimaryDisplayPowerCase = - DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>, - EventThreadIsSupportedVariant, DispSyncIsSupportedVariant, - TransitionVariant>; - -// A sample configuration for the external display. -// In addition to not having event thread support, we emulate not having doze -// support. -template <typename TransitionVariant> -using ExternalDisplayPowerCase = - DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>, - EventThreadNotSupportedVariant, DispSyncNotSupportedVariant, - TransitionVariant>; - -class SetPowerModeInternalTest : public DisplayTransactionTest { -public: - template <typename Case> - void transitionDisplayCommon(); -}; - -template <PowerMode PowerMode> -struct PowerModeInitialVSyncEnabled : public std::false_type {}; - -template <> -struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {}; - -template <> -struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {}; - -template <typename Case> -void SetPowerModeInternalTest::transitionDisplayCommon() { - // -------------------------------------------------------------------- - // Preconditions - - Case::Doze::setupComposerCallExpectations(this); - auto display = - Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE); - Case::setInitialPrimaryHWVsyncEnabled(this, - PowerModeInitialVSyncEnabled< - Case::Transition::INITIAL_POWER_MODE>::value); - - // -------------------------------------------------------------------- - // Call Expectations - - Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE); - Case::Transition::template setupCallExpectations<Case>(this); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), - Case::Transition::TARGET_POWER_MODE); - - // -------------------------------------------------------------------- - // Postconditions - - Case::Transition::verifyPostconditions(this); -} - -TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { - using Case = SimplePrimaryDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // A primary display device is set up - Case::Display::injectHwcDisplay(this); - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The display is already set to PowerMode::ON - display.mutableDisplayDevice()->setPowerMode(PowerMode::ON); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON); - - // -------------------------------------------------------------------- - // Postconditions - - EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); -} - -TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) { - using Case = HwcVirtualDisplayCase; - - // -------------------------------------------------------------------- - // Preconditions - - // Insert display data so that the HWC thinks it created the virtual display. - const auto displayId = Case::Display::DISPLAY_ID::get(); - ASSERT_TRUE(displayId); - mFlinger.mutableHwcDisplayData().try_emplace(*displayId); - - // A virtual display device is set up - Case::Display::injectHwcDisplay(this); - auto display = Case::Display::makeFakeExistingDisplayInjector(this); - display.inject(); - - // The display is set to PowerMode::ON - getDisplayDevice(display.token())->setPowerMode(PowerMode::ON); - - // -------------------------------------------------------------------- - // Invocation - - mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF); - - // -------------------------------------------------------------------- - // Postconditions - - EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) { - transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); -} - -TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) { - transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>(); -} - -} // namespace } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h new file mode 100644 index 0000000000..01cdb2896f --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -0,0 +1,756 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + +#include <type_traits> +#include "DisplayIdentificationTest.h" + +#include <binder/IPCThreadState.h> +#include <compositionengine/Display.h> +#include <compositionengine/DisplayColorProfile.h> +#include <compositionengine/impl/Display.h> +#include <compositionengine/impl/OutputCompositionState.h> +#include <compositionengine/mock/Display.h> +#include <compositionengine/mock/DisplayColorProfile.h> +#include <compositionengine/mock/DisplaySurface.h> +#include <compositionengine/mock/RenderSurface.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <gui/mock/GraphicBufferConsumer.h> +#include <gui/mock/GraphicBufferProducer.h> +#include <log/log.h> +#include <private/android_filesystem_config.h> +#include <renderengine/mock/RenderEngine.h> +#include <ui/DebugUtils.h> + +#include "TestableScheduler.h" +#include "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockComposer.h" +#include "mock/DisplayHardware/MockPowerAdvisor.h" +#include "mock/MockEventThread.h" +#include "mock/MockMessageQueue.h" +#include "mock/MockNativeWindowSurface.h" +#include "mock/MockSchedulerCallback.h" +#include "mock/MockSurfaceInterceptor.h" +#include "mock/MockVsyncController.h" +#include "mock/system/window/MockNativeWindow.h" + +namespace android { + +// TODO: Do not polute the android namespace +namespace hal = android::hardware::graphics::composer::hal; + +using testing::_; +using testing::AnyNumber; +using testing::DoAll; +using testing::Mock; +using testing::ResultOf; +using testing::Return; +using testing::SetArgPointee; + +using hal::ColorMode; +using hal::Connection; +using hal::DisplayCapability; +using hal::DisplayType; +using hal::Error; +using hal::Hdr; +using hal::HWDisplayId; +using hal::IComposer; +using hal::IComposerClient; +using hal::PerFrameMetadataKey; +using hal::PowerMode; + +class DisplayTransactionTest : public testing::Test { +public: + ~DisplayTransactionTest() override; + + // -------------------------------------------------------------------- + // Mock/Fake injection + + void injectMockScheduler(); + void injectMockComposer(int virtualDisplayCount); + void injectFakeBufferQueueFactory(); + void injectFakeNativeWindowSurfaceFactory(); + sp<DisplayDevice> injectDefaultInternalDisplay( + std::function<void(TestableSurfaceFlinger::FakeDisplayDeviceInjector&)>); + + // -------------------------------------------------------------------- + // Postcondition helpers + + bool hasPhysicalHwcDisplay(hal::HWDisplayId hwcDisplayId); + bool hasTransactionFlagSet(int flag); + bool hasDisplayDevice(sp<IBinder> displayToken); + sp<DisplayDevice> getDisplayDevice(sp<IBinder> displayToken); + bool hasCurrentDisplayState(sp<IBinder> displayToken); + const DisplayDeviceState& getCurrentDisplayState(sp<IBinder> displayToken); + bool hasDrawingDisplayState(sp<IBinder> displayToken); + const DisplayDeviceState& getDrawingDisplayState(sp<IBinder> displayToken); + + // -------------------------------------------------------------------- + // Test instances + + TestableSurfaceFlinger mFlinger; + sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow(); + sp<GraphicBuffer> mBuffer = new GraphicBuffer(); + Hwc2::mock::PowerAdvisor mPowerAdvisor; + + // These mocks are created by the test, but are destroyed by SurfaceFlinger + // by virtue of being stored into a std::unique_ptr. However we still need + // to keep a reference to them for use in setting up call expectations. + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); + Hwc2::mock::Composer* mComposer = nullptr; + mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); + sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor; + + mock::VsyncController* mVsyncController = new mock::VsyncController; + mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker; + mock::SchedulerCallback mSchedulerCallback; + mock::EventThread* mEventThread = new mock::EventThread; + mock::EventThread* mSFEventThread = new mock::EventThread; + + // These mocks are created only when expected to be created via a factory. + sp<mock::GraphicBufferConsumer> mConsumer; + sp<mock::GraphicBufferProducer> mProducer; + surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr; + +protected: + DisplayTransactionTest(); +}; + +constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'667; +constexpr int32_t DEFAULT_DPI = 320; +constexpr int DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT = HAL_PIXEL_FORMAT_RGB_565; + +constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value + +/* ------------------------------------------------------------------------ + * Boolean avoidance + * + * To make calls and template instantiations more readable, we define some + * local enums along with an implicit bool conversion. + */ + +#define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true }; + +BOOL_SUBSTITUTE(Async); +BOOL_SUBSTITUTE(Critical); +BOOL_SUBSTITUTE(Primary); +BOOL_SUBSTITUTE(Secure); +BOOL_SUBSTITUTE(Virtual); + +template <typename PhysicalDisplay> +struct PhysicalDisplayIdType {}; + +template <uint64_t displayId> +using HalVirtualDisplayIdType = std::integral_constant<uint64_t, displayId>; + +struct GpuVirtualDisplayIdType {}; + +template <typename> +struct IsPhysicalDisplayId : std::bool_constant<false> {}; + +template <typename PhysicalDisplay> +struct IsPhysicalDisplayId<PhysicalDisplayIdType<PhysicalDisplay>> : std::bool_constant<true> {}; + +template <typename> +struct DisplayIdGetter; + +template <typename PhysicalDisplay> +struct DisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> { + static PhysicalDisplayId get() { + if (!PhysicalDisplay::HAS_IDENTIFICATION_DATA) { + return PhysicalDisplayId::fromPort(static_cast<bool>(PhysicalDisplay::PRIMARY) + ? LEGACY_DISPLAY_TYPE_PRIMARY + : LEGACY_DISPLAY_TYPE_EXTERNAL); + } + + const auto info = + parseDisplayIdentificationData(PhysicalDisplay::PORT, + PhysicalDisplay::GET_IDENTIFICATION_DATA()); + return info ? info->id : PhysicalDisplayId::fromPort(PhysicalDisplay::PORT); + } +}; + +template <uint64_t displayId> +struct DisplayIdGetter<HalVirtualDisplayIdType<displayId>> { + static HalVirtualDisplayId get() { return HalVirtualDisplayId(displayId); } +}; + +template <> +struct DisplayIdGetter<GpuVirtualDisplayIdType> { + static GpuVirtualDisplayId get() { return GpuVirtualDisplayId(0); } +}; + +template <typename> +struct DisplayConnectionTypeGetter { + static constexpr std::optional<DisplayConnectionType> value; +}; + +template <typename PhysicalDisplay> +struct DisplayConnectionTypeGetter<PhysicalDisplayIdType<PhysicalDisplay>> { + static constexpr std::optional<DisplayConnectionType> value = PhysicalDisplay::CONNECTION_TYPE; +}; + +template <typename> +struct HwcDisplayIdGetter { + static constexpr std::optional<HWDisplayId> value; +}; + +constexpr HWDisplayId HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID = 1010; + +template <uint64_t displayId> +struct HwcDisplayIdGetter<HalVirtualDisplayIdType<displayId>> { + static constexpr std::optional<HWDisplayId> value = HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID; +}; + +template <typename PhysicalDisplay> +struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> { + static constexpr std::optional<HWDisplayId> value = PhysicalDisplay::HWC_DISPLAY_ID; +}; + +// DisplayIdType can be: +// 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC. +// 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC. +// 3) GpuVirtualDisplayIdType for virtual display without HWC backing. +template <typename DisplayIdType, int width, int height, Critical critical, Async async, + Secure secure, Primary primary, int grallocUsage> +struct DisplayVariant { + using DISPLAY_ID = DisplayIdGetter<DisplayIdType>; + using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>; + using HWC_DISPLAY_ID_OPT = HwcDisplayIdGetter<DisplayIdType>; + + // The display width and height + static constexpr int WIDTH = width; + static constexpr int HEIGHT = height; + + static constexpr int GRALLOC_USAGE = grallocUsage; + + // Whether the display is virtual or physical + static constexpr Virtual VIRTUAL = + IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE; + + // When creating native window surfaces for the framebuffer, whether those should be critical + static constexpr Critical CRITICAL = critical; + + // When creating native window surfaces for the framebuffer, whether those should be async + static constexpr Async ASYNC = async; + + // Whether the display should be treated as secure + static constexpr Secure SECURE = secure; + + // Whether the display is primary + static constexpr Primary PRIMARY = primary; + + static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) { + auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder(); + if (auto displayId = PhysicalDisplayId::tryCast(DISPLAY_ID::get())) { + ceDisplayArgs.setPhysical({*displayId, 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 + // a GpuVirtualDisplayId, even if we are in the HwcVirtualDisplayVariant. + // In this case we later override it by calling display.setDisplayIdForTesting(). + ceDisplayArgs.setUseHwcVirtualDisplays(false); + + GpuVirtualDisplayId desiredDisplayId = GpuVirtualDisplayId::tryCast(DISPLAY_ID::get()) + .value_or(GpuVirtualDisplayId(0)); + + ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId()) + .WillByDefault(Return(desiredDisplayId)); + + auto& generator = test->mFlinger.gpuVirtualDisplayIdGenerator(); + ceDisplayArgs.setGpuVirtualDisplayIdGenerator(generator); + } + ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor); + + auto compositionDisplay = + compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), + ceDisplayArgs.build()); + + if (HalVirtualDisplayId::tryCast(DISPLAY_ID::get())) { + // CompositionEngine has assigned a placeholder GpuVirtualDisplayId and we need to + // override it with the correct HalVirtualDisplayId. + compositionDisplay->setDisplayIdForTesting(DISPLAY_ID::get()); + } + + auto injector = + TestableSurfaceFlinger::FakeDisplayDeviceInjector(test->mFlinger, + compositionDisplay, + CONNECTION_TYPE::value, + HWC_DISPLAY_ID_OPT::value, + static_cast<bool>(PRIMARY)); + + injector.setSecure(static_cast<bool>(SECURE)); + injector.setNativeWindow(test->mNativeWindow); + + // Creating a DisplayDevice requires getting default dimensions from the + // native window along with some other initial setup. + EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0))); + EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0))); + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)) + .WillRepeatedly(Return(0)); + + return injector; + } + + // Called by tests to set up any native window creation call expectations. + static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mNativeWindowSurface, getNativeWindow()) + .WillOnce(Return(test->mNativeWindow)); + + EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(WIDTH), Return(0))); + EXPECT_CALL(*test->mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(HEIGHT), Return(0))); + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)) + .WillRepeatedly(Return(0)); + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)) + .WillRepeatedly(Return(0)); + } + + static void setupFramebufferConsumerBufferQueueCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*test->mConsumer, setConsumerName(_)).WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*test->mConsumer, setConsumerUsageBits(GRALLOC_USAGE)) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*test->mConsumer, setDefaultBufferSize(WIDTH, HEIGHT)) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*test->mConsumer, setMaxAcquiredBufferCount(_)) + .WillRepeatedly(Return(NO_ERROR)); + } + + static void setupFramebufferProducerBufferQueueCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mProducer, allocateBuffers(0, 0, 0, 0)).WillRepeatedly(Return()); + } +}; + +template <HWDisplayId hwcDisplayId, DisplayType hwcDisplayType, typename DisplayVariant, + typename PhysicalDisplay = void> +struct HwcDisplayVariant { + // The display id supplied by the HWC + static constexpr HWDisplayId HWC_DISPLAY_ID = hwcDisplayId; + + // The HWC display type + static constexpr DisplayType HWC_DISPLAY_TYPE = hwcDisplayType; + + // The HWC active configuration id + static constexpr int HWC_ACTIVE_CONFIG_ID = 2001; + static constexpr PowerMode INIT_POWER_MODE = hal::PowerMode::ON; + + static void injectPendingHotplugEvent(DisplayTransactionTest* test, Connection connection) { + test->mFlinger.mutablePendingHotplugEvents().emplace_back( + TestableSurfaceFlinger::HotplugEvent{HWC_DISPLAY_ID, connection}); + } + + // Called by tests to inject a HWC display setup + static void injectHwcDisplayWithNoDefaultCapabilities(DisplayTransactionTest* test) { + const auto displayId = DisplayVariant::DISPLAY_ID::get(); + ASSERT_FALSE(GpuVirtualDisplayId::tryCast(displayId)); + TestableSurfaceFlinger::FakeHwcDisplayInjector(displayId, HWC_DISPLAY_TYPE, + static_cast<bool>(DisplayVariant::PRIMARY)) + .setHwcDisplayId(HWC_DISPLAY_ID) + .setWidth(DisplayVariant::WIDTH) + .setHeight(DisplayVariant::HEIGHT) + .setActiveConfig(HWC_ACTIVE_CONFIG_ID) + .setPowerMode(INIT_POWER_MODE) + .inject(&test->mFlinger, test->mComposer); + } + + // Called by tests to inject a HWC display setup + static void injectHwcDisplay(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getDisplayCapabilities(HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), + Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, setPowerMode(HWC_DISPLAY_ID, INIT_POWER_MODE)) + .WillOnce(Return(Error::NONE)); + injectHwcDisplayWithNoDefaultCapabilities(test); + } + + static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( + DisplayTransactionTest* test) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() + .setPhysical({DisplayVariant::DISPLAY_ID::get(), + PhysicalDisplay::CONNECTION_TYPE}) + .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT}) + .setIsSecure(static_cast<bool>(DisplayVariant::SECURE)) + .setPowerAdvisor(&test->mPowerAdvisor) + .setName(std::string("Injected display for ") + + test_info->test_case_name() + "." + test_info->name()) + .build(); + + return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), + ceDisplayArgs); + } + + static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) { + constexpr auto CONNECTION_TYPE = + PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal + ? IComposerClient::DisplayConnectionType::INTERNAL + : IComposerClient::DisplayConnectionType::EXTERNAL; + + EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), Return(hal::V2_4::Error::NONE))); + + EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)) + .WillOnce(Return(hal::Error::NONE)); + EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}), + Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, + getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, + IComposerClient::Attribute::WIDTH, _)) + .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::WIDTH), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, + getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, + IComposerClient::Attribute::HEIGHT, _)) + .WillOnce(DoAll(SetArgPointee<3>(DisplayVariant::HEIGHT), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, + getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, + IComposerClient::Attribute::VSYNC_PERIOD, _)) + .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_REFRESH_RATE), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, + getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, + IComposerClient::Attribute::DPI_X, _)) + .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, + getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, + IComposerClient::Attribute::DPI_Y, _)) + .WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, + getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID, + IComposerClient::Attribute::CONFIG_GROUP, _)) + .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE))); + + if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) { + EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(PhysicalDisplay::PORT), + SetArgPointee<2>(PhysicalDisplay::GET_IDENTIFICATION_DATA()), + Return(Error::NONE))); + } else { + EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _)) + .WillOnce(Return(Error::UNSUPPORTED)); + } + } + + // Called by tests to set up HWC call expectations + static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getActiveConfig(HWC_DISPLAY_ID, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(HWC_ACTIVE_CONFIG_ID), Return(Error::NONE))); + } +}; + +// Physical displays are expected to be synchronous, secure, and have a HWC display for output. +constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY = + GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_FB; + +template <typename PhysicalDisplay, int width, int height, Critical critical> +struct PhysicalDisplayVariant + : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical, + Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY, + GRALLOC_USAGE_PHYSICAL_DISPLAY>, + HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL, + DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, + critical, Async::FALSE, Secure::TRUE, + PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY>, + PhysicalDisplay> {}; + +template <bool hasIdentificationData> +struct PrimaryDisplay { + static constexpr auto CONNECTION_TYPE = DisplayConnectionType::Internal; + static constexpr Primary PRIMARY = Primary::TRUE; + static constexpr uint8_t PORT = 255; + static constexpr HWDisplayId HWC_DISPLAY_ID = 1001; + static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData; + static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid; +}; + +template <bool hasIdentificationData> +struct ExternalDisplay { + static constexpr auto CONNECTION_TYPE = DisplayConnectionType::External; + static constexpr Primary PRIMARY = Primary::FALSE; + static constexpr uint8_t PORT = 254; + static constexpr HWDisplayId HWC_DISPLAY_ID = 1002; + static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData; + static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid; +}; + +struct TertiaryDisplay { + static constexpr Primary PRIMARY = Primary::FALSE; + static constexpr uint8_t PORT = 253; + static constexpr HWDisplayId HWC_DISPLAY_ID = 1003; + static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid; +}; + +// A primary display is a physical display that is critical +using PrimaryDisplayVariant = + PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>; + +// An external display is physical display that is not critical. +using ExternalDisplayVariant = + PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>; + +using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>; + +// A virtual display not supported by the HWC. +constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0; + +template <int width, int height, Secure secure> +struct NonHwcVirtualDisplayVariant + : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure, + Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY> { + using Base = + DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, + secure, Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY>; + + static void injectHwcDisplay(DisplayTransactionTest*) {} + + static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( + DisplayTransactionTest* test) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId()) + .WillByDefault(Return(Base::DISPLAY_ID::get())); + + auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() + .setPixels({Base::WIDTH, Base::HEIGHT}) + .setIsSecure(static_cast<bool>(Base::SECURE)) + .setPowerAdvisor(&test->mPowerAdvisor) + .setName(std::string("Injected display for ") + + test_info->test_case_name() + "." + test_info->name()) + .setGpuVirtualDisplayIdGenerator( + test->mFlinger.gpuVirtualDisplayIdGenerator()) + .build(); + + return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), + ceDisplayArgs); + } + + static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0); + } + + static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) { + Base::setupNativeWindowSurfaceCreationCallExpectations(test); + EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1); + } +}; + +// A virtual display supported by the HWC. +constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER; + +template <int width, int height, Secure secure> +struct HwcVirtualDisplayVariant + : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE, + secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>, + HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL, + DisplayVariant<HalVirtualDisplayIdType<42>, width, height, + Critical::FALSE, Async::TRUE, secure, Primary::FALSE, + GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY>> { + using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, + Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>; + using Self = HwcVirtualDisplayVariant<width, height, secure>; + + static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( + DisplayTransactionTest* test) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + // In order to prevent compostition engine calling into HWComposer, we + // 1. turn off the use of HWC virtual displays, + // 2. provide a GpuVirtualDisplayIdGenerator which always returns some fake ID + // 3. override the ID by calling setDisplayIdForTesting() + + ON_CALL(test->mFlinger.gpuVirtualDisplayIdGenerator(), nextId()) + .WillByDefault(Return(GpuVirtualDisplayId(0))); + + auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder() + .setUseHwcVirtualDisplays(false) + .setPixels({Base::WIDTH, Base::HEIGHT}) + .setIsSecure(static_cast<bool>(Base::SECURE)) + .setPowerAdvisor(&test->mPowerAdvisor) + .setName(std::string("Injected display for ") + + test_info->test_case_name() + "." + test_info->name()) + .setGpuVirtualDisplayIdGenerator( + test->mFlinger.gpuVirtualDisplayIdGenerator()) + .build(); + + auto compositionDisplay = + compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(), + ceDisplayArgs); + compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get()); + + // Insert display data so that the HWC thinks it created the virtual display. + if (const auto displayId = Base::DISPLAY_ID::get(); + HalVirtualDisplayId::tryCast(displayId)) { + test->mFlinger.mutableHwcDisplayData().try_emplace(displayId); + } + + return compositionDisplay; + } + + static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) { + Base::setupNativeWindowSurfaceCreationCallExpectations(test); + EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1); + } + + static void setupHwcVirtualDisplayCreationCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, createVirtualDisplay(Base::WIDTH, Base::HEIGHT, _, _)) + .WillOnce(DoAll(SetArgPointee<3>(Self::HWC_DISPLAY_ID), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE)); + } +}; + +// For this variant, the display is not a HWC display, so no HDR support should +// be configured. +struct NonHwcDisplayHdrSupportVariant { + static constexpr bool HDR10_PLUS_SUPPORTED = false; + static constexpr bool HDR10_SUPPORTED = false; + static constexpr bool HDR_HLG_SUPPORTED = false; + static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)).Times(0); + } +}; + +// For this variant, the composer should respond with am empty list of HDR +// modes, so no HDR support should be configured. +template <typename Display> +struct HdrNotSupportedVariant { + static constexpr bool HDR10_PLUS_SUPPORTED = false; + static constexpr bool HDR10_SUPPORTED = false; + static constexpr bool HDR_HLG_SUPPORTED = false; + static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>()), Return(Error::NONE))); + } +}; + +struct NonHwcPerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = 0; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(_)).Times(0); + } +}; + +template <typename Display> +struct NoPerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = 0; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) + .WillOnce(Return(std::vector<PerFrameMetadataKey>())); + } +}; + +// For this variant, SurfaceFlinger should configure itself with wide display +// support, but the display should respond with an empty list of supported color +// modes. Wide-color support for the display should not be configured. +template <typename Display> +struct WideColorNotSupportedVariant { + static constexpr bool WIDE_COLOR_SUPPORTED = false; + + static void injectConfigChange(DisplayTransactionTest* test) { + test->mFlinger.mutableUseColorManagement() = true; + test->mFlinger.mutableHasWideColorDisplay() = true; + } + + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>()), Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0); + } +}; + +// For this variant, SurfaceFlinger should not configure itself with wide +// display support, so the display should not be configured for wide-color +// support. +struct WideColorSupportNotConfiguredVariant { + static constexpr bool WIDE_COLOR_SUPPORTED = false; + + static void injectConfigChange(DisplayTransactionTest* test) { + test->mFlinger.mutableHasWideColorDisplay() = false; + test->mFlinger.mutableUseColorManagement() = false; + test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; + } + + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getColorModes(_, _)).Times(0); + EXPECT_CALL(*test->mComposer, getRenderIntents(_, _, _)).Times(0); + EXPECT_CALL(*test->mComposer, setColorMode(_, _, _)).Times(0); + } +}; + +/* ------------------------------------------------------------------------ + * Typical display configurations to test + */ + +template <typename DisplayPolicy, typename WideColorSupportPolicy, typename HdrSupportPolicy, + typename PerFrameMetadataSupportPolicy> +struct Case { + using Display = DisplayPolicy; + using WideColorSupport = WideColorSupportPolicy; + using HdrSupport = HdrSupportPolicy; + using PerFrameMetadataSupport = PerFrameMetadataSupportPolicy; +}; + +using SimplePrimaryDisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + HdrNotSupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using SimpleExternalDisplayCase = + Case<ExternalDisplayVariant, WideColorNotSupportedVariant<ExternalDisplayVariant>, + HdrNotSupportedVariant<ExternalDisplayVariant>, + NoPerFrameMetadataSupportVariant<ExternalDisplayVariant>>; +using SimpleTertiaryDisplayCase = + Case<TertiaryDisplayVariant, WideColorNotSupportedVariant<TertiaryDisplayVariant>, + HdrNotSupportedVariant<TertiaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<TertiaryDisplayVariant>>; + +using NonHwcVirtualDisplayCase = + Case<NonHwcVirtualDisplayVariant<1024, 768, Secure::FALSE>, + WideColorSupportNotConfiguredVariant, NonHwcDisplayHdrSupportVariant, + NonHwcPerFrameMetadataSupportVariant>; +using SimpleHwcVirtualDisplayVariant = HwcVirtualDisplayVariant<1024, 768, Secure::TRUE>; +using HwcVirtualDisplayCase = + Case<SimpleHwcVirtualDisplayVariant, WideColorSupportNotConfiguredVariant, + HdrNotSupportedVariant<SimpleHwcVirtualDisplayVariant>, + NoPerFrameMetadataSupportVariant<SimpleHwcVirtualDisplayVariant>>; + +} // namespace android + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp deleted file mode 100644 index 9dc4193ecc..0000000000 --- a/services/surfaceflinger/tests/unittests/EventControlThreadTest.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "LibSurfaceFlingerUnittests" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <log/log.h> - -#include "AsyncCallRecorder.h" -#include "Scheduler/EventControlThread.h" - -namespace android { -namespace { - -using namespace std::chrono_literals; -using testing::_; - -class EventControlThreadTest : public testing::Test { -protected: - EventControlThreadTest(); - ~EventControlThreadTest() override; - - void createThread(); - - void expectVSyncEnableCallbackCalled(bool enable); - - AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; - - std::unique_ptr<EventControlThread> mThread; -}; - -EventControlThreadTest::EventControlThreadTest() { - 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()); -} - -EventControlThreadTest::~EventControlThreadTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); -} - -void EventControlThreadTest::createThread() { - mThread = std::make_unique<android::impl::EventControlThread>( - mVSyncSetEnabledCallRecorder.getInvocable()); -} - -void EventControlThreadTest::expectVSyncEnableCallbackCalled(bool expectedEnabled) { - auto args = mVSyncSetEnabledCallRecorder.waitForCall(); - ASSERT_TRUE(args.has_value()); - EXPECT_EQ(std::get<0>(args.value()), expectedEnabled); -} - -/* ------------------------------------------------------------------------ - * Test cases - */ - -TEST_F(EventControlThreadTest, signalsVSyncDisabledOnStartup) { - createThread(); - - // On thread start, there should be an automatic explicit call to disable - // vsyncs - expectVSyncEnableCallbackCalled(false); -} - -TEST_F(EventControlThreadTest, signalsVSyncDisabledOnce) { - createThread(); - expectVSyncEnableCallbackCalled(false); - - mThread->setVsyncEnabled(false); - - EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); -} - -TEST_F(EventControlThreadTest, signalsVSyncEnabledThenDisabled) { - createThread(); - expectVSyncEnableCallbackCalled(false); - - mThread->setVsyncEnabled(true); - - expectVSyncEnableCallbackCalled(true); - - mThread->setVsyncEnabled(false); - - expectVSyncEnableCallbackCalled(false); -} - -TEST_F(EventControlThreadTest, signalsVSyncEnabledOnce) { - createThread(); - expectVSyncEnableCallbackCalled(false); - - mThread->setVsyncEnabled(true); - - expectVSyncEnableCallbackCalled(true); - - mThread->setVsyncEnabled(true); - - EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); -} - -} // namespace -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index b90b566eee..3aafd456d9 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -36,9 +36,9 @@ namespace android { namespace { -constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111; -constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID = 222; -constexpr PhysicalDisplayId DISPLAY_ID_64BIT = 0xabcd12349876fedcULL; +constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID(111); +constexpr PhysicalDisplayId EXTERNAL_DISPLAY_ID(222); +constexpr PhysicalDisplayId DISPLAY_ID_64BIT(0xabcd12349876fedcULL); class MockVSyncSource : public VSyncSource { public: @@ -46,7 +46,9 @@ public: MOCK_METHOD1(setVSyncEnabled, void(bool)); MOCK_METHOD1(setCallback, void(VSyncSource::Callback*)); - MOCK_METHOD1(setPhaseOffset, void(nsecs_t)); + MOCK_METHOD2(setDuration, + void(std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration)); MOCK_METHOD1(pauseVsyncCallback, void(bool)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; @@ -57,9 +59,11 @@ class EventThreadTest : public testing::Test { protected: class MockEventThreadConnection : public EventThreadConnection { public: - MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback, + MockEventThreadConnection(impl::EventThread* eventThread, uid_t callingUid, + ResyncCallback&& resyncCallback, ISurfaceComposer::ConfigChanged configChanged) - : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {} + : EventThreadConnection(eventThread, callingUid, std::move(resyncCallback), + configChanged) {} MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event)); }; @@ -71,10 +75,12 @@ protected: void createThread(std::unique_ptr<VSyncSource>); sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, - ISurfaceComposer::ConfigChanged configChanged); + ISurfaceComposer::ConfigChanged configChanged, + uid_t ownerUid = mConnectionUid); void expectVSyncSetEnabledCallReceived(bool expectedState); - void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset); + void expectVSyncSetDurationCallReceived(std::chrono::nanoseconds expectedDuration, + std::chrono::nanoseconds expectedReadyDuration); VSyncSource::Callback* expectVSyncSetCallbackCallReceived(); void expectInterceptCallReceived(nsecs_t expectedTimestamp); void expectVsyncEventReceivedByConnection(const char* name, @@ -86,18 +92,26 @@ protected: void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId, nsecs_t expectedVsyncPeriod); + void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t); AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; - AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder; + AsyncCallRecorder<void (*)(std::chrono::nanoseconds, std::chrono::nanoseconds)> + mVSyncSetDurationCallRecorder; AsyncCallRecorder<void (*)()> mResyncCallRecorder; AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder; ConnectionEventRecorder mConnectionEventCallRecorder{0}; + ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0}; MockVSyncSource* mVSyncSource; VSyncSource::Callback* mCallback = nullptr; std::unique_ptr<impl::EventThread> mThread; sp<MockEventThreadConnection> mConnection; + sp<MockEventThreadConnection> mThrottledConnection; + + static constexpr uid_t mConnectionUid = 443; + static constexpr uid_t mThrottledConnectionUid = 177; }; EventThreadTest::EventThreadTest() { @@ -114,12 +128,15 @@ EventThreadTest::EventThreadTest() { EXPECT_CALL(*mVSyncSource, setCallback(_)) .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable())); - EXPECT_CALL(*mVSyncSource, setPhaseOffset(_)) - .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable())); + EXPECT_CALL(*mVSyncSource, setDuration(_, _)) + .WillRepeatedly(Invoke(mVSyncSetDurationCallRecorder.getInvocable())); createThread(std::move(vsyncSource)); mConnection = createConnection(mConnectionEventCallRecorder, ISurfaceComposer::eConfigChangedDispatch); + mThrottledConnection = + createConnection(mThrottledConnectionEventCallRecorder, + ISurfaceComposer::eConfigChangedDispatch, mThrottledConnectionUid); // A display must be connected for VSYNC events to be delivered. mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, true); @@ -136,8 +153,15 @@ EventThreadTest::~EventThreadTest() { } void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) { + const auto throttleVsync = [&](nsecs_t expectedVsyncTimestamp, uid_t uid) { + mThrottleVsyncCallRecorder.getInvocable()(expectedVsyncTimestamp, uid); + return (uid == mThrottledConnectionUid); + }; + mThread = std::make_unique<impl::EventThread>(std::move(source), - mInterceptVSyncCallRecorder.getInvocable()); + /*tokenManager=*/nullptr, + mInterceptVSyncCallRecorder.getInvocable(), + throttleVsync); // EventThread should register itself as VSyncSource callback. mCallback = expectVSyncSetCallbackCallReceived(); @@ -145,10 +169,11 @@ void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) { } sp<EventThreadTest::MockEventThreadConnection> EventThreadTest::createConnection( - ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged) { + ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged, + uid_t ownerUid) { sp<MockEventThreadConnection> connection = - new MockEventThreadConnection(mThread.get(), mResyncCallRecorder.getInvocable(), - configChanged); + new MockEventThreadConnection(mThread.get(), ownerUid, + mResyncCallRecorder.getInvocable(), configChanged); EXPECT_CALL(*connection, postEvent(_)).WillRepeatedly(Invoke(recorder.getInvocable())); return connection; } @@ -159,10 +184,12 @@ void EventThreadTest::expectVSyncSetEnabledCallReceived(bool expectedState) { EXPECT_EQ(expectedState, std::get<0>(args.value())); } -void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset) { - auto args = mVSyncSetPhaseOffsetCallRecorder.waitForCall(); +void EventThreadTest::expectVSyncSetDurationCallReceived( + std::chrono::nanoseconds expectedDuration, std::chrono::nanoseconds expectedReadyDuration) { + auto args = mVSyncSetDurationCallRecorder.waitForCall(); ASSERT_TRUE(args.has_value()); - EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value())); + EXPECT_EQ(expectedDuration, std::get<0>(args.value())); + EXPECT_EQ(expectedReadyDuration, std::get<1>(args.value())); } VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() { @@ -176,6 +203,13 @@ void EventThreadTest::expectInterceptCallReceived(nsecs_t expectedTimestamp) { EXPECT_EQ(expectedTimestamp, std::get<0>(args.value())); } +void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t uid) { + auto args = mThrottleVsyncCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedTimestamp, std::get<0>(args.value())); + EXPECT_EQ(uid, std::get<1>(args.value())); +} + void EventThreadTest::expectVsyncEventReceivedByConnection( const char* name, ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount) { @@ -229,7 +263,7 @@ namespace { TEST_F(EventThreadTest, canCreateAndDestroyThreadWithNoEventsSent) { EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mVSyncSetCallbackCallRecorder.waitForCall(0us).has_value()); - EXPECT_FALSE(mVSyncSetPhaseOffsetCallRecorder.waitForCall(0us).has_value()); + EXPECT_FALSE(mVSyncSetDurationCallRecorder.waitForCall(0us).has_value()); EXPECT_FALSE(mResyncCallRecorder.waitForCall(0us).has_value()); EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value()); EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value()); @@ -258,15 +292,17 @@ TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { // Use the received callback to signal a first vsync event. // The interceptor should receive the event, as well as the connection. - mCallback->onVSyncEvent(123, 456); + mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); + expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); // Use the received callback to signal a second vsync event. - // The interceptor should receive the event, but the the connection should + // The interceptor should receive the event, but the connection should // not as it was only interested in the first. - mCallback->onVSyncEvent(456, 123); + mCallback->onVSyncEvent(456, 123, 0); expectInterceptCallReceived(456); + EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); // EventThread should also detect that at this point that it does not need @@ -299,7 +335,7 @@ TEST_F(EventThreadTest, setVsyncRateZeroPostsNoVSyncEventsToThatConnection) { // Send a vsync event. EventThread should then make a call to the // interceptor, and the second connection. The first connection should not // get the event. - mCallback->onVSyncEvent(123, 456); + mCallback->onVSyncEvent(123, 456, 0); expectInterceptCallReceived(123); EXPECT_FALSE(firstConnectionEventRecorder.waitForUnexpectedCall().has_value()); expectVsyncEventReceivedByConnection("secondConnection", secondConnectionEventRecorder, 123, @@ -314,18 +350,21 @@ TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) { // Send a vsync event. EventThread should then make a call to the // interceptor, and the connection. - mCallback->onVSyncEvent(123, 456); + mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); + expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); // A second event should go to the same places. - mCallback->onVSyncEvent(456, 123); + mCallback->onVSyncEvent(456, 123, 0); expectInterceptCallReceived(456); + expectThrottleVsyncReceived(123, mConnectionUid); expectVsyncEventReceivedByConnection(456, 2u); // A third event should go to the same places. - mCallback->onVSyncEvent(789, 777); + mCallback->onVSyncEvent(789, 777, 111); expectInterceptCallReceived(789); + expectThrottleVsyncReceived(777, mConnectionUid); expectVsyncEventReceivedByConnection(789, 3u); } @@ -336,22 +375,25 @@ TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) { expectVSyncSetEnabledCallReceived(true); // The first event will be seen by the interceptor, and not the connection. - mCallback->onVSyncEvent(123, 456); + mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The second event will be seen by the interceptor and the connection. - mCallback->onVSyncEvent(456, 123); + mCallback->onVSyncEvent(456, 123, 0); expectInterceptCallReceived(456); expectVsyncEventReceivedByConnection(456, 2u); + EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The third event will be seen by the interceptor, and not the connection. - mCallback->onVSyncEvent(789, 777); + mCallback->onVSyncEvent(789, 777, 744); expectInterceptCallReceived(789); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + EXPECT_FALSE(mThrottleVsyncCallRecorder.waitForUnexpectedCall().has_value()); // The fourth event will be seen by the interceptor and the connection. - mCallback->onVSyncEvent(101112, 7847); + mCallback->onVSyncEvent(101112, 7847, 86); expectInterceptCallReceived(101112); expectVsyncEventReceivedByConnection(101112, 4u); } @@ -366,7 +408,7 @@ TEST_F(EventThreadTest, connectionsRemovedIfInstanceDestroyed) { mConnection = nullptr; // The first event will be seen by the interceptor, and not the connection. - mCallback->onVSyncEvent(123, 456); + mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); @@ -386,13 +428,13 @@ TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { // The first event will be seen by the interceptor, and by the connection, // which then returns an error. - mCallback->onVSyncEvent(123, 456); + mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); // A subsequent event will be seen by the interceptor and not by the // connection. - mCallback->onVSyncEvent(456, 123); + mCallback->onVSyncEvent(456, 123, 0); expectInterceptCallReceived(456); EXPECT_FALSE(errorConnectionEventRecorder.waitForUnexpectedCall().has_value()); @@ -401,31 +443,31 @@ TEST_F(EventThreadTest, connectionsRemovedIfEventDeliveryError) { } TEST_F(EventThreadTest, tracksEventConnections) { - EXPECT_EQ(1, mThread->getEventThreadConnectionCount()); + EXPECT_EQ(2, mThread->getEventThreadConnectionCount()); ConnectionEventRecorder errorConnectionEventRecorder{NO_MEMORY}; sp<MockEventThreadConnection> errorConnection = createConnection(errorConnectionEventRecorder, ISurfaceComposer::eConfigChangedSuppress); mThread->setVsyncRate(1, errorConnection); - EXPECT_EQ(2, mThread->getEventThreadConnectionCount()); + EXPECT_EQ(3, mThread->getEventThreadConnectionCount()); ConnectionEventRecorder secondConnectionEventRecorder{0}; sp<MockEventThreadConnection> secondConnection = createConnection(secondConnectionEventRecorder, ISurfaceComposer::eConfigChangedSuppress); mThread->setVsyncRate(1, secondConnection); - EXPECT_EQ(3, mThread->getEventThreadConnectionCount()); + EXPECT_EQ(4, mThread->getEventThreadConnectionCount()); // EventThread should enable vsync callbacks. expectVSyncSetEnabledCallReceived(true); // The first event will be seen by the interceptor, and by the connection, // which then returns an error. - mCallback->onVSyncEvent(123, 456); + mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); expectVsyncEventReceivedByConnection("successConnection", secondConnectionEventRecorder, 123, 1u); - EXPECT_EQ(2, mThread->getEventThreadConnectionCount()); + EXPECT_EQ(3, mThread->getEventThreadConnectionCount()); } TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { @@ -440,13 +482,13 @@ TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { // The first event will be seen by the interceptor, and by the connection, // which then returns an non-fatal error. - mCallback->onVSyncEvent(123, 456); + mCallback->onVSyncEvent(123, 456, 789); expectInterceptCallReceived(123); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 123, 1u); // A subsequent event will be seen by the interceptor, and by the connection, // which still then returns an non-fatal error. - mCallback->onVSyncEvent(456, 123); + mCallback->onVSyncEvent(456, 123, 0); expectInterceptCallReceived(456); expectVsyncEventReceivedByConnection("errorConnection", errorConnectionEventRecorder, 456, 2u); @@ -455,8 +497,8 @@ TEST_F(EventThreadTest, eventsDroppedIfNonfatalEventDeliveryError) { } TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) { - mThread->setPhaseOffset(321); - expectVSyncSetPhaseOffsetCallReceived(321); + mThread->setDuration(321ns, 456ns); + expectVSyncSetDurationCallReceived(321ns, 456ns); } TEST_F(EventThreadTest, postHotplugInternalDisconnect) { @@ -507,5 +549,35 @@ TEST_F(EventThreadTest, suppressConfigChanged) { ASSERT_FALSE(args.has_value()); } +TEST_F(EventThreadTest, requestNextVsyncWithThrottleVsyncDoesntPostVSync) { + // Signal that we want the next vsync event to be posted to the throttled connection + mThread->requestNextVsync(mThrottledConnection); + + // EventThread should immediately request a resync. + EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value()); + + // EventThread should enable vsync callbacks. + expectVSyncSetEnabledCallReceived(true); + + // Use the received callback to signal a first vsync event. + // The interceptor should receive the event, but not the connection. + mCallback->onVSyncEvent(123, 456, 789); + expectInterceptCallReceived(123); + expectThrottleVsyncReceived(456, mThrottledConnectionUid); + mThrottledConnectionEventCallRecorder.waitForUnexpectedCall(); + + // Use the received callback to signal a second vsync event. + // The interceptor should receive the event, but the connection should + // not as it was only interested in the first. + mCallback->onVSyncEvent(456, 123, 0); + expectInterceptCallReceived(456); + expectThrottleVsyncReceived(123, mThrottledConnectionUid); + EXPECT_FALSE(mConnectionEventCallRecorder.waitForUnexpectedCall().has_value()); + + // EventThread should not change the vsync state as it didn't send the event + // yet + EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value()); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h index b50ddf5b9a..4cd1e0adf1 100644 --- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h +++ b/services/surfaceflinger/tests/unittests/FakeVsyncConfiguration.h @@ -18,19 +18,23 @@ #include <gmock/gmock.h> -#include "Scheduler/PhaseOffsets.h" +#include "Scheduler/VsyncConfiguration.h" namespace android::scheduler { -struct FakePhaseOffsets : PhaseConfiguration { +struct FakePhaseOffsets : VsyncConfiguration { static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0; + static constexpr auto FAKE_DURATION_OFFSET_NS = std::chrono::nanoseconds(0); - Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); } + VsyncConfigSet getConfigsForRefreshRate(float) const override { return getCurrentConfigs(); } - Offsets getCurrentOffsets() const override { - return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, - {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, - {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}}; + VsyncConfigSet getCurrentConfigs() const override { + return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, + FAKE_DURATION_OFFSET_NS}, + {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, + FAKE_DURATION_OFFSET_NS}, + {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS, FAKE_DURATION_OFFSET_NS, + FAKE_DURATION_OFFSET_NS}}; } void setRefreshRateFps(float) override {} diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp new file mode 100644 index 0000000000..03c6f70db5 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -0,0 +1,416 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gmock/gmock-spec-builders.h" +#include "mock/MockTimeStats.h" +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include <FrameTimeline/FrameTimeline.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <cinttypes> + +using namespace std::chrono_literals; +using testing::Contains; + +MATCHER_P(HasBit, bit, "") { + return (arg & bit) != 0; +} + +namespace android::frametimeline { + +class FrameTimelineTest : public testing::Test { +public: + FrameTimelineTest() { + 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()); + } + + ~FrameTimelineTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + void SetUp() override { + mTimeStats = std::make_shared<mock::TimeStats>(); + mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats); + mTokenManager = &mFrameTimeline->mTokenManager; + maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames; + maxTokenRetentionTime = mTokenManager->kMaxRetentionTime; + } + + void flushTokens(nsecs_t flushTime) { + std::lock_guard<std::mutex> lock(mTokenManager->mMutex); + mTokenManager->flushTokens(flushTime); + } + + SurfaceFrame& getSurfaceFrame(size_t displayFrameIdx, size_t surfaceFrameIdx) { + std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex); + return *(mFrameTimeline->mDisplayFrames[displayFrameIdx]->surfaceFrames[surfaceFrameIdx]); + } + + std::shared_ptr<impl::FrameTimeline::DisplayFrame> getDisplayFrame(size_t idx) { + std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex); + return mFrameTimeline->mDisplayFrames[idx]; + } + + static bool compareTimelineItems(const TimelineItem& a, const TimelineItem& b) { + return a.startTime == b.startTime && a.endTime == b.endTime && + a.presentTime == b.presentTime; + } + + const std::unordered_map<int64_t, TimelineItem>& getPredictions() { + return mTokenManager->mPredictions; + } + + uint32_t getNumberOfDisplayFrames() { + std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex); + return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size()); + } + + std::shared_ptr<mock::TimeStats> mTimeStats; + std::unique_ptr<impl::FrameTimeline> mFrameTimeline; + impl::TokenManager* mTokenManager; + FenceToFenceTimeMap fenceFactory; + uint32_t* maxDisplayFrames; + nsecs_t maxTokenRetentionTime; +}; + +static const std::string sLayerNameOne = "layer1"; +static const std::string sLayerNameTwo = "layer2"; +static constexpr const uid_t sUidOne = 0; +static constexpr pid_t sPidOne = 10; +static constexpr pid_t sPidTwo = 20; + +TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) { + int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0}); + EXPECT_EQ(getPredictions().size(), 1); + flushTokens(systemTime() + maxTokenRetentionTime); + int64_t token2 = mTokenManager->generateTokenForPredictions({10, 20, 30}); + std::optional<TimelineItem> predictions = mTokenManager->getPredictionsForToken(token1); + + // token1 should have expired + EXPECT_EQ(getPredictions().size(), 1); + EXPECT_EQ(predictions.has_value(), false); + + predictions = mTokenManager->getPredictionsForToken(token2); + EXPECT_EQ(compareTimelineItems(*predictions, TimelineItem(10, 20, 30)), true); +} + +TEST_F(FrameTimelineTest, createSurfaceFrameForToken_getOwnerPidReturnsCorrectPid) { + auto surfaceFrame1 = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); + auto surfaceFrame2 = mFrameTimeline->createSurfaceFrameForToken(sPidTwo, sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); + EXPECT_EQ(surfaceFrame1->getOwnerPid(), sPidOne); + EXPECT_EQ(surfaceFrame2->getOwnerPid(), sPidTwo); +} + +TEST_F(FrameTimelineTest, createSurfaceFrameForToken_noToken) { + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); + EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::None); +} + +TEST_F(FrameTimelineTest, createSurfaceFrameForToken_expiredToken) { + int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0}); + flushTokens(systemTime() + maxTokenRetentionTime); + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, token1); + + EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Expired); +} + +TEST_F(FrameTimelineTest, createSurfaceFrameForToken_validToken) { + int64_t token1 = mTokenManager->generateTokenForPredictions({10, 20, 30}); + auto surfaceFrame = mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, token1); + + EXPECT_EQ(surfaceFrame->getPredictionState(), PredictionState::Valid); + EXPECT_EQ(compareTimelineItems(surfaceFrame->getPredictions(), TimelineItem(10, 20, 30)), true); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_droppedFramesNotUpdated) { + 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(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, token1); + + // Set up the display frame + mFrameTimeline->setSfWakeUp(token1, 20); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), SurfaceFrame::PresentState::Dropped); + mFrameTimeline->setSfPresent(25, presentFence1); + presentFence1->signalForTest(30); + + // Trigger a flush by calling setSfPresent for the next frame + mFrameTimeline->setSfWakeUp(token2, 50); + mFrameTimeline->setSfPresent(55, presentFence2); + + auto& droppedSurfaceFrame = getSurfaceFrame(0, 0); + EXPECT_EQ(droppedSurfaceFrame.getPresentState(), SurfaceFrame::PresentState::Dropped); + EXPECT_EQ(droppedSurfaceFrame.getActuals().presentTime, 0); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_presentedFramesUpdated) { + 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(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, surfaceFrameToken1); + auto surfaceFrame2 = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameTwo, + sLayerNameTwo, surfaceFrameToken1); + mFrameTimeline->setSfWakeUp(sfToken1, 22); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), + SurfaceFrame::PresentState::Presented); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame2), + SurfaceFrame::PresentState::Presented); + mFrameTimeline->setSfPresent(26, presentFence1); + auto displayFrame = getDisplayFrame(0); + SurfaceFrame& presentedSurfaceFrame1 = getSurfaceFrame(0, 0); + SurfaceFrame& presentedSurfaceFrame2 = getSurfaceFrame(0, 1); + presentFence1->signalForTest(42); + + // Fences haven't been flushed yet, so it should be 0 + EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 0); + 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(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, surfaceFrameToken2); + mFrameTimeline->setSfWakeUp(sfToken2, 52); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame3), SurfaceFrame::PresentState::Dropped); + mFrameTimeline->setSfPresent(56, presentFence2); + displayFrame = getDisplayFrame(0); + + // Fences have flushed, so the present timestamps should be updated + EXPECT_EQ(displayFrame->surfaceFlingerActuals.presentTime, 42); + EXPECT_EQ(presentedSurfaceFrame1.getActuals().presentTime, 42); + EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42); +} + +TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) { + // Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque + int frameTimeFactor = 0; + for (size_t i = 0; i < *maxDisplayFrames; i++) { + auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions( + {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor}); + int64_t sfToken = mTokenManager->generateTokenForPredictions( + {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor}); + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, surfaceFrameToken); + mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), + SurfaceFrame::PresentState::Presented); + mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence); + presentFence->signalForTest(32 + frameTimeFactor); + frameTimeFactor += 30; + } + auto displayFrame0 = getDisplayFrame(0); + + // The 0th Display Frame should have actuals 22, 27, 32 + EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(22, 27, 32)), + true); + + // Add one more display frame + auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + int64_t surfaceFrameToken = mTokenManager->generateTokenForPredictions( + {10 + frameTimeFactor, 20 + frameTimeFactor, 30 + frameTimeFactor}); + int64_t sfToken = mTokenManager->generateTokenForPredictions( + {22 + frameTimeFactor, 26 + frameTimeFactor, 30 + frameTimeFactor}); + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, surfaceFrameToken); + mFrameTimeline->setSfWakeUp(sfToken, 22 + frameTimeFactor); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), SurfaceFrame::PresentState::Presented); + mFrameTimeline->setSfPresent(27 + frameTimeFactor, presentFence); + presentFence->signalForTest(32 + frameTimeFactor); + displayFrame0 = getDisplayFrame(0); + + // The window should have slided by 1 now and the previous 0th display frame + // should have been removed from the deque + EXPECT_EQ(compareTimelineItems(displayFrame0->surfaceFlingerActuals, TimelineItem(52, 57, 62)), + true); +} + +TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceAfterQueue) { + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue", + "acquireFenceAfterQueue", std::nullopt); + surfaceFrame->setActualQueueTime(123); + surfaceFrame->setAcquireFenceTime(456); + EXPECT_EQ(surfaceFrame->getActuals().endTime, 456); +} + +TEST_F(FrameTimelineTest, surfaceFrameEndTimeAcquireFenceBeforeQueue) { + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, 0, "acquireFenceAfterQueue", + "acquireFenceAfterQueue", std::nullopt); + surfaceFrame->setActualQueueTime(456); + surfaceFrame->setAcquireFenceTime(123); + EXPECT_EQ(surfaceFrame->getActuals().endTime, 456); +} + +TEST_F(FrameTimelineTest, setMaxDisplayFramesSetsSizeProperly) { + auto presentFence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + presentFence->signalForTest(2); + + // Size shouldn't exceed maxDisplayFrames - 64 + for (size_t i = 0; i < *maxDisplayFrames + 10; i++) { + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); + int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); + mFrameTimeline->setSfWakeUp(sfToken, 22); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), + SurfaceFrame::PresentState::Presented); + mFrameTimeline->setSfPresent(27, presentFence); + } + EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames); + + // Increase the size to 256 + mFrameTimeline->setMaxDisplayFrames(256); + EXPECT_EQ(*maxDisplayFrames, 256); + + for (size_t i = 0; i < *maxDisplayFrames + 10; i++) { + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); + int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); + mFrameTimeline->setSfWakeUp(sfToken, 22); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), + SurfaceFrame::PresentState::Presented); + mFrameTimeline->setSfPresent(27, presentFence); + } + EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames); + + // Shrink the size to 128 + mFrameTimeline->setMaxDisplayFrames(128); + EXPECT_EQ(*maxDisplayFrames, 128); + + for (size_t i = 0; i < *maxDisplayFrames + 10; i++) { + auto surfaceFrame = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, std::nullopt); + int64_t sfToken = mTokenManager->generateTokenForPredictions({22, 26, 30}); + mFrameTimeline->setSfWakeUp(sfToken, 22); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame), + SurfaceFrame::PresentState::Presented); + mFrameTimeline->setSfPresent(27, presentFence); + } + EXPECT_EQ(getNumberOfDisplayFrames(), *maxDisplayFrames); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsLongSfCpu) { + EXPECT_CALL(*mTimeStats, + incrementJankyFrames(sUidOne, sLayerNameOne, + HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed))); + EXPECT_CALL(*mTimeStats, + incrementJankyFrames(HasBit(TimeStats::JankType::SurfaceFlingerDeadlineMissed))); + 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()}); + 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()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, surfaceFrameToken1); + mFrameTimeline->setSfWakeUp(sfToken1, + std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count()); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), + SurfaceFrame::PresentState::Presented); + presentFence1->signalForTest( + std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()); + + mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(), + presentFence1); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsDisplayMiss) { + EXPECT_CALL(*mTimeStats, + incrementJankyFrames(sUidOne, sLayerNameOne, HasBit(TimeStats::JankType::Display))); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::Display))); + 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()}); + 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()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, surfaceFrameToken1); + mFrameTimeline->setSfWakeUp(sfToken1, + std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count()); + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), + SurfaceFrame::PresentState::Presented); + presentFence1->signalForTest( + std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()); + mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(59ms).count(), + presentFence1); +} + +TEST_F(FrameTimelineTest, presentFenceSignaled_reportsAppMiss) { + EXPECT_CALL(*mTimeStats, + incrementJankyFrames(sUidOne, sLayerNameOne, + HasBit(TimeStats::JankType::AppDeadlineMissed))); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(HasBit(TimeStats::JankType::AppDeadlineMissed))); + 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()}); + 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()}); + auto surfaceFrame1 = + mFrameTimeline->createSurfaceFrameForToken(sPidOne, sUidOne, sLayerNameOne, + sLayerNameOne, surfaceFrameToken1); + surfaceFrame1->setAcquireFenceTime( + std::chrono::duration_cast<std::chrono::nanoseconds>(45ms).count()); + mFrameTimeline->setSfWakeUp(sfToken1, + std::chrono::duration_cast<std::chrono::nanoseconds>(52ms).count()); + + mFrameTimeline->addSurfaceFrame(std::move(surfaceFrame1), + SurfaceFrame::PresentState::Presented); + presentFence1->signalForTest( + std::chrono::duration_cast<std::chrono::nanoseconds>(90ms).count()); + mFrameTimeline->setSfPresent(std::chrono::duration_cast<std::chrono::nanoseconds>(56ms).count(), + presentFence1); +} + +} // namespace android::frametimeline diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 91b304cca8..fa12315a91 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -23,7 +23,13 @@ #include <vector> +// StrictMock<T> derives from T and is not marked final, so the destructor of T is expected to be +// virtual in case StrictMock<T> is used as a polymorphic base class. That is not the case here. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" #include <gmock/gmock.h> +#pragma clang diagnostic pop + #include <gui/LayerMetadata.h> #include <log/log.h> @@ -45,27 +51,20 @@ using ::testing::Return; using ::testing::SetArgPointee; using ::testing::StrictMock; -struct MockHWC2ComposerCallback : public HWC2::ComposerCallback { - ~MockHWC2ComposerCallback() = default; - - MOCK_METHOD3(onHotplugReceived, - void(int32_t sequenceId, hal::HWDisplayId display, hal::Connection connection)); - MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId display)); +struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { + MOCK_METHOD3(onHotplugReceived, void(int32_t sequenceId, hal::HWDisplayId, hal::Connection)); + MOCK_METHOD2(onRefreshReceived, void(int32_t sequenceId, hal::HWDisplayId)); MOCK_METHOD4(onVsyncReceived, - void(int32_t sequenceId, hal::HWDisplayId display, int64_t timestamp, - std::optional<hal::VsyncPeriodNanos> vsyncPeriod)); + void(int32_t sequenceId, hal::HWDisplayId, int64_t timestamp, + std::optional<hal::VsyncPeriodNanos>)); MOCK_METHOD3(onVsyncPeriodTimingChangedReceived, - void(int32_t sequenceId, hal::HWDisplayId display, - const hal::VsyncPeriodChangeTimeline& updatedTimeline)); - MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId display)); + void(int32_t sequenceId, hal::HWDisplayId, const hal::VsyncPeriodChangeTimeline&)); + MOCK_METHOD2(onSeamlessPossible, void(int32_t sequenceId, hal::HWDisplayId)); }; -struct HWComposerTest : public testing::Test { +struct HWComposerSetConfigurationTest : testing::Test { Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>(); -}; - -struct HWComposerSetConfigurationTest : public HWComposerTest { - StrictMock<MockHWC2ComposerCallback> mCallback; + MockHWC2ComposerCallback mCallback; }; TEST_F(HWComposerSetConfigurationTest, loadsLayerMetadataSupport) { diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index cae317bd5d..0fbe8eca06 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -30,6 +30,7 @@ #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/MockLayer.h" +#include "mock/MockSchedulerCallback.h" using testing::_; using testing::Return; @@ -49,6 +50,8 @@ protected: LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); } + void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); } + impl::LayerHistory& history() { return *mScheduler->mutableLayerHistory(); } const impl::LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); } @@ -73,7 +76,13 @@ protected: .setConfigGroup(0) .build()}, HwcConfigIndexType(0)}; - TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, false)}; + + mock::NoOpSchedulerCallback mSchedulerCallback; + static constexpr bool kUseContentDetectionV2 = false; + + TestableScheduler* const mScheduler = + new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2); + TestableSurfaceFlinger mFlinger; const nsecs_t mTime = systemTime(); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp index afd2b7197f..cb376cd7bb 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp @@ -27,6 +27,7 @@ #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/MockLayer.h" +#include "mock/MockSchedulerCallback.h" using testing::_; using testing::Return; @@ -50,6 +51,8 @@ protected: LayerHistoryTestV2() { mFlinger.resetScheduler(mScheduler); } + void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); } + impl::LayerHistoryV2& history() { return *mScheduler->mutableLayerHistoryV2(); } const impl::LayerHistoryV2& history() const { return *mScheduler->mutableLayerHistoryV2(); } @@ -113,9 +116,14 @@ protected: .setConfigGroup(0) .build()}, HwcConfigIndexType(0)}; - TestableScheduler* const mScheduler{new TestableScheduler(mConfigs, true)}; - TestableSurfaceFlinger mFlinger; + mock::NoOpSchedulerCallback mSchedulerCallback; + static constexpr bool kUseContentDetectionV2 = true; + + TestableScheduler* const mScheduler = + new TestableScheduler(mConfigs, mSchedulerCallback, kUseContentDetectionV2); + + TestableSurfaceFlinger mFlinger; }; namespace { diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp new file mode 100644 index 0000000000..53dfe3ffba --- /dev/null +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -0,0 +1,165 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "FrameTimeline.h" +#include "Scheduler/MessageQueue.h" +#include "SurfaceFlinger.h" + +namespace android { + +using namespace std::chrono_literals; +using namespace testing; + +using CallbackToken = scheduler::VSyncDispatch::CallbackToken; + +class TestableMessageQueue : public impl::MessageQueue { +public: + class MockHandler : public MessageQueue::Handler { + public: + explicit MockHandler(MessageQueue& queue) : MessageQueue::Handler(queue) {} + ~MockHandler() override = default; + MOCK_METHOD2(dispatchInvalidate, void(int64_t vsyncId, nsecs_t expectedVSyncTimestamp)); + }; + + TestableMessageQueue() = default; + ~TestableMessageQueue() override = default; + + void initHandler(const sp<MockHandler>& handler) { mHandler = handler; } + + void triggerVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) { + vsyncCallback(vsyncTime, targetWakeupTime, readyTime); + } +}; + +class MockVSyncDispatch : public scheduler::VSyncDispatch { +public: + MockVSyncDispatch() = default; + ~MockVSyncDispatch() override = default; + + MOCK_METHOD2(registerCallback, + CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string)); + MOCK_METHOD1(unregisterCallback, void(CallbackToken)); + MOCK_METHOD2(schedule, scheduler::ScheduleResult(CallbackToken, ScheduleTiming)); + MOCK_METHOD1(cancel, scheduler::CancelResult(CallbackToken token)); + MOCK_CONST_METHOD1(dump, void(std::string&)); +}; + +class MockTokenManager : public frametimeline::TokenManager { +public: + MockTokenManager() = default; + ~MockTokenManager() override = default; + + MOCK_METHOD1(generateTokenForPredictions, int64_t(frametimeline::TimelineItem&& prediction)); +}; + +class MessageQueueTest : public testing::Test { +public: + MessageQueueTest() = default; + ~MessageQueueTest() override = default; + + void SetUp() override { + EXPECT_NO_FATAL_FAILURE(mEventQueue.initHandler(mHandler)); + + EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken)); + EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, mDuration)); + EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1); + } + + sp<TestableMessageQueue::MockHandler> mHandler = + new TestableMessageQueue::MockHandler(mEventQueue); + MockVSyncDispatch mVSyncDispatch; + MockTokenManager mTokenManager; + TestableMessageQueue mEventQueue; + + const CallbackToken mCallbackToken{5}; + constexpr static auto mDuration = std::chrono::nanoseconds(100ms); + constexpr static auto mDifferentDuration = std::chrono::nanoseconds(250ms); +}; + +namespace { +/* ------------------------------------------------------------------------ + * Test cases + */ +TEST_F(MessageQueueTest, invalidate) { + const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), + .readyDuration = 0, + .earliestVsync = 0}; + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); +} + +TEST_F(MessageQueueTest, invalidateTwice) { + InSequence s; + const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), + .readyDuration = 0, + .earliestVsync = 0}; + + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); + + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); +} + +TEST_F(MessageQueueTest, invalidateTwiceWithCallback) { + InSequence s; + const auto timing = scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), + .readyDuration = 0, + .earliestVsync = 0}; + + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); + + const auto startTime = 100; + const auto endTime = startTime + mDuration.count(); + const auto presentTime = 500; + const auto vsyncId = 42; + EXPECT_CALL(mTokenManager, + generateTokenForPredictions( + frametimeline::TimelineItem(startTime, endTime, presentTime))) + .WillOnce(Return(vsyncId)); + EXPECT_CALL(*mHandler, dispatchInvalidate(vsyncId, presentTime)).Times(1); + EXPECT_NO_FATAL_FAILURE(mEventQueue.triggerVsyncCallback(presentTime, startTime, endTime)); + + const auto timingAfterCallback = + scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDuration.count(), + .readyDuration = 0, + .earliestVsync = presentTime}; + + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).Times(1); + EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); +} + +TEST_F(MessageQueueTest, invalidateWithDurationChange) { + EXPECT_NO_FATAL_FAILURE(mEventQueue.setDuration(mDifferentDuration)); + + const auto timing = + scheduler::VSyncDispatch::ScheduleTiming{.workDuration = mDifferentDuration.count(), + .readyDuration = 0, + .earliestVsync = 0}; + + EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).Times(1); + EXPECT_NO_FATAL_FAILURE(mEventQueue.invalidate()); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp deleted file mode 100644 index 0b7468291a..0000000000 --- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#undef LOG_TAG -#define LOG_TAG "SchedulerUnittests" - -#include <gmock/gmock.h> -#include <log/log.h> -#include <thread> - -#include "Scheduler/PhaseOffsets.h" - -using namespace testing; - -namespace android { -namespace scheduler { - -class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations { -public: - TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration, - nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, - nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration) - : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration, - sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration, - appEarlyGlDuration) {} -}; - -class PhaseDurationTest : public testing::Test { -protected: - PhaseDurationTest() - : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000, - 21'000'000) {} - - ~PhaseDurationTest() = default; - - TestablePhaseOffsetsAsDurations mPhaseDurations; -}; - -namespace { -/* ------------------------------------------------------------------------ - * Test cases - */ -TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) { - mPhaseDurations.setRefreshRateFps(60.0f); - auto currentOffsets = mPhaseDurations.getCurrentOffsets(); - auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f); - - EXPECT_EQ(currentOffsets, offsets); - EXPECT_EQ(offsets.late.sf, 6'166'667); - - EXPECT_EQ(offsets.late.app, 2'333'334); - - EXPECT_EQ(offsets.early.sf, 666'667); - - EXPECT_EQ(offsets.early.app, 833'334); - - EXPECT_EQ(offsets.earlyGl.sf, 3'166'667); - - EXPECT_EQ(offsets.earlyGl.app, 15'500'001); -} - -TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) { - mPhaseDurations.setRefreshRateFps(90.0f); - auto currentOffsets = mPhaseDurations.getCurrentOffsets(); - auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f); - - EXPECT_EQ(currentOffsets, offsets); - EXPECT_EQ(offsets.late.sf, 611'111); - - EXPECT_EQ(offsets.late.app, 2'333'333); - - EXPECT_EQ(offsets.early.sf, -4'888'889); - - EXPECT_EQ(offsets.early.app, 833'333); - - EXPECT_EQ(offsets.earlyGl.sf, -2'388'889); - - EXPECT_EQ(offsets.earlyGl.app, 9'944'444); -} - -TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) { - TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1); - - auto validateOffsets = [](auto& offsets) { - EXPECT_EQ(offsets.late.sf, 1'000'000); - - EXPECT_EQ(offsets.late.app, 1'000'000); - - EXPECT_EQ(offsets.early.sf, 1'000'000); - - EXPECT_EQ(offsets.early.app, 1'000'000); - - EXPECT_EQ(offsets.earlyGl.sf, 1'000'000); - - EXPECT_EQ(offsets.earlyGl.app, 1'000'000); - }; - - phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f); - auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets(); - auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f); - EXPECT_EQ(currentOffsets, offsets); - validateOffsets(offsets); - - phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f); - currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets(); - offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f); - EXPECT_EQ(currentOffsets, offsets); - validateOffsets(offsets); -} - -TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) { - auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f); - - EXPECT_EQ(offsets.late.sf, 57'527'208); - - EXPECT_EQ(offsets.late.app, 37'027'208); - - EXPECT_EQ(offsets.early.sf, 52'027'208); - - EXPECT_EQ(offsets.early.app, 35'527'208); - - EXPECT_EQ(offsets.earlyGl.sf, 54'527'208); - - EXPECT_EQ(offsets.earlyGl.app, 33'527'208); -} - -} // namespace - -class TestablePhaseOffsets : public impl::PhaseOffsets { -public: - TestablePhaseOffsets() - : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 1'000'000, 1'000'000, {}, {}, {}, {}, - 10'000'000) {} -}; - -class PhaseOffsetsTest : public testing::Test { -protected: - PhaseOffsetsTest() = default; - ~PhaseOffsetsTest() = default; - - TestablePhaseOffsets mPhaseOffsets; -}; - -namespace { -TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) { - auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f); - - EXPECT_EQ(offsets.late.sf, 1'000'000); - - EXPECT_EQ(offsets.late.app, 1'000'000); - - EXPECT_EQ(offsets.early.sf, 1'000'000); - - EXPECT_EQ(offsets.early.app, 1'000'000); - - EXPECT_EQ(offsets.earlyGl.sf, 1'000'000); - - EXPECT_EQ(offsets.earlyGl.app, 1'000'000); -} - -} // namespace -} // namespace scheduler -} // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 1f6f166b45..4762fd4b66 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -1471,6 +1471,34 @@ TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) { EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction()); } +TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUnknownUid) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, + /*currentConfigId=*/HWC_CONFIG_ID_30); + EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(1234)); +} + +TEST_F(RefreshRateConfigsTest, RefreshRateDividerForUid) { + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(m30_60_72_90_120Device, + /*currentConfigId=*/HWC_CONFIG_ID_30); + const uid_t uid = 1234; + refreshRateConfigs->setPreferredRefreshRateForUid(uid, 30); + EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid)); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60); + EXPECT_EQ(2, refreshRateConfigs->getRefreshRateDividerForUid(uid)); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_72); + EXPECT_EQ(1, refreshRateConfigs->getRefreshRateDividerForUid(uid)); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90); + EXPECT_EQ(3, refreshRateConfigs->getRefreshRateDividerForUid(uid)); + + refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_120); + EXPECT_EQ(4, refreshRateConfigs->getRefreshRateDividerForUid(uid)); +} + } // namespace } // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp index 43b8e01c3a..8cd8372626 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp @@ -14,10 +14,6 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" @@ -31,9 +27,8 @@ #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockDispSync.h" -#include "mock/MockEventControlThread.h" #include "mock/MockEventThread.h" +#include "mock/MockVsyncController.h" namespace android { @@ -65,7 +60,7 @@ protected: static constexpr int32_t PRIORITY_UNSET = -1; void setupScheduler(); - void setupComposer(int virtualDisplayCount); + void setupComposer(uint32_t virtualDisplayCount); sp<BufferQueueLayer> createBufferQueueLayer(); sp<BufferStateLayer> createBufferStateLayer(); sp<EffectLayer> createEffectLayer(); @@ -123,7 +118,11 @@ void RefreshRateSelectionTest::setParent(Layer* child, Layer* parent) { } void RefreshRateSelectionTest::commitTransaction(Layer* layer) { - layer->commitTransaction(layer->getCurrentState()); + layer->pushPendingState(); + auto c = layer->getCurrentState(); + if (layer->applyPendingStates(&c)) { + layer->commitTransaction(c); + } } void RefreshRateSelectionTest::setupScheduler() { @@ -132,26 +131,28 @@ void RefreshRateSelectionTest::setupScheduler() { EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); - auto primaryDispSync = std::make_unique<mock::DispSync>(); + auto vsyncController = std::make_unique<mock::VsyncController>(); + auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); - EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0)); - EXPECT_CALL(*primaryDispSync, getPeriod()) + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); - EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0)); - mFlinger.setupScheduler(std::move(primaryDispSync), - std::make_unique<mock::EventControlThread>(), std::move(eventThread), - std::move(sfEventThread)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), + std::move(eventThread), std::move(sfEventThread)); } -void RefreshRateSelectionTest::setupComposer(int virtualDisplayCount) { +void RefreshRateSelectionTest::setupComposer(uint32_t virtualDisplayCount) { mComposer = new Hwc2::mock::Composer(); EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount)); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); @@ -283,6 +284,3 @@ TEST_F(RefreshRateSelectionTest, testPriorityOnEffectLayers) { } // namespace } // namespace android - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 1aa7320a8a..509858a4ab 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -14,39 +14,35 @@ * limitations under the License. */ -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" - -#undef LOG_TAG -#define LOG_TAG "SchedulerUnittests" - #include <gmock/gmock.h> #include <gtest/gtest.h> #include <log/log.h> #include <mutex> -#include "Scheduler/EventControlThread.h" #include "Scheduler/EventThread.h" #include "Scheduler/RefreshRateConfigs.h" #include "TestableScheduler.h" +#include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockDisplay.h" #include "mock/MockEventThread.h" +#include "mock/MockLayer.h" +#include "mock/MockSchedulerCallback.h" using testing::_; using testing::Return; namespace android { +namespace { -constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = 999; +constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID(999); class SchedulerTest : public testing::Test { protected: class MockEventThreadConnection : public android::EventThreadConnection { public: explicit MockEventThreadConnection(EventThread* eventThread) - : EventThreadConnection(eventThread, ResyncCallback(), + : EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress) {} ~MockEventThreadConnection() = default; @@ -56,32 +52,32 @@ protected: }; SchedulerTest(); - ~SchedulerTest() override; - std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; - std::unique_ptr<TestableScheduler> mScheduler; + Hwc2::mock::Display mDisplay; + const scheduler::RefreshRateConfigs mConfigs{{HWC2::Display::Config::Builder(mDisplay, 0) + .setVsyncPeriod(16'666'667) + .setConfigGroup(0) + .build()}, + HwcConfigIndexType(0)}; + + mock::SchedulerCallback mSchedulerCallback; + + // The scheduler should initially disable VSYNC. + struct ExpectDisableVsync { + ExpectDisableVsync(mock::SchedulerCallback& callback) { + EXPECT_CALL(callback, setVsyncEnabled(false)).Times(1); + } + } mExpectDisableVsync{mSchedulerCallback}; + + static constexpr bool kUseContentDetectionV2 = false; + TestableScheduler mScheduler{mConfigs, mSchedulerCallback, kUseContentDetectionV2}; Scheduler::ConnectionHandle mConnectionHandle; mock::EventThread* mEventThread; sp<MockEventThreadConnection> mEventThreadConnection; - Hwc2::mock::Display mDisplay; }; SchedulerTest::SchedulerTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{ - HWC2::Display::Config::Builder(mDisplay, 0) - .setVsyncPeriod(int32_t(16666667)) - .setConfigGroup(0) - .build()}; - mRefreshRateConfigs = std::make_unique< - scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0)); - - mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs, false); - auto eventThread = std::make_unique<mock::EventThread>(); mEventThread = eventThread.get(); EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0)); @@ -93,86 +89,111 @@ SchedulerTest::SchedulerTest() { EXPECT_CALL(*mEventThread, createEventConnection(_, _)) .WillRepeatedly(Return(mEventThreadConnection)); - mConnectionHandle = mScheduler->createConnection(std::move(eventThread)); + mConnectionHandle = mScheduler.createConnection(std::move(eventThread)); EXPECT_TRUE(mConnectionHandle); } -SchedulerTest::~SchedulerTest() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); -} - -namespace { -/* ------------------------------------------------------------------------ - * Test cases - */ +} // namespace TEST_F(SchedulerTest, invalidConnectionHandle) { Scheduler::ConnectionHandle handle; - sp<IDisplayEventConnection> connection; - ASSERT_NO_FATAL_FAILURE( - connection = mScheduler->createDisplayEventConnection(handle, - ISurfaceComposer:: - eConfigChangedSuppress)); + const sp<IDisplayEventConnection> connection = + mScheduler.createDisplayEventConnection(handle, + ISurfaceComposer::eConfigChangedSuppress); + EXPECT_FALSE(connection); - EXPECT_FALSE(mScheduler->getEventConnection(handle)); + EXPECT_FALSE(mScheduler.getEventConnection(handle)); // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads. EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false)); + mScheduler.onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false); EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle)); + mScheduler.onScreenAcquired(handle); EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle)); + mScheduler.onScreenReleased(handle); std::string output; EXPECT_CALL(*mEventThread, dump(_)).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output)); + mScheduler.dump(handle, output); EXPECT_TRUE(output.empty()); - EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10)); + EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(0); + mScheduler.setDuration(handle, 10ns, 20ns); } TEST_F(SchedulerTest, validConnectionHandle) { - sp<IDisplayEventConnection> connection; - ASSERT_NO_FATAL_FAILURE( - connection = mScheduler->createDisplayEventConnection(mConnectionHandle, - ISurfaceComposer:: - eConfigChangedSuppress)); + const sp<IDisplayEventConnection> connection = + mScheduler.createDisplayEventConnection(mConnectionHandle, + ISurfaceComposer::eConfigChangedSuppress); + ASSERT_EQ(mEventThreadConnection, connection); - EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle)); + EXPECT_TRUE(mScheduler.getEventConnection(mConnectionHandle)); EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1); - ASSERT_NO_FATAL_FAILURE( - mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false)); + mScheduler.onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false); EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1); - ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle)); + mScheduler.onScreenAcquired(mConnectionHandle); EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1); - ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle)); + mScheduler.onScreenReleased(mConnectionHandle); std::string output("dump"); EXPECT_CALL(*mEventThread, dump(output)).Times(1); - ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output)); + mScheduler.dump(mConnectionHandle, output); EXPECT_FALSE(output.empty()); - EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1); - ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10)); + EXPECT_CALL(*mEventThread, setDuration(10ns, 20ns)).Times(1); + mScheduler.setDuration(mConnectionHandle, 10ns, 20ns); static constexpr size_t kEventConnections = 5; - ON_CALL(*mEventThread, getEventThreadConnectionCount()) - .WillByDefault(Return(kEventConnections)); - EXPECT_EQ(kEventConnections, mScheduler->getEventThreadConnectionCount(mConnectionHandle)); + EXPECT_CALL(*mEventThread, getEventThreadConnectionCount()).WillOnce(Return(kEventConnections)); + EXPECT_EQ(kEventConnections, mScheduler.getEventThreadConnectionCount(mConnectionHandle)); } -} // namespace -} // namespace android +TEST_F(SchedulerTest, noLayerHistory) { + // Layer history should not be created if there is a single config. + ASSERT_FALSE(mScheduler.hasLayerHistory()); + + TestableSurfaceFlinger flinger; + mock::MockLayer layer(flinger.flinger()); + + // Content detection should be no-op. + mScheduler.registerLayer(&layer); + mScheduler.recordLayerHistory(&layer, 0, LayerHistory::LayerUpdateType::Buffer); + + constexpr bool kPowerStateNormal = true; + mScheduler.setDisplayPowerState(kPowerStateNormal); + + constexpr uint32_t kDisplayArea = 999'999; + mScheduler.onPrimaryDisplayAreaChanged(kDisplayArea); -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion" + EXPECT_CALL(mSchedulerCallback, changeRefreshRate(_, _)).Times(0); + mScheduler.chooseRefreshRateForContent(); +} + +TEST_F(SchedulerTest, testDispatchCachedReportedConfig) { + // If the optional fields are cleared, the function should return before + // onConfigChange is called. + mScheduler.clearOptionalFieldsInFeatures(); + EXPECT_NO_FATAL_FAILURE(mScheduler.dispatchCachedReportedConfig()); + EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0); +} + +TEST_F(SchedulerTest, onNonPrimaryDisplayConfigChanged_invalidParameters) { + HwcConfigIndexType configId = HwcConfigIndexType(111); + nsecs_t vsyncPeriod = 111111; + + // If the handle is incorrect, the function should return before + // onConfigChange is called. + Scheduler::ConnectionHandle invalidHandle = {.id = 123}; + EXPECT_NO_FATAL_FAILURE(mScheduler.onNonPrimaryDisplayConfigChanged(invalidHandle, + PHYSICAL_DISPLAY_ID, + configId, vsyncPeriod)); + EXPECT_CALL(*mEventThread, onConfigChanged(_, _, _)).Times(0); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index 0d6c799763..e25d501fe3 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -32,10 +32,9 @@ #pragma clang diagnostic pop // ignored "-Wconversion" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockDispSync.h" -#include "mock/MockEventControlThread.h" #include "mock/MockEventThread.h" #include "mock/MockMessageQueue.h" +#include "mock/MockVsyncController.h" namespace android { @@ -158,7 +157,11 @@ void SetFrameRateTest::reparentChildren(sp<Layer> parent, sp<Layer> newParent) { void SetFrameRateTest::commitTransaction() { for (auto layer : mLayers) { - layer.get()->commitTransaction(layer.get()->getCurrentState()); + layer->pushPendingState(); + auto c = layer->getCurrentState(); + if (layer->applyPendingStates(&c)) { + layer->commitTransaction(c); + } } } @@ -168,23 +171,25 @@ void SetFrameRateTest::setupScheduler() { EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) - .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); - auto primaryDispSync = std::make_unique<mock::DispSync>(); + auto vsyncController = std::make_unique<mock::VsyncController>(); + auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); - EXPECT_CALL(*primaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0)); - EXPECT_CALL(*primaryDispSync, getPeriod()) + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); - EXPECT_CALL(*primaryDispSync, expectedPresentTime(_)).WillRepeatedly(Return(0)); - mFlinger.setupScheduler(std::move(primaryDispSync), - std::make_unique<mock::EventControlThread>(), std::move(eventThread), - std::move(sfEventThread)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), + std::move(eventThread), std::move(sfEventThread)); } void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp new file mode 100644 index 0000000000..2362a31827 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +namespace android { +namespace { + +class CreateDisplayTest : public DisplayTransactionTest {}; + +TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) { + const String8 name("virtual.test"); + + // -------------------------------------------------------------------- + // Call Expectations + + // The call should notify the interceptor that a display was created. + EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); + + // -------------------------------------------------------------------- + // Invocation + + sp<IBinder> displayToken = mFlinger.createDisplay(name, false); + + // -------------------------------------------------------------------- + // Postconditions + + // The display should have been added to the current state + ASSERT_TRUE(hasCurrentDisplayState(displayToken)); + const auto& display = getCurrentDisplayState(displayToken); + EXPECT_TRUE(display.isVirtual()); + EXPECT_FALSE(display.isSecure); + EXPECT_EQ(name.string(), display.displayName); + + // -------------------------------------------------------------------- + // Cleanup conditions + + // Destroying the display invalidates the display state. + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); +} + +TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) { + const String8 name("virtual.test"); + + // -------------------------------------------------------------------- + // Call Expectations + + // The call should notify the interceptor that a display was created. + EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); + + // -------------------------------------------------------------------- + // Invocation + int64_t oldId = IPCThreadState::self()->clearCallingIdentity(); + // Set the calling identity to graphics so captureDisplay with secure is allowed. + IPCThreadState::self()->restoreCallingIdentity(static_cast<int64_t>(AID_GRAPHICS) << 32 | + AID_GRAPHICS); + sp<IBinder> displayToken = mFlinger.createDisplay(name, true); + IPCThreadState::self()->restoreCallingIdentity(oldId); + + // -------------------------------------------------------------------- + // Postconditions + + // The display should have been added to the current state + ASSERT_TRUE(hasCurrentDisplayState(displayToken)); + const auto& display = getCurrentDisplayState(displayToken); + EXPECT_TRUE(display.isVirtual()); + EXPECT_TRUE(display.isSecure); + EXPECT_EQ(name.string(), display.displayName); + + // -------------------------------------------------------------------- + // Cleanup conditions + + // Destroying the display invalidates the display state. + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); +} + +} // namespace +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp new file mode 100644 index 0000000000..061443479e --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +namespace android { +namespace { + +class DestroyDisplayTest : public DisplayTransactionTest {}; + +TEST_F(DestroyDisplayTest, destroyDisplayClearsCurrentStateForDisplay) { + using Case = NonHwcVirtualDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A virtual display exists + auto existing = Case::Display::makeFakeExistingDisplayInjector(this); + existing.inject(); + + // -------------------------------------------------------------------- + // Call Expectations + + // The call should notify the interceptor that a display was created. + EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); + + // Destroying the display invalidates the display state. + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.destroyDisplay(existing.token()); + + // -------------------------------------------------------------------- + // Postconditions + + // The display should have been removed from the current state + EXPECT_FALSE(hasCurrentDisplayState(existing.token())); + + // Ths display should still exist in the drawing state + EXPECT_TRUE(hasDrawingDisplayState(existing.token())); + + // The display transaction needed flasg should be set + EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); +} + +TEST_F(DestroyDisplayTest, destroyDisplayHandlesUnknownDisplay) { + // -------------------------------------------------------------------- + // Preconditions + + sp<BBinder> displayToken = new BBinder(); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.destroyDisplay(displayToken); +} + +} // namespace +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp new file mode 100644 index 0000000000..0171f1b41d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayNativePrimariesTest.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android { +namespace { + +class GetDisplayNativePrimaries : public DisplayTransactionTest { +public: + GetDisplayNativePrimaries(); + void populateDummyDisplayNativePrimaries(ui::DisplayPrimaries& primaries); + void checkDummyDisplayNativePrimaries(const ui::DisplayPrimaries& primaries); + +private: + static constexpr float mStartingTestValue = 1.0f; +}; + +GetDisplayNativePrimaries::GetDisplayNativePrimaries() { + SimplePrimaryDisplayCase::Display::injectHwcDisplay(this); + injectFakeNativeWindowSurfaceFactory(); +} + +void GetDisplayNativePrimaries::populateDummyDisplayNativePrimaries( + ui::DisplayPrimaries& primaries) { + float startingVal = mStartingTestValue; + primaries.red.X = startingVal++; + primaries.red.Y = startingVal++; + primaries.red.Z = startingVal++; + primaries.green.X = startingVal++; + primaries.green.Y = startingVal++; + primaries.green.Z = startingVal++; + primaries.blue.X = startingVal++; + primaries.blue.Y = startingVal++; + primaries.blue.Z = startingVal++; + primaries.white.X = startingVal++; + primaries.white.Y = startingVal++; + primaries.white.Z = startingVal++; +} + +void GetDisplayNativePrimaries::checkDummyDisplayNativePrimaries( + const ui::DisplayPrimaries& primaries) { + float startingVal = mStartingTestValue; + EXPECT_EQ(primaries.red.X, startingVal++); + EXPECT_EQ(primaries.red.Y, startingVal++); + EXPECT_EQ(primaries.red.Z, startingVal++); + EXPECT_EQ(primaries.green.X, startingVal++); + EXPECT_EQ(primaries.green.Y, startingVal++); + EXPECT_EQ(primaries.green.Z, startingVal++); + EXPECT_EQ(primaries.blue.X, startingVal++); + EXPECT_EQ(primaries.blue.Y, startingVal++); + EXPECT_EQ(primaries.blue.Z, startingVal++); + EXPECT_EQ(primaries.white.X, startingVal++); + EXPECT_EQ(primaries.white.Y, startingVal++); + EXPECT_EQ(primaries.white.Z, startingVal++); +} + +TEST_F(GetDisplayNativePrimaries, nullDisplayToken) { + ui::DisplayPrimaries primaries; + EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries)); +} + +TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) { + auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this); + injector.inject(); + auto internalDisplayToken = injector.token(); + + ui::DisplayPrimaries expectedPrimaries; + populateDummyDisplayNativePrimaries(expectedPrimaries); + mFlinger.setInternalDisplayPrimaries(expectedPrimaries); + + ui::DisplayPrimaries primaries; + EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries)); + + checkDummyDisplayNativePrimaries(primaries); +} + +TEST_F(GetDisplayNativePrimaries, notInternalDisplayToken) { + sp<BBinder> notInternalDisplayToken = new BBinder(); + + ui::DisplayPrimaries primaries; + populateDummyDisplayNativePrimaries(primaries); + EXPECT_EQ(NAME_NOT_FOUND, + mFlinger.getDisplayNativePrimaries(notInternalDisplayToken, primaries)); + + // Check primaries argument wasn't modified in case of failure + checkDummyDisplayNativePrimaries(primaries); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp new file mode 100644 index 0000000000..cd3f6ab4bf --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp @@ -0,0 +1,734 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +namespace android { +namespace { + +class HandleTransactionLockedTest : public DisplayTransactionTest { +public: + template <typename Case> + void setupCommonPreconditions(); + + template <typename Case, bool connected> + static void expectHotplugReceived(mock::EventThread*); + + template <typename Case> + void setupCommonCallExpectationsForConnectProcessing(); + + template <typename Case> + void setupCommonCallExpectationsForDisconnectProcessing(); + + template <typename Case> + void processesHotplugConnectCommon(); + + template <typename Case> + void ignoresHotplugConnectCommon(); + + template <typename Case> + void processesHotplugDisconnectCommon(); + + template <typename Case> + void verifyDisplayIsConnected(const sp<IBinder>& displayToken); + + template <typename Case> + void verifyPhysicalDisplayIsConnected(); + + void verifyDisplayIsNotConnected(const sp<IBinder>& displayToken); +}; + +template <typename Case> +void HandleTransactionLockedTest::setupCommonPreconditions() { + // Wide color displays support is configured appropriately + Case::WideColorSupport::injectConfigChange(this); + + // SurfaceFlinger will use a test-controlled factory for BufferQueues + injectFakeBufferQueueFactory(); + + // SurfaceFlinger will use a test-controlled factory for native window + // surfaces. + injectFakeNativeWindowSurfaceFactory(); +} + +template <typename Case, bool connected> +void HandleTransactionLockedTest::expectHotplugReceived(mock::EventThread* eventThread) { + const auto convert = [](auto physicalDisplayId) { + return std::make_optional(DisplayId{physicalDisplayId}); + }; + + EXPECT_CALL(*eventThread, + onHotplugReceived(ResultOf(convert, Case::Display::DISPLAY_ID::get()), connected)) + .Times(1); +} + +template <typename Case> +void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessing() { + Case::Display::setupHwcHotplugCallExpectations(this); + + Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this); + Case::Display::setupFramebufferProducerBufferQueueCallExpectations(this); + Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this); + Case::Display::setupHwcGetActiveConfigCallExpectations(this); + + Case::WideColorSupport::setupComposerCallExpectations(this); + Case::HdrSupport::setupComposerCallExpectations(this); + Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); + + EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); + expectHotplugReceived<Case, true>(mEventThread); + expectHotplugReceived<Case, true>(mSFEventThread); +} + +template <typename Case> +void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() { + EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1); + + expectHotplugReceived<Case, false>(mEventThread); + expectHotplugReceived<Case, false>(mSFEventThread); +} + +template <typename Case> +void HandleTransactionLockedTest::verifyDisplayIsConnected(const sp<IBinder>& displayToken) { + // The display device should have been set up in the list of displays. + ASSERT_TRUE(hasDisplayDevice(displayToken)); + const auto& device = getDisplayDevice(displayToken); + EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure()); + EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary()); + + std::optional<DisplayDeviceState::Physical> expectedPhysical; + if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) { + const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(displayId); + const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; + ASSERT_TRUE(hwcDisplayId); + expectedPhysical = {.id = *displayId, + .type = *connectionType, + .hwcDisplayId = *hwcDisplayId}; + } + + // The display should have been set up in the current display state + ASSERT_TRUE(hasCurrentDisplayState(displayToken)); + const auto& current = getCurrentDisplayState(displayToken); + EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), current.isVirtual()); + EXPECT_EQ(expectedPhysical, current.physical); + + // The display should have been set up in the drawing display state + ASSERT_TRUE(hasDrawingDisplayState(displayToken)); + const auto& draw = getDrawingDisplayState(displayToken); + EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual()); + EXPECT_EQ(expectedPhysical, draw.physical); +} + +template <typename Case> +void HandleTransactionLockedTest::verifyPhysicalDisplayIsConnected() { + // HWComposer should have an entry for the display + EXPECT_TRUE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); + + // SF should have a display token. + const auto displayId = Case::Display::DISPLAY_ID::get(); + ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); + ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1); + auto& displayToken = mFlinger.mutablePhysicalDisplayTokens()[displayId]; + + verifyDisplayIsConnected<Case>(displayToken); +} + +void HandleTransactionLockedTest::verifyDisplayIsNotConnected(const sp<IBinder>& displayToken) { + EXPECT_FALSE(hasDisplayDevice(displayToken)); + EXPECT_FALSE(hasCurrentDisplayState(displayToken)); + EXPECT_FALSE(hasDrawingDisplayState(displayToken)); +} + +template <typename Case> +void HandleTransactionLockedTest::processesHotplugConnectCommon() { + // -------------------------------------------------------------------- + // Preconditions + + setupCommonPreconditions<Case>(); + + // A hotplug connect event is enqueued for a display + Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + + // -------------------------------------------------------------------- + // Call Expectations + + setupCommonCallExpectationsForConnectProcessing<Case>(); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + verifyPhysicalDisplayIsConnected<Case>(); + + // -------------------------------------------------------------------- + // Cleanup conditions + + EXPECT_CALL(*mComposer, + setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) + .WillOnce(Return(Error::NONE)); + EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); +} + +template <typename Case> +void HandleTransactionLockedTest::ignoresHotplugConnectCommon() { + // -------------------------------------------------------------------- + // Preconditions + + setupCommonPreconditions<Case>(); + + // A hotplug connect event is enqueued for a display + Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + // HWComposer should not have an entry for the display + EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); +} + +template <typename Case> +void HandleTransactionLockedTest::processesHotplugDisconnectCommon() { + // -------------------------------------------------------------------- + // Preconditions + + setupCommonPreconditions<Case>(); + + // A hotplug disconnect event is enqueued for a display + Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + + // The display is already completely set up. + Case::Display::injectHwcDisplay(this); + auto existing = Case::Display::makeFakeExistingDisplayInjector(this); + existing.inject(); + + // -------------------------------------------------------------------- + // Call Expectations + + EXPECT_CALL(*mComposer, getDisplayIdentificationData(Case::Display::HWC_DISPLAY_ID, _, _)) + .Times(0); + + setupCommonCallExpectationsForDisconnectProcessing<Case>(); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + // HWComposer should not have an entry for the display + EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); + + // SF should not have a display token. + const auto displayId = Case::Display::DISPLAY_ID::get(); + ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); + ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0); + + // The existing token should have been removed + verifyDisplayIsNotConnected(existing.token()); +} + +TEST_F(HandleTransactionLockedTest, processesHotplugConnectPrimaryDisplay) { + processesHotplugConnectCommon<SimplePrimaryDisplayCase>(); +} + +TEST_F(HandleTransactionLockedTest, + processesHotplugConnectPrimaryDisplayWithExternalAlreadyConnected) { + // Inject an external display. + ExternalDisplayVariant::injectHwcDisplay(this); + + processesHotplugConnectCommon<SimplePrimaryDisplayCase>(); +} + +TEST_F(HandleTransactionLockedTest, processesHotplugConnectExternalDisplay) { + // Inject a primary display. + PrimaryDisplayVariant::injectHwcDisplay(this); + + processesHotplugConnectCommon<SimpleExternalDisplayCase>(); +} + +TEST_F(HandleTransactionLockedTest, ignoresHotplugConnectIfPrimaryAndExternalAlreadyConnected) { + // Inject both a primary and external display. + PrimaryDisplayVariant::injectHwcDisplay(this); + ExternalDisplayVariant::injectHwcDisplay(this); + + // TODO: This is an unnecessary call. + EXPECT_CALL(*mComposer, + getDisplayIdentificationData(TertiaryDisplayVariant::HWC_DISPLAY_ID, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(TertiaryDisplay::PORT), + SetArgPointee<2>(TertiaryDisplay::GET_IDENTIFICATION_DATA()), + Return(Error::NONE))); + + ignoresHotplugConnectCommon<SimpleTertiaryDisplayCase>(); +} + +TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectPrimaryDisplay) { + processesHotplugDisconnectCommon<SimplePrimaryDisplayCase>(); +} + +TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectExternalDisplay) { + processesHotplugDisconnectCommon<SimpleExternalDisplayCase>(); +} + +TEST_F(HandleTransactionLockedTest, processesHotplugConnectThenDisconnectPrimary) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + setupCommonPreconditions<Case>(); + + // A hotplug connect event is enqueued for a display + Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + // A hotplug disconnect event is also enqueued for the same display + Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + + // -------------------------------------------------------------------- + // Call Expectations + + setupCommonCallExpectationsForConnectProcessing<Case>(); + setupCommonCallExpectationsForDisconnectProcessing<Case>(); + + EXPECT_CALL(*mComposer, + setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) + .WillOnce(Return(Error::NONE)); + EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + // HWComposer should not have an entry for the display + EXPECT_FALSE(hasPhysicalHwcDisplay(Case::Display::HWC_DISPLAY_ID)); + + // SF should not have a display token. + const auto displayId = Case::Display::DISPLAY_ID::get(); + ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); + ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 0); +} + +TEST_F(HandleTransactionLockedTest, processesHotplugDisconnectThenConnectPrimary) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + setupCommonPreconditions<Case>(); + + // The display is already completely set up. + Case::Display::injectHwcDisplay(this); + auto existing = Case::Display::makeFakeExistingDisplayInjector(this); + existing.inject(); + + // A hotplug disconnect event is enqueued for a display + Case::Display::injectPendingHotplugEvent(this, Connection::DISCONNECTED); + // A hotplug connect event is also enqueued for the same display + Case::Display::injectPendingHotplugEvent(this, Connection::CONNECTED); + + // -------------------------------------------------------------------- + // Call Expectations + + setupCommonCallExpectationsForConnectProcessing<Case>(); + setupCommonCallExpectationsForDisconnectProcessing<Case>(); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + // The existing token should have been removed + verifyDisplayIsNotConnected(existing.token()); + const auto displayId = Case::Display::DISPLAY_ID::get(); + ASSERT_TRUE(PhysicalDisplayId::tryCast(displayId)); + ASSERT_TRUE(mFlinger.mutablePhysicalDisplayTokens().count(displayId) == 1); + EXPECT_NE(existing.token(), mFlinger.mutablePhysicalDisplayTokens()[displayId]); + + // A new display should be connected in its place + + verifyPhysicalDisplayIsConnected<Case>(); + + // -------------------------------------------------------------------- + // Cleanup conditions + + EXPECT_CALL(*mComposer, + setVsyncEnabled(Case::Display::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE)) + .WillOnce(Return(Error::NONE)); + EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); +} + +TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { + using Case = HwcVirtualDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // The HWC supports at least one virtual display + injectMockComposer(1); + + setupCommonPreconditions<Case>(); + + // A virtual display was added to the current state, and it has a + // surface(producer) + sp<BBinder> displayToken = new BBinder(); + + DisplayDeviceState state; + state.isSecure = static_cast<bool>(Case::Display::SECURE); + + sp<mock::GraphicBufferProducer> surface{new mock::GraphicBufferProducer()}; + state.surface = surface; + mFlinger.mutableCurrentState().displays.add(displayToken, state); + + // -------------------------------------------------------------------- + // Call Expectations + + Case::Display::setupFramebufferConsumerBufferQueueCallExpectations(this); + Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this); + + EXPECT_CALL(*surface, query(NATIVE_WINDOW_WIDTH, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::WIDTH), Return(NO_ERROR))); + EXPECT_CALL(*surface, query(NATIVE_WINDOW_HEIGHT, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(Case::Display::HEIGHT), Return(NO_ERROR))); + EXPECT_CALL(*surface, query(NATIVE_WINDOW_FORMAT, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_VIRTUAL_DISPLAY_SURFACE_FORMAT), + Return(NO_ERROR))); + EXPECT_CALL(*surface, query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(NO_ERROR))); + + EXPECT_CALL(*surface, setAsyncMode(true)).Times(1); + + EXPECT_CALL(*mProducer, connect(_, NATIVE_WINDOW_API_EGL, false, _)).Times(1); + EXPECT_CALL(*mProducer, disconnect(_, _)).Times(1); + + Case::Display::setupHwcVirtualDisplayCreationCallExpectations(this); + Case::WideColorSupport::setupComposerCallExpectations(this); + Case::HdrSupport::setupComposerCallExpectations(this); + Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + // The display device should have been set up in the list of displays. + verifyDisplayIsConnected<Case>(displayToken); + + // -------------------------------------------------------------------- + // Cleanup conditions + + EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID)) + .WillOnce(Return(Error::NONE)); + EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); + + // Cleanup + mFlinger.mutableCurrentState().displays.removeItem(displayToken); + mFlinger.mutableDrawingState().displays.removeItem(displayToken); +} + +TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { + using Case = HwcVirtualDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // The HWC supports at least one virtual display + injectMockComposer(1); + + setupCommonPreconditions<Case>(); + + // A virtual display was added to the current state, but it does not have a + // surface. + sp<BBinder> displayToken = new BBinder(); + + DisplayDeviceState state; + state.isSecure = static_cast<bool>(Case::Display::SECURE); + + mFlinger.mutableCurrentState().displays.add(displayToken, state); + + // -------------------------------------------------------------------- + // Call Expectations + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + // There will not be a display device set up. + EXPECT_FALSE(hasDisplayDevice(displayToken)); + + // The drawing display state will be set from the current display state. + ASSERT_TRUE(hasDrawingDisplayState(displayToken)); + const auto& draw = getDrawingDisplayState(displayToken); + EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), draw.isVirtual()); +} + +TEST_F(HandleTransactionLockedTest, processesVirtualDisplayRemoval) { + using Case = HwcVirtualDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A virtual display is set up but is removed from the current state. + const auto displayId = Case::Display::DISPLAY_ID::get(); + ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId)); + mFlinger.mutableHwcDisplayData().try_emplace(displayId); + Case::Display::injectHwcDisplay(this); + auto existing = Case::Display::makeFakeExistingDisplayInjector(this); + existing.inject(); + mFlinger.mutableCurrentState().displays.removeItem(existing.token()); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + // The existing token should have been removed + verifyDisplayIsNotConnected(existing.token()); +} + +TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackChanges) { + using Case = NonHwcVirtualDisplayCase; + + constexpr uint32_t oldLayerStack = 0u; + constexpr uint32_t newLayerStack = 123u; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // There is a change to the layerStack state + display.mutableDrawingDisplayState().layerStack = oldLayerStack; + display.mutableCurrentDisplayState().layerStack = newLayerStack; + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + EXPECT_EQ(newLayerStack, display.mutableDisplayDevice()->getLayerStack()); +} + +TEST_F(HandleTransactionLockedTest, processesDisplayTransformChanges) { + using Case = NonHwcVirtualDisplayCase; + + constexpr ui::Rotation oldTransform = ui::ROTATION_0; + constexpr ui::Rotation newTransform = ui::ROTATION_180; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // There is a change to the orientation state + display.mutableDrawingDisplayState().orientation = oldTransform; + display.mutableCurrentDisplayState().orientation = newTransform; + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + EXPECT_EQ(newTransform, display.mutableDisplayDevice()->getOrientation()); +} + +TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) { + using Case = NonHwcVirtualDisplayCase; + + const Rect oldLayerStackRect(0, 0, 0, 0); + const Rect newLayerStackRect(0, 0, 123, 456); + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // There is a change to the layerStackSpaceRect state + display.mutableDrawingDisplayState().layerStackSpaceRect = oldLayerStackRect; + display.mutableCurrentDisplayState().layerStackSpaceRect = newLayerStackRect; + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect()); +} + +TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) { + using Case = NonHwcVirtualDisplayCase; + + const Rect oldFrame(0, 0, 0, 0); + const Rect newFrame(0, 0, 123, 456); + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // There is a change to the layerStackSpaceRect state + display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame; + display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame; + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + // -------------------------------------------------------------------- + // Postconditions + + EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect()); +} + +TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { + using Case = NonHwcVirtualDisplayCase; + + constexpr int oldWidth = 0; + constexpr int oldHeight = 10; + constexpr int newWidth = 123; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto nativeWindow = new mock::NativeWindow(); + auto displaySurface = new compositionengine::mock::DisplaySurface(); + sp<GraphicBuffer> buf = new GraphicBuffer(); + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.setNativeWindow(nativeWindow); + display.setDisplaySurface(displaySurface); + // Setup injection expectations + EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _)) + .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0))); + EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) + .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0))); + EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1); + EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1); + EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1); + EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1); + display.inject(); + + // There is a change to the layerStackSpaceRect state + display.mutableDrawingDisplayState().width = oldWidth; + display.mutableDrawingDisplayState().height = oldHeight; + display.mutableCurrentDisplayState().width = newWidth; + display.mutableCurrentDisplayState().height = oldHeight; + + // -------------------------------------------------------------------- + // Call Expectations + + EXPECT_CALL(*displaySurface, resizeBuffers(newWidth, oldHeight)).Times(1); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); +} + +TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { + using Case = NonHwcVirtualDisplayCase; + + constexpr int oldWidth = 0; + constexpr int oldHeight = 10; + constexpr int newHeight = 123; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto nativeWindow = new mock::NativeWindow(); + auto displaySurface = new compositionengine::mock::DisplaySurface(); + sp<GraphicBuffer> buf = new GraphicBuffer(); + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.setNativeWindow(nativeWindow); + display.setDisplaySurface(displaySurface); + // Setup injection expectations + EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _)) + .WillOnce(DoAll(SetArgPointee<1>(oldWidth), Return(0))); + EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) + .WillOnce(DoAll(SetArgPointee<1>(oldHeight), Return(0))); + EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1); + EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1); + EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1); + EXPECT_CALL(*nativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1); + display.inject(); + + // There is a change to the layerStackSpaceRect state + display.mutableDrawingDisplayState().width = oldWidth; + display.mutableDrawingDisplayState().height = oldHeight; + display.mutableCurrentDisplayState().width = oldWidth; + display.mutableCurrentDisplayState().height = newHeight; + + // -------------------------------------------------------------------- + // Call Expectations + + EXPECT_CALL(*displaySurface, resizeBuffers(oldWidth, newHeight)).Times(1); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp new file mode 100644 index 0000000000..69e0501bd6 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +#include <android/hardware/power/Boost.h> + +namespace android { +namespace { + +using android::hardware::power::Boost; + +TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) { + mFlinger.scheduler()->replaceTouchTimer(100); + std::this_thread::sleep_for(10ms); // wait for callback to be triggered + EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch + + std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + + EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT))); + std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + + std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback + EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); + + EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION))); + std::this_thread::sleep_for(10ms); // wait for callback to be triggered. + EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); +} + +} // namespace +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp new file mode 100644 index 0000000000..42f4cf31ba --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnHotplugReceivedTest.cpp @@ -0,0 +1,158 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +namespace android { +namespace { + +class OnHotplugReceivedTest : public DisplayTransactionTest {}; + +TEST_F(OnHotplugReceivedTest, hotplugEnqueuesEventsForDisplayTransaction) { + constexpr int currentSequenceId = 123; + constexpr HWDisplayId hwcDisplayId1 = 456; + constexpr HWDisplayId hwcDisplayId2 = 654; + + // -------------------------------------------------------------------- + // Preconditions + + // Set the current sequence id for accepted events + mFlinger.mutableComposerSequenceId() = currentSequenceId; + + // Set the main thread id so that the current thread does not appear to be + // the main thread. + mFlinger.mutableMainThreadId() = std::thread::id(); + + // -------------------------------------------------------------------- + // Call Expectations + + // We expect invalidate() to be invoked once to trigger display transaction + // processing. + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); + + // -------------------------------------------------------------------- + // Invocation + + // Simulate two hotplug events (a connect and a disconnect) + mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId1, Connection::CONNECTED); + mFlinger.onHotplugReceived(currentSequenceId, hwcDisplayId2, Connection::DISCONNECTED); + + // -------------------------------------------------------------------- + // Postconditions + + // The display transaction needed flag should be set. + EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); + + // All events should be in the pending event queue. + const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents(); + ASSERT_EQ(2u, pendingEvents.size()); + EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId); + EXPECT_EQ(Connection::CONNECTED, pendingEvents[0].connection); + EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId); + EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection); +} + +TEST_F(OnHotplugReceivedTest, hotplugDiscardsUnexpectedEvents) { + constexpr int currentSequenceId = 123; + constexpr int otherSequenceId = 321; + constexpr HWDisplayId displayId = 456; + + // -------------------------------------------------------------------- + // Preconditions + + // Set the current sequence id for accepted events + mFlinger.mutableComposerSequenceId() = currentSequenceId; + + // Set the main thread id so that the current thread does not appear to be + // the main thread. + mFlinger.mutableMainThreadId() = std::thread::id(); + + // -------------------------------------------------------------------- + // Call Expectations + + // We do not expect any calls to invalidate(). + EXPECT_CALL(*mMessageQueue, invalidate()).Times(0); + + // -------------------------------------------------------------------- + // Invocation + + // Call with an unexpected sequence id + mFlinger.onHotplugReceived(otherSequenceId, displayId, Connection::INVALID); + + // -------------------------------------------------------------------- + // Postconditions + + // The display transaction needed flag should not be set + EXPECT_FALSE(hasTransactionFlagSet(eDisplayTransactionNeeded)); + + // There should be no pending events + EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty()); +} + +TEST_F(OnHotplugReceivedTest, hotplugProcessesEnqueuedEventsIfCalledOnMainThread) { + constexpr int currentSequenceId = 123; + constexpr HWDisplayId displayId1 = 456; + + // -------------------------------------------------------------------- + // Note: + // -------------------------------------------------------------------- + // This test case is a bit tricky. We want to verify that + // onHotplugReceived() calls processDisplayHotplugEventsLocked(), but we + // don't really want to provide coverage for everything the later function + // does as there are specific tests for it. + // -------------------------------------------------------------------- + + // -------------------------------------------------------------------- + // Preconditions + + // Set the current sequence id for accepted events + mFlinger.mutableComposerSequenceId() = currentSequenceId; + + // Set the main thread id so that the current thread does appear to be the + // main thread. + mFlinger.mutableMainThreadId() = std::this_thread::get_id(); + + // -------------------------------------------------------------------- + // Call Expectations + + // We expect invalidate() to be invoked once to trigger display transaction + // processing. + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); + + // -------------------------------------------------------------------- + // Invocation + + // Simulate a disconnect on a display id that is not connected. This should + // be enqueued by onHotplugReceived(), and dequeued by + // processDisplayHotplugEventsLocked(), but then ignored as invalid. + mFlinger.onHotplugReceived(currentSequenceId, displayId1, Connection::DISCONNECTED); + + // -------------------------------------------------------------------- + // Postconditions + + // The display transaction needed flag should be set. + EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); + + // There should be no event queued on return, as it should have been + // processed. + EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty()); +} + +} // namespace +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp new file mode 100644 index 0000000000..7a9403b5c0 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +namespace android { +namespace { + +class OnInitializeDisplaysTest : public DisplayTransactionTest {}; + +TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A primary display is set up + Case::Display::injectHwcDisplay(this); + auto primaryDisplay = Case::Display::makeFakeExistingDisplayInjector(this); + primaryDisplay.inject(); + + // -------------------------------------------------------------------- + // Call Expectations + + // We expect the surface interceptor to possibly be used, but we treat it as + // disabled since it is called as a side effect rather than directly by this + // function. + EXPECT_CALL(*mSurfaceInterceptor, isEnabled()).WillOnce(Return(false)); + + // We expect a call to get the active display config. + Case::Display::setupHwcGetActiveConfigCallExpectations(this); + + // We expect invalidate() to be invoked once to trigger display transaction + // processing. + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); + + EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.onInitializeDisplays(); + + // -------------------------------------------------------------------- + // Postconditions + + // The primary display should have a current state + ASSERT_TRUE(hasCurrentDisplayState(primaryDisplay.token())); + const auto& primaryDisplayState = getCurrentDisplayState(primaryDisplay.token()); + // The layer stack state should be set to zero + EXPECT_EQ(0u, primaryDisplayState.layerStack); + // The orientation state should be set to zero + EXPECT_EQ(ui::ROTATION_0, primaryDisplayState.orientation); + + // The orientedDisplaySpaceRect state should be set to INVALID + EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.orientedDisplaySpaceRect); + + // The layerStackSpaceRect state should be set to INVALID + EXPECT_EQ(Rect::INVALID_RECT, primaryDisplayState.layerStackSpaceRect); + + // The width and height should both be zero + EXPECT_EQ(0u, primaryDisplayState.width); + EXPECT_EQ(0u, primaryDisplayState.height); + + // The display should be set to PowerMode::ON + ASSERT_TRUE(hasDisplayDevice(primaryDisplay.token())); + auto displayDevice = primaryDisplay.mutableDisplayDevice(); + EXPECT_EQ(PowerMode::ON, displayDevice->getPowerMode()); + + // The display refresh period should be set in the orientedDisplaySpaceRect tracker. + FrameStats stats; + mFlinger.getAnimFrameTracker().getStats(&stats); + EXPECT_EQ(DEFAULT_REFRESH_RATE, stats.refreshPeriodNano); + + // The display transaction needed flag should be set. + EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded)); + + // The compositor timing should be set to default values + const auto& compositorTiming = mFlinger.getCompositorTiming(); + EXPECT_EQ(-DEFAULT_REFRESH_RATE, compositorTiming.deadline); + EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.interval); + EXPECT_EQ(DEFAULT_REFRESH_RATE, compositorTiming.presentLatency); +} + +} // namespace +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp new file mode 100644 index 0000000000..be019848fb --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetDisplayStateTest.cpp @@ -0,0 +1,493 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android { +namespace { + +class SetDisplayStateLockedTest : public DisplayTransactionTest {}; + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWithUnknownDisplay) { + // -------------------------------------------------------------------- + // Preconditions + + // We have an unknown display token not associated with a known display + sp<BBinder> displayToken = new BBinder(); + + // The requested display state references the unknown display. + DisplayState state; + state.what = DisplayState::eLayerStackChanged; + state.token = displayToken; + state.layerStack = 456; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags are empty + EXPECT_EQ(0u, flags); + + // The display token still doesn't match anything known. + EXPECT_FALSE(hasCurrentDisplayState(displayToken)); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingWhenNoChanges) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is already set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // No changes are made to the display + DisplayState state; + state.what = 0; + state.token = display.token(); + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags are empty + EXPECT_EQ(0u, flags); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSurfaceDidNotChange) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is already set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // There is a surface that can be set. + sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer(); + + // The current display state has the surface set + display.mutableCurrentDisplayState().surface = surface; + + // The incoming request sets the same surface + DisplayState state; + state.what = DisplayState::eSurfaceChanged; + state.token = display.token(); + state.surface = surface; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags are empty + EXPECT_EQ(0u, flags); + + // The current display state is unchanged. + EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get()); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfSurfaceChanged) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is already set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // There is a surface that can be set. + sp<mock::GraphicBufferProducer> surface = new mock::GraphicBufferProducer(); + + // The current display state does not have a surface + display.mutableCurrentDisplayState().surface = nullptr; + + // The incoming request sets a surface + DisplayState state; + state.what = DisplayState::eSurfaceChanged; + state.token = display.token(); + state.surface = surface; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags indicate a transaction is needed + EXPECT_EQ(eDisplayTransactionNeeded, flags); + + // The current display layer stack state is set to the new value + EXPECT_EQ(surface.get(), display.getCurrentDisplayState().surface.get()); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfLayerStackDidNotChange) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is already set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display has a layer stack set + display.mutableCurrentDisplayState().layerStack = 456u; + + // The incoming request sets the same layer stack + DisplayState state; + state.what = DisplayState::eLayerStackChanged; + state.token = display.token(); + state.layerStack = 456u; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags are empty + EXPECT_EQ(0u, flags); + + // The current display state is unchanged + EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackChanged) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display has a layer stack set + display.mutableCurrentDisplayState().layerStack = 654u; + + // The incoming request sets a different layer stack + DisplayState state; + state.what = DisplayState::eLayerStackChanged; + state.token = display.token(); + state.layerStack = 456u; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags indicate a transaction is needed + EXPECT_EQ(eDisplayTransactionNeeded, flags); + + // The desired display state has been set to the new value. + EXPECT_EQ(456u, display.getCurrentDisplayState().layerStack); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfProjectionDidNotChange) { + using Case = SimplePrimaryDisplayCase; + constexpr ui::Rotation initialOrientation = ui::ROTATION_180; + const Rect initialOrientedDisplayRect = {1, 2, 3, 4}; + const Rect initialLayerStackRect = {5, 6, 7, 8}; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The current display state projection state is all set + display.mutableCurrentDisplayState().orientation = initialOrientation; + display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect; + display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect; + + // The incoming request sets the same projection state + DisplayState state; + state.what = DisplayState::eDisplayProjectionChanged; + state.token = display.token(); + state.orientation = initialOrientation; + state.orientedDisplaySpaceRect = initialOrientedDisplayRect; + state.layerStackSpaceRect = initialLayerStackRect; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags are empty + EXPECT_EQ(0u, flags); + + // The current display state is unchanged + EXPECT_EQ(initialOrientation, display.getCurrentDisplayState().orientation); + + EXPECT_EQ(initialOrientedDisplayRect, + display.getCurrentDisplayState().orientedDisplaySpaceRect); + EXPECT_EQ(initialLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfOrientationChanged) { + using Case = SimplePrimaryDisplayCase; + constexpr ui::Rotation initialOrientation = ui::ROTATION_90; + constexpr ui::Rotation desiredOrientation = ui::ROTATION_180; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The current display state has an orientation set + display.mutableCurrentDisplayState().orientation = initialOrientation; + + // The incoming request sets a different orientation + DisplayState state; + state.what = DisplayState::eDisplayProjectionChanged; + state.token = display.token(); + state.orientation = desiredOrientation; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags indicate a transaction is needed + EXPECT_EQ(eDisplayTransactionNeeded, flags); + + // The current display state has the new value. + EXPECT_EQ(desiredOrientation, display.getCurrentDisplayState().orientation); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfFrameChanged) { + using Case = SimplePrimaryDisplayCase; + const Rect initialOrientedDisplayRect = {0, 0, 0, 0}; + const Rect desiredOrientedDisplayRect = {5, 6, 7, 8}; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The current display state does not have a orientedDisplaySpaceRect + display.mutableCurrentDisplayState().orientedDisplaySpaceRect = initialOrientedDisplayRect; + + // The incoming request sets a orientedDisplaySpaceRect + DisplayState state; + state.what = DisplayState::eDisplayProjectionChanged; + state.token = display.token(); + state.orientedDisplaySpaceRect = desiredOrientedDisplayRect; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags indicate a transaction is needed + EXPECT_EQ(eDisplayTransactionNeeded, flags); + + // The current display state has the new value. + EXPECT_EQ(desiredOrientedDisplayRect, + display.getCurrentDisplayState().orientedDisplaySpaceRect); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfLayerStackRectChanged) { + using Case = SimplePrimaryDisplayCase; + const Rect initialLayerStackRect = {0, 0, 0, 0}; + const Rect desiredLayerStackRect = {5, 6, 7, 8}; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The current display state does not have a layerStackSpaceRect + display.mutableCurrentDisplayState().layerStackSpaceRect = initialLayerStackRect; + + // The incoming request sets a layerStackSpaceRect + DisplayState state; + state.what = DisplayState::eDisplayProjectionChanged; + state.token = display.token(); + state.layerStackSpaceRect = desiredLayerStackRect; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags indicate a transaction is needed + EXPECT_EQ(eDisplayTransactionNeeded, flags); + + // The current display state has the new value. + EXPECT_EQ(desiredLayerStackRect, display.getCurrentDisplayState().layerStackSpaceRect); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedDoesNothingIfSizeDidNotChange) { + using Case = SimplePrimaryDisplayCase; + constexpr uint32_t initialWidth = 1024; + constexpr uint32_t initialHeight = 768; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The current display state has a size set + display.mutableCurrentDisplayState().width = initialWidth; + display.mutableCurrentDisplayState().height = initialHeight; + + // The incoming request sets the same display size + DisplayState state; + state.what = DisplayState::eDisplaySizeChanged; + state.token = display.token(); + state.width = initialWidth; + state.height = initialHeight; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags are empty + EXPECT_EQ(0u, flags); + + // The current display state is unchanged + EXPECT_EQ(initialWidth, display.getCurrentDisplayState().width); + EXPECT_EQ(initialHeight, display.getCurrentDisplayState().height); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfWidthChanged) { + using Case = SimplePrimaryDisplayCase; + constexpr uint32_t initialWidth = 0; + constexpr uint32_t desiredWidth = 1024; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display does not yet have a width + display.mutableCurrentDisplayState().width = initialWidth; + + // The incoming request sets a display width + DisplayState state; + state.what = DisplayState::eDisplaySizeChanged; + state.token = display.token(); + state.width = desiredWidth; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags indicate a transaction is needed + EXPECT_EQ(eDisplayTransactionNeeded, flags); + + // The current display state has the new value. + EXPECT_EQ(desiredWidth, display.getCurrentDisplayState().width); +} + +TEST_F(SetDisplayStateLockedTest, setDisplayStateLockedRequestsUpdateIfHeightChanged) { + using Case = SimplePrimaryDisplayCase; + constexpr uint32_t initialHeight = 0; + constexpr uint32_t desiredHeight = 768; + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display does not yet have a height + display.mutableCurrentDisplayState().height = initialHeight; + + // The incoming request sets a display height + DisplayState state; + state.what = DisplayState::eDisplaySizeChanged; + state.token = display.token(); + state.height = desiredHeight; + + // -------------------------------------------------------------------- + // Invocation + + uint32_t flags = mFlinger.setDisplayStateLocked(state); + + // -------------------------------------------------------------------- + // Postconditions + + // The returned flags indicate a transaction is needed + EXPECT_EQ(eDisplayTransactionNeeded, flags); + + // The current display state has the new value. + EXPECT_EQ(desiredHeight, display.getCurrentDisplayState().height); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp new file mode 100644 index 0000000000..2117628ec3 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -0,0 +1,498 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android { +namespace { + +// Used when we simulate a display that supports doze. +template <typename Display> +struct DozeIsSupportedVariant { + static constexpr bool DOZE_SUPPORTED = true; + static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE = + IComposerClient::PowerMode::DOZE; + static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND = + IComposerClient::PowerMode::DOZE_SUSPEND; + + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>( + std::vector<DisplayCapability>({DisplayCapability::DOZE})), + Return(Error::NONE))); + } +}; + +template <typename Display> +// Used when we simulate a display that does not support doze. +struct DozeNotSupportedVariant { + static constexpr bool DOZE_SUPPORTED = false; + static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE = + IComposerClient::PowerMode::ON; + static constexpr IComposerClient::PowerMode ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND = + IComposerClient::PowerMode::ON; + + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getDisplayCapabilities(Display::HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<DisplayCapability>({})), + Return(Error::NONE))); + } +}; + +struct EventThreadBaseSupportedVariant { + static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) { + // The callback should not be notified to toggle VSYNC. + EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(_)).Times(0); + + // The event thread should not be notified. + EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0); + EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0); + } +}; + +struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant { + static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) { + // These calls are only expected for the primary display. + + // Instead expect no calls. + setupVsyncAndEventThreadNoCallExpectations(test); + } + + static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) { + // These calls are only expected for the primary display. + + // Instead expect no calls. + setupVsyncAndEventThreadNoCallExpectations(test); + } +}; + +struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { + static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) { + // The callback should be notified to enable VSYNC. + EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(true)).Times(1); + + // The event thread should be notified that the screen was acquired. + EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1); + } + + static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) { + // The callback should be notified to disable VSYNC. + EXPECT_CALL(test->mSchedulerCallback, setVsyncEnabled(false)).Times(1); + + // The event thread should not be notified that the screen was released. + EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1); + } +}; + +struct DispSyncIsSupportedVariant { + static void setupResetModelCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_REFRESH_RATE)).Times(1); + EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1); + } +}; + +struct DispSyncNotSupportedVariant { + static void setupResetModelCallExpectations(DisplayTransactionTest* /* test */) {} +}; + +// -------------------------------------------------------------------- +// Note: +// +// There are a large number of transitions we could test, however we only test a +// selected subset which provides complete test coverage of the implementation. +// -------------------------------------------------------------------- + +template <PowerMode initialPowerMode, PowerMode targetPowerMode> +struct TransitionVariantCommon { + static constexpr auto INITIAL_POWER_MODE = initialPowerMode; + static constexpr auto TARGET_POWER_MODE = targetPowerMode; + + static void verifyPostconditions(DisplayTransactionTest*) {} +}; + +struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, PowerMode::ON> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); + Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::DispSync::setupResetModelCallExpectations(test); + Case::setupRepaintEverythingCallExpectations(test); + } + + static void verifyPostconditions(DisplayTransactionTest* test) { + EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); + EXPECT_TRUE(test->mFlinger.getHasPoweredOff()); + } +}; + +struct TransitionOffToDozeSuspendVariant + : public TransitionVariantCommon<PowerMode::OFF, PowerMode::DOZE_SUSPEND> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); + Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::setupRepaintEverythingCallExpectations(test); + } + + static void verifyPostconditions(DisplayTransactionTest* test) { + EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); + EXPECT_TRUE(test->mFlinger.getHasPoweredOff()); + } +}; + +struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); + } + + static void verifyPostconditions(DisplayTransactionTest* test) { + EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); + } +}; + +struct TransitionDozeSuspendToOffVariant + : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); + } + + static void verifyPostconditions(DisplayTransactionTest* test) { + EXPECT_TRUE(test->mFlinger.getVisibleRegionsDirty()); + } +}; + +struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); + } +}; + +struct TransitionDozeSuspendToDozeVariant + : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::DispSync::setupResetModelCallExpectations(test); + Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); + } +}; + +struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); + } +}; + +struct TransitionDozeSuspendToOnVariant + : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::DispSync::setupResetModelCallExpectations(test); + Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); + } +}; + +struct TransitionOnToDozeSuspendVariant + : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); + } +}; + +struct TransitionOnToUnknownVariant + : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> { + template <typename Case> + static void setupCallExpectations(DisplayTransactionTest* test) { + Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::setupNoComposerPowerModeCallExpectations(test); + } +}; + +// -------------------------------------------------------------------- +// Note: +// +// Rather than testing the cartesian product of +// DozeIsSupported/DozeNotSupported with all other options, we use one for one +// display type, and the other for another display type. +// -------------------------------------------------------------------- + +template <typename DisplayVariant, typename DozeVariant, typename EventThreadVariant, + typename DispSyncVariant, typename TransitionVariant> +struct DisplayPowerCase { + using Display = DisplayVariant; + using Doze = DozeVariant; + using EventThread = EventThreadVariant; + using DispSync = DispSyncVariant; + using Transition = TransitionVariant; + + static auto injectDisplayWithInitialPowerMode(DisplayTransactionTest* test, PowerMode mode) { + Display::injectHwcDisplayWithNoDefaultCapabilities(test); + auto display = Display::makeFakeExistingDisplayInjector(test); + display.inject(); + display.mutableDisplayDevice()->setPowerMode(mode); + return display; + } + + static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) { + test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled; + } + + static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mMessageQueue, invalidate()).Times(1); + } + + static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test, + PowerMode mode) { + EXPECT_CALL(*test->mSurfaceInterceptor, isEnabled()).WillOnce(Return(true)); + EXPECT_CALL(*test->mSurfaceInterceptor, savePowerModeUpdate(_, static_cast<int32_t>(mode))) + .Times(1); + } + + static void setupComposerCallExpectations(DisplayTransactionTest* test, PowerMode mode) { + // Any calls to get the active config will return a default value. + EXPECT_CALL(*test->mComposer, getActiveConfig(Display::HWC_DISPLAY_ID, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(Display::HWC_ACTIVE_CONFIG_ID), + Return(Error::NONE))); + + // Any calls to get whether the display supports dozing will return the value set by the + // policy variant. + EXPECT_CALL(*test->mComposer, getDozeSupport(Display::HWC_DISPLAY_ID, _)) + .WillRepeatedly(DoAll(SetArgPointee<1>(Doze::DOZE_SUPPORTED), Return(Error::NONE))); + + EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, mode)).Times(1); + } + + static void setupNoComposerPowerModeCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, setPowerMode(Display::HWC_DISPLAY_ID, _)).Times(0); + } +}; + +// A sample configuration for the primary display. +// In addition to having event thread support, we emulate doze support. +template <typename TransitionVariant> +using PrimaryDisplayPowerCase = + DisplayPowerCase<PrimaryDisplayVariant, DozeIsSupportedVariant<PrimaryDisplayVariant>, + EventThreadIsSupportedVariant, DispSyncIsSupportedVariant, + TransitionVariant>; + +// A sample configuration for the external display. +// In addition to not having event thread support, we emulate not having doze +// support. +template <typename TransitionVariant> +using ExternalDisplayPowerCase = + DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>, + EventThreadNotSupportedVariant, DispSyncNotSupportedVariant, + TransitionVariant>; + +class SetPowerModeInternalTest : public DisplayTransactionTest { +public: + template <typename Case> + void transitionDisplayCommon(); +}; + +template <PowerMode PowerMode> +struct PowerModeInitialVSyncEnabled : public std::false_type {}; + +template <> +struct PowerModeInitialVSyncEnabled<PowerMode::ON> : public std::true_type {}; + +template <> +struct PowerModeInitialVSyncEnabled<PowerMode::DOZE> : public std::true_type {}; + +template <typename Case> +void SetPowerModeInternalTest::transitionDisplayCommon() { + // -------------------------------------------------------------------- + // Preconditions + + Case::Doze::setupComposerCallExpectations(this); + auto display = + Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE); + Case::setInitialPrimaryHWVsyncEnabled(this, + PowerModeInitialVSyncEnabled< + Case::Transition::INITIAL_POWER_MODE>::value); + + // -------------------------------------------------------------------- + // Call Expectations + + Case::setupSurfaceInterceptorCallExpectations(this, Case::Transition::TARGET_POWER_MODE); + Case::Transition::template setupCallExpectations<Case>(this); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), + Case::Transition::TARGET_POWER_MODE); + + // -------------------------------------------------------------------- + // Postconditions + + Case::Transition::verifyPostconditions(this); +} + +TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfNoChange) { + using Case = SimplePrimaryDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // A primary display device is set up + Case::Display::injectHwcDisplay(this); + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display is already set to PowerMode::ON + display.mutableDisplayDevice()->setPowerMode(PowerMode::ON); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::ON); + + // -------------------------------------------------------------------- + // Postconditions + + EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); +} + +TEST_F(SetPowerModeInternalTest, setPowerModeInternalDoesNothingIfVirtualDisplay) { + using Case = HwcVirtualDisplayCase; + + // -------------------------------------------------------------------- + // Preconditions + + // Insert display data so that the HWC thinks it created the virtual display. + const auto displayId = Case::Display::DISPLAY_ID::get(); + ASSERT_TRUE(HalVirtualDisplayId::tryCast(displayId)); + mFlinger.mutableHwcDisplayData().try_emplace(displayId); + + // A virtual display device is set up + Case::Display::injectHwcDisplay(this); + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.inject(); + + // The display is set to PowerMode::ON + getDisplayDevice(display.token())->setPowerMode(PowerMode::ON); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.setPowerModeInternal(display.mutableDisplayDevice(), PowerMode::OFF); + + // -------------------------------------------------------------------- + // Postconditions + + EXPECT_EQ(PowerMode::ON, display.mutableDisplayDevice()->getPowerMode()); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToOffVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOffVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozePrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozePrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay) { + transitionDisplayCommon<PrimaryDisplayPowerCase<TransitionOnToUnknownVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToDozeSuspendVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOffExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOffVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); +} + +TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDisplay) { + transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>(); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp new file mode 100644 index 0000000000..61f0788fa9 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp @@ -0,0 +1,316 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DisplayTransactionTestHelpers.h" + +namespace android { +namespace { + +using hal::RenderIntent; + +// For this variant, SurfaceFlinger should configure itself with wide display +// support, and the display should respond with an non-empty list of supported +// color modes. Wide-color support should be configured. +template <typename Display> +struct WideColorP3ColorimetricSupportedVariant { + static constexpr bool WIDE_COLOR_SUPPORTED = true; + + static void injectConfigChange(DisplayTransactionTest* test) { + test->mFlinger.mutableUseColorManagement() = true; + test->mFlinger.mutableHasWideColorDisplay() = true; + test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; + } + + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_DATASPACE)).Times(1); + + EXPECT_CALL(*test->mComposer, getColorModes(Display::HWC_DISPLAY_ID, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<ColorMode>({ColorMode::DISPLAY_P3})), + Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, + getRenderIntents(Display::HWC_DISPLAY_ID, ColorMode::DISPLAY_P3, _)) + .WillOnce(DoAll(SetArgPointee<2>( + std::vector<RenderIntent>({RenderIntent::COLORIMETRIC})), + Return(Error::NONE))); + EXPECT_CALL(*test->mComposer, + setColorMode(Display::HWC_DISPLAY_ID, ColorMode::SRGB, + RenderIntent::COLORIMETRIC)) + .WillOnce(Return(Error::NONE)); + } +}; + +template <typename Display> +struct Hdr10PlusSupportedVariant { + static constexpr bool HDR10_PLUS_SUPPORTED = true; + static constexpr bool HDR10_SUPPORTED = true; + static constexpr bool HDR_HLG_SUPPORTED = false; + static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getHdrCapabilities(_, _, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({ + Hdr::HDR10_PLUS, + Hdr::HDR10, + })), + Return(Error::NONE))); + } +}; + +// For this variant, the composer should respond with a non-empty list of HDR +// modes containing HDR10, so HDR10 support should be configured. +template <typename Display> +struct Hdr10SupportedVariant { + static constexpr bool HDR10_PLUS_SUPPORTED = false; + static constexpr bool HDR10_SUPPORTED = true; + static constexpr bool HDR_HLG_SUPPORTED = false; + static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HDR10})), + Return(Error::NONE))); + } +}; + +// For this variant, the composer should respond with a non-empty list of HDR +// modes containing HLG, so HLG support should be configured. +template <typename Display> +struct HdrHlgSupportedVariant { + static constexpr bool HDR10_PLUS_SUPPORTED = false; + static constexpr bool HDR10_SUPPORTED = false; + static constexpr bool HDR_HLG_SUPPORTED = true; + static constexpr bool HDR_DOLBY_VISION_SUPPORTED = false; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _)) + .WillOnce( + DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::HLG})), Return(Error::NONE))); + } +}; + +// For this variant, the composer should respond with a non-empty list of HDR +// modes containing DOLBY_VISION, so DOLBY_VISION support should be configured. +template <typename Display> +struct HdrDolbyVisionSupportedVariant { + static constexpr bool HDR10_PLUS_SUPPORTED = false; + static constexpr bool HDR10_SUPPORTED = false; + static constexpr bool HDR_HLG_SUPPORTED = false; + static constexpr bool HDR_DOLBY_VISION_SUPPORTED = true; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getHdrCapabilities(Display::HWC_DISPLAY_ID, _, _, _, _)) + .WillOnce(DoAll(SetArgPointee<1>(std::vector<Hdr>({Hdr::DOLBY_VISION})), + Return(Error::NONE))); + } +}; + +template <typename Display> +struct Smpte2086PerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::SMPTE2086; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) + .WillOnce(Return(std::vector<PerFrameMetadataKey>({ + PerFrameMetadataKey::DISPLAY_RED_PRIMARY_X, + PerFrameMetadataKey::DISPLAY_RED_PRIMARY_Y, + PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_X, + PerFrameMetadataKey::DISPLAY_GREEN_PRIMARY_Y, + PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_X, + PerFrameMetadataKey::DISPLAY_BLUE_PRIMARY_Y, + PerFrameMetadataKey::WHITE_POINT_X, + PerFrameMetadataKey::WHITE_POINT_Y, + PerFrameMetadataKey::MAX_LUMINANCE, + PerFrameMetadataKey::MIN_LUMINANCE, + }))); + } +}; + +template <typename Display> +struct Cta861_3_PerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::CTA861_3; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) + .WillOnce(Return(std::vector<PerFrameMetadataKey>({ + PerFrameMetadataKey::MAX_CONTENT_LIGHT_LEVEL, + PerFrameMetadataKey::MAX_FRAME_AVERAGE_LIGHT_LEVEL, + }))); + } +}; + +template <typename Display> +struct Hdr10_Plus_PerFrameMetadataSupportVariant { + static constexpr int PER_FRAME_METADATA_KEYS = HdrMetadata::Type::HDR10PLUS; + static void setupComposerCallExpectations(DisplayTransactionTest* test) { + EXPECT_CALL(*test->mComposer, getPerFrameMetadataKeys(Display::HWC_DISPLAY_ID)) + .WillOnce(Return(std::vector<PerFrameMetadataKey>({ + PerFrameMetadataKey::HDR10_PLUS_SEI, + }))); + } +}; + +using WideColorP3ColorimetricDisplayCase = + Case<PrimaryDisplayVariant, WideColorP3ColorimetricSupportedVariant<PrimaryDisplayVariant>, + HdrNotSupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using Hdr10PlusDisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + Hdr10SupportedVariant<PrimaryDisplayVariant>, + Hdr10_Plus_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using Hdr10DisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + Hdr10SupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using HdrHlgDisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + HdrHlgSupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using HdrDolbyVisionDisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + HdrDolbyVisionSupportedVariant<PrimaryDisplayVariant>, + NoPerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using HdrSmpte2086DisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + HdrNotSupportedVariant<PrimaryDisplayVariant>, + Smpte2086PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; +using HdrCta861_3_DisplayCase = + Case<PrimaryDisplayVariant, WideColorNotSupportedVariant<PrimaryDisplayVariant>, + HdrNotSupportedVariant<PrimaryDisplayVariant>, + Cta861_3_PerFrameMetadataSupportVariant<PrimaryDisplayVariant>>; + +class SetupNewDisplayDeviceInternalTest : public DisplayTransactionTest { +public: + template <typename T> + void setupNewDisplayDeviceInternalTest(); +}; + +template <typename Case> +void SetupNewDisplayDeviceInternalTest::setupNewDisplayDeviceInternalTest() { + const sp<BBinder> displayToken = new BBinder(); + const sp<compositionengine::mock::DisplaySurface> displaySurface = + new compositionengine::mock::DisplaySurface(); + const sp<mock::GraphicBufferProducer> producer = new mock::GraphicBufferProducer(); + + // -------------------------------------------------------------------- + // Preconditions + + // Wide color displays support is configured appropriately + Case::WideColorSupport::injectConfigChange(this); + + // The display is setup with the HWC. + Case::Display::injectHwcDisplay(this); + + // SurfaceFlinger will use a test-controlled factory for native window + // surfaces. + injectFakeNativeWindowSurfaceFactory(); + + // A compositionengine::Display has already been created + auto compositionDisplay = Case::Display::injectCompositionDisplay(this); + + // -------------------------------------------------------------------- + // Call Expectations + + // Various native window calls will be made. + Case::Display::setupNativeWindowSurfaceCreationCallExpectations(this); + Case::Display::setupHwcGetActiveConfigCallExpectations(this); + Case::WideColorSupport::setupComposerCallExpectations(this); + Case::HdrSupport::setupComposerCallExpectations(this); + Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); + + // -------------------------------------------------------------------- + // Invocation + + DisplayDeviceState state; + if (const auto connectionType = Case::Display::CONNECTION_TYPE::value) { + const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get()); + ASSERT_TRUE(displayId); + const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value; + ASSERT_TRUE(hwcDisplayId); + state.physical = {.id = *displayId, .type = *connectionType, .hwcDisplayId = *hwcDisplayId}; + } + + state.isSecure = static_cast<bool>(Case::Display::SECURE); + + auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state, + displaySurface, producer); + + // -------------------------------------------------------------------- + // Postconditions + + ASSERT_TRUE(device != nullptr); + EXPECT_EQ(Case::Display::DISPLAY_ID::get(), device->getId()); + EXPECT_EQ(Case::Display::CONNECTION_TYPE::value, device->getConnectionType()); + EXPECT_EQ(static_cast<bool>(Case::Display::VIRTUAL), device->isVirtual()); + EXPECT_EQ(static_cast<bool>(Case::Display::SECURE), device->isSecure()); + EXPECT_EQ(static_cast<bool>(Case::Display::PRIMARY), device->isPrimary()); + EXPECT_EQ(Case::Display::WIDTH, device->getWidth()); + EXPECT_EQ(Case::Display::HEIGHT, device->getHeight()); + EXPECT_EQ(Case::WideColorSupport::WIDE_COLOR_SUPPORTED, device->hasWideColorGamut()); + EXPECT_EQ(Case::HdrSupport::HDR10_PLUS_SUPPORTED, device->hasHDR10PlusSupport()); + EXPECT_EQ(Case::HdrSupport::HDR10_SUPPORTED, device->hasHDR10Support()); + EXPECT_EQ(Case::HdrSupport::HDR_HLG_SUPPORTED, device->hasHLGSupport()); + EXPECT_EQ(Case::HdrSupport::HDR_DOLBY_VISION_SUPPORTED, device->hasDolbyVisionSupport()); + // Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are + // remapped, and the test only ever sets up one config. If there were an error + // looking up the remapped index, device->getActiveConfig() would be -1 instead. + EXPECT_EQ(0, device->getActiveConfig().value()); + EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS, + device->getSupportedPerFrameMetadata()); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createSimplePrimaryDisplay) { + setupNewDisplayDeviceInternalTest<SimplePrimaryDisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createSimpleExternalDisplay) { + setupNewDisplayDeviceInternalTest<SimpleExternalDisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createNonHwcVirtualDisplay) { + setupNewDisplayDeviceInternalTest<NonHwcVirtualDisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) { + setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) { + setupNewDisplayDeviceInternalTest<WideColorP3ColorimetricDisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10PlusDisplay) { + setupNewDisplayDeviceInternalTest<Hdr10PlusDisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createHdr10Display) { + setupNewDisplayDeviceInternalTest<Hdr10DisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createHdrHlgDisplay) { + setupNewDisplayDeviceInternalTest<HdrHlgDisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createHdrDolbyVisionDisplay) { + setupNewDisplayDeviceInternalTest<HdrDolbyVisionDisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createHdrSmpte2086DisplayCase) { + setupNewDisplayDeviceInternalTest<HdrSmpte2086DisplayCase>(); +} + +TEST_F(SetupNewDisplayDeviceInternalTest, createHdrCta816_3_DisplayCase) { + setupNewDisplayDeviceInternalTest<HdrCta861_3_DisplayCase>(); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index d5ecae81cd..a9d9dc08ad 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -16,83 +16,108 @@ #pragma once +#include <Scheduler/Scheduler.h> #include <gmock/gmock.h> #include <gui/ISurfaceComposer.h> -#include "Scheduler/DispSync.h" #include "Scheduler/EventThread.h" #include "Scheduler/LayerHistory.h" #include "Scheduler/Scheduler.h" +#include "Scheduler/VSyncTracker.h" +#include "Scheduler/VsyncController.h" +#include "mock/MockVSyncTracker.h" +#include "mock/MockVsyncController.h" namespace android { -class TestableScheduler : public Scheduler, private ISchedulerCallback { +class TestableScheduler : public Scheduler { public: - TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) - : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) { - if (mUseContentDetectionV2) { - mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs); - } else { - mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>(); - } - } + TestableScheduler(const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback, + bool useContentDetectionV2) + : TestableScheduler(std::make_unique<mock::VsyncController>(), + std::make_unique<mock::VSyncTracker>(), configs, callback, + useContentDetectionV2) {} - TestableScheduler(std::unique_ptr<DispSync> primaryDispSync, - std::unique_ptr<EventControlThread> eventControlThread, - const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2) - : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this, - useContentDetectionV2, true) { - if (mUseContentDetectionV2) { - mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>(configs); - } else { - mLayerHistory = std::make_unique<scheduler::impl::LayerHistory>(); - } - } + TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, + std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, + const scheduler::RefreshRateConfigs& configs, ISchedulerCallback& callback, + bool useContentDetectionV2) + : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr}, configs, + callback, createLayerHistory(configs, useContentDetectionV2), + {.supportKernelTimer = false, + .useContentDetection = true, + .useContentDetectionV2 = useContentDetectionV2}) {} // Used to inject mock event thread. ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { return Scheduler::createConnection(std::move(eventThread)); } - size_t layerHistorySize() const NO_THREAD_SAFETY_ANALYSIS { - if (mUseContentDetectionV2) { - return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get()) - ->mLayerInfos.size(); - } else { - return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get()) - ->mLayerInfos.size(); - } - } - /* ------------------------------------------------------------------------ * Read-write access to private data to set up preconditions and assert * post-conditions. */ auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; } - auto& mutableEventControlThread() { return mEventControlThread; } - auto& mutablePrimaryDispSync() { return mPrimaryDispSync; } auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; } - auto mutableLayerHistory() { + + bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); } + + auto* mutableLayerHistory() { + LOG_ALWAYS_FATAL_IF(mOptions.useContentDetectionV2); return static_cast<scheduler::impl::LayerHistory*>(mLayerHistory.get()); } - auto mutableLayerHistoryV2() { + + auto* mutableLayerHistoryV2() { + LOG_ALWAYS_FATAL_IF(!mOptions.useContentDetectionV2); return static_cast<scheduler::impl::LayerHistoryV2*>(mLayerHistory.get()); } + size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { + if (!mLayerHistory) return 0; + return mOptions.useContentDetectionV2 ? mutableLayerHistoryV2()->mLayerInfos.size() + : mutableLayerHistory()->mLayerInfos.size(); + } + + void replaceTouchTimer(int64_t millis) { + if (mTouchTimer) { + mTouchTimer.reset(); + } + mTouchTimer.emplace( + std::chrono::milliseconds(millis), + [this] { touchTimerCallback(TimerState::Reset); }, + [this] { touchTimerCallback(TimerState::Expired); }); + mTouchTimer->start(); + } + + bool isTouchActive() { + std::lock_guard<std::mutex> lock(mFeatureStateLock); + return mFeatures.touch == Scheduler::TouchState::Active; + } + + void dispatchCachedReportedConfig() { + std::lock_guard<std::mutex> lock(mFeatureStateLock); + return Scheduler::dispatchCachedReportedConfig(); + } + + void clearOptionalFieldsInFeatures() { + std::lock_guard<std::mutex> lock(mFeatureStateLock); + mFeatures.cachedConfigChangedParams.reset(); + } + + void onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, + HwcConfigIndexType configId, nsecs_t vsyncPeriod) { + return Scheduler::onNonPrimaryDisplayConfigChanged(handle, displayId, configId, + vsyncPeriod); + } + ~TestableScheduler() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the Scheduler instance may // still be referenced by something despite our best efforts to destroy // it after each test is done. - mutableEventControlThread().reset(); - mutablePrimaryDispSync().reset(); + mVsyncSchedule.controller.reset(); mConnections.clear(); } - -private: - void changeRefreshRate(const RefreshRate&, ConfigEvent) override {} - void repaintEverythingForHWC() override {} - void kernelTimerChanged(bool /*expired*/) override {} }; } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index cbf264de53..6ce738a89f 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -29,7 +29,7 @@ #include "ContainerLayer.h" #include "DisplayDevice.h" #include "EffectLayer.h" -#include "FakePhaseOffsets.h" +#include "FakeVsyncConfiguration.h" #include "Layer.h" #include "NativeWindowSurface.h" #include "Scheduler/MessageQueue.h" @@ -40,6 +40,7 @@ #include "SurfaceInterceptor.h" #include "TestableScheduler.h" #include "mock/DisplayHardware/MockDisplay.h" +#include "mock/MockDisplayIdGenerator.h" namespace android { @@ -65,15 +66,6 @@ class Factory final : public surfaceflinger::Factory { public: ~Factory() = default; - std::unique_ptr<DispSync> createDispSync(const char*, bool) override { - return nullptr; - } - - std::unique_ptr<EventControlThread> createEventControlThread( - std::function<void(bool)>) override { - return nullptr; - } - std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; } @@ -82,19 +74,18 @@ public: return std::make_unique<android::impl::MessageQueue>(); } - std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration( + std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration( const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override { return std::make_unique<scheduler::FakePhaseOffsets>(); } - std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>, - const scheduler::RefreshRateConfigs&, + std::unique_ptr<Scheduler> createScheduler(const scheduler::RefreshRateConfigs&, ISchedulerCallback&) override { return nullptr; } - std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger* flinger) override { - return std::make_unique<android::impl::SurfaceInterceptor>(flinger); + sp<SurfaceInterceptor> createSurfaceInterceptor() override { + return new android::impl::SurfaceInterceptor(); } sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override { @@ -175,12 +166,15 @@ public: } // namespace surfaceflinger::test -class TestableSurfaceFlinger { +class TestableSurfaceFlinger final : private ISchedulerCallback { public: using HotplugEvent = SurfaceFlinger::HotplugEvent; SurfaceFlinger* flinger() { return mFlinger.get(); } TestableScheduler* scheduler() { return mScheduler; } + mock::DisplayIdGenerator<GpuVirtualDisplayId>& gpuVirtualDisplayIdGenerator() { + return mGpuVirtualDisplayIdGenerator; + } // Extend this as needed for accessing SurfaceFlinger private (and public) // functions. @@ -198,37 +192,43 @@ public: mFlinger->mCompositionEngine->setTimeStats(timeStats); } - void setupScheduler(std::unique_ptr<DispSync> primaryDispSync, - std::unique_ptr<EventControlThread> eventControlThread, + // The ISchedulerCallback argument can be nullptr for a no-op implementation. + void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, + std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, - bool useContentDetectionV2 = false) { + ISchedulerCallback* callback = nullptr, bool hasMultipleConfigs = false) { std::vector<std::shared_ptr<const HWC2::Display::Config>> configs{ HWC2::Display::Config::Builder(mDisplay, 0) - .setVsyncPeriod(int32_t(16666667)) + .setVsyncPeriod(16'666'667) .setConfigGroup(0) .build()}; + if (hasMultipleConfigs) { + configs.emplace_back(HWC2::Display::Config::Builder(mDisplay, 1) + .setVsyncPeriod(11'111'111) + .setConfigGroup(0) + .build()); + } + mFlinger->mRefreshRateConfigs = std::make_unique< scheduler::RefreshRateConfigs>(configs, /*currentConfig=*/HwcConfigIndexType(0)); mFlinger->mRefreshRateStats = std::make_unique< scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats, /*currentConfig=*/HwcConfigIndexType(0), /*powerMode=*/hal::PowerMode::OFF); - mFlinger->mPhaseConfiguration = - mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs); + mFlinger->mVsyncConfiguration = + mFactory.createVsyncConfiguration(*mFlinger->mRefreshRateConfigs); + mFlinger->mVsyncModulator.emplace(mFlinger->mVsyncConfiguration->getCurrentConfigs()); - mScheduler = - new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread), - *mFlinger->mRefreshRateConfigs, useContentDetectionV2); + constexpr bool kUseContentDetectionV2 = false; + mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), + *mFlinger->mRefreshRateConfigs, *(callback ?: this), + kUseContentDetectionV2); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); resetScheduler(mScheduler); - - mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle, - mFlinger->mSfConnectionHandle, - mFlinger->mPhaseConfiguration->getCurrentOffsets()); } void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } @@ -277,6 +277,10 @@ public: layer->mPotentialCursor = potentialCursor; } + static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) { + layer->mDrawingParent = drawingParent; + } + /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ @@ -289,8 +293,6 @@ public: return mFlinger->destroyDisplay(displayToken); } - auto resetDisplayState() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->resetDisplayState(); } - auto setupNewDisplayDeviceInternal( const wp<IBinder>& displayToken, std::shared_ptr<compositionengine::Display> compositionDisplay, @@ -321,27 +323,30 @@ public: return mFlinger->onInitializeDisplays(); } + auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); } + // Allow reading display state without locking, as if called on the SF main thread. auto setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) NO_THREAD_SAFETY_ANALYSIS { return mFlinger->setPowerModeInternal(display, mode); } - auto onMessageReceived(int32_t what) { return mFlinger->onMessageReceived(what, systemTime()); } + auto onMessageReceived(int32_t what) { + return mFlinger->onMessageReceived(what, /*vsyncId=*/0, systemTime()); + } - auto captureScreenImplLocked(const RenderArea& renderArea, - SurfaceFlinger::TraverseLayersFunction traverseLayers, - ANativeWindowBuffer* buffer, bool useIdentityTransform, - bool forSystem, int* outSyncFd, bool regionSampling) { - bool ignored; - return mFlinger->captureScreenImplLocked(renderArea, traverseLayers, buffer, - useIdentityTransform, forSystem, outSyncFd, - regionSampling, ignored); + auto renderScreenImplLocked(const RenderArea& renderArea, + SurfaceFlinger::TraverseLayersFunction traverseLayers, + const sp<GraphicBuffer>& buffer, bool forSystem, int* outSyncFd, + bool regionSampling) { + ScreenCaptureResults captureResults; + return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem, + outSyncFd, regionSampling, captureResults); } - auto traverseLayersInDisplay(const sp<const DisplayDevice>& display, - const LayerVector::Visitor& visitor) { - return mFlinger->SurfaceFlinger::traverseLayersInDisplay(display, visitor); + auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid, + const LayerVector::Visitor& visitor) { + return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid, visitor); } auto getDisplayNativePrimaries(const sp<IBinder>& displayToken, @@ -351,16 +356,18 @@ public: auto& getTransactionQueue() { return mFlinger->mTransactionQueues; } - auto setTransactionState(const Vector<ComposerState>& states, + auto setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, - std::vector<ListenerCallbacks>& listenerCallbacks) { - return mFlinger->setTransactionState(states, displays, flags, applyToken, - inputWindowCommands, desiredPresentTime, uncacheBuffer, - hasListenerCallbacks, listenerCallbacks); + std::vector<ListenerCallbacks>& listenerCallbacks, + uint64_t transactionId) { + return mFlinger->setTransactionState(frameTimelineVsyncId, states, displays, flags, + applyToken, inputWindowCommands, desiredPresentTime, + uncacheBuffer, hasListenerCallbacks, listenerCallbacks, + transactionId); } auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); }; @@ -427,7 +434,7 @@ public: mutableCurrentState().displays.clear(); mutableDrawingState().displays.clear(); mutableEventQueue().reset(); - mutableInterceptor().reset(); + mutableInterceptor().clear(); mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); mFlinger->mCompositionEngine->setRenderEngine( @@ -464,7 +471,8 @@ public: static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0; static constexpr hal::PowerMode DEFAULT_POWER_MODE = hal::PowerMode::ON; - FakeHwcDisplayInjector(DisplayId displayId, hal::DisplayType hwcDisplayType, bool isPrimary) + FakeHwcDisplayInjector(HalDisplayId displayId, hal::DisplayType hwcDisplayType, + bool isPrimary) : mDisplayId(displayId), mHwcDisplayType(hwcDisplayType), mIsPrimary(isPrimary) {} auto& setHwcDisplayId(hal::HWDisplayId displayId) { @@ -537,14 +545,16 @@ public: flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display); if (mHwcDisplayType == hal::DisplayType::PHYSICAL) { - flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId); + const auto physicalId = PhysicalDisplayId::tryCast(mDisplayId); + LOG_ALWAYS_FATAL_IF(!physicalId); + flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, *physicalId); (mIsPrimary ? flinger->mutableInternalHwcDisplayId() : flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId; } } private: - const DisplayId mDisplayId; + const HalDisplayId mDisplayId; const hal::DisplayType mHwcDisplayType; const bool mIsPrimary; @@ -636,8 +646,10 @@ public: DisplayDeviceState state; if (const auto type = mCreationArgs.connectionType) { LOG_ALWAYS_FATAL_IF(!displayId); + const auto physicalId = PhysicalDisplayId::tryCast(*displayId); + LOG_ALWAYS_FATAL_IF(!physicalId); LOG_ALWAYS_FATAL_IF(!mHwcDisplayId); - state.physical = {*displayId, *type, *mHwcDisplayId}; + state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId}; } state.isSecure = mCreationArgs.isSecure; @@ -661,10 +673,17 @@ public: const std::optional<hal::HWDisplayId> mHwcDisplayId; }; +private: + void setVsyncEnabled(bool) override {} + void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override {} + void repaintEverythingForHWC() override {} + void kernelTimerChanged(bool) override {} + surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization); TestableScheduler* mScheduler = nullptr; Hwc2::mock::Display mDisplay; + mock::DisplayIdGenerator<GpuVirtualDisplayId> mGpuVirtualDisplayIdGenerator; }; } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 0a246505ca..a90f4247a9 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -58,6 +58,7 @@ using PowerMode = hardware::graphics::composer::V2_4::IComposerClient::PowerMode #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" @@ -227,7 +228,8 @@ static std::string genLayerName(int32_t layerId) { void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) { switch (type) { case TimeStamp::POST: - ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id), ts)); + ASSERT_NO_FATAL_FAILURE( + mTimeStats->setPostTime(id, frameNumber, genLayerName(id), UID_0, ts)); break; case TimeStamp::ACQUIRE: ASSERT_NO_FATAL_FAILURE(mTimeStats->setAcquireTime(id, frameNumber, ts)); @@ -349,6 +351,61 @@ TEST_F(TimeStatsTest, canIncreaseBadDesiredPresent) { EXPECT_THAT(result, HasSubstr(expectedResult)); } +TEST_F(TimeStatsTest, canIncreaseJankyFrames) { + // 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(TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::None); + + const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); + std::string expectedResult = "totalTimelineFrames = " + std::to_string(5); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "jankyFrames = " + std::to_string(4); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "appUnattributedJankyFrame = " + 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), + TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None); + + const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); + std::string expectedResult = "totalTimelineFrames = " + std::to_string(5); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "jankyFrames = " + std::to_string(4); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfLongCpuJankyFrames = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfLongGpuJankyFrames = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "sfUnattributedJankyFrame = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); + expectedResult = "appUnattributedJankyFrame = " + std::to_string(1); + EXPECT_THAT(result, HasSubstr(expectedResult)); +} + TEST_F(TimeStatsTest, canIncreaseClientCompositionReusedFrames) { // this stat is not in the proto so verify by checking the string dump constexpr size_t CLIENT_COMPOSITION_REUSED_FRAMES = 2; @@ -789,6 +846,16 @@ TEST_F(TimeStatsTest, canClearDumpOnlyTimeStats) { .count()); mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>( std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count())); + + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None); + EXPECT_TRUE(inputCommand(InputCommand::CLEAR, FMT_STRING).empty()); const std::string result(inputCommand(InputCommand::DUMP_ALL, FMT_STRING)); @@ -797,6 +864,11 @@ 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) { @@ -904,6 +976,8 @@ TEST_F(TimeStatsTest, globalStatsCallback) { mTimeStats->incrementClientCompositionFrames(); } + insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); + mTimeStats->recordDisplayEventConnectionCount(DISPLAY_EVENT_CONNECTIONS); mTimeStats->setPowerMode(PowerMode::ON); mTimeStats->recordFrameDuration(1000000, 3000000); @@ -913,6 +987,12 @@ TEST_F(TimeStatsTest, globalStatsCallback) { mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000)); mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000)); + mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(TimeStats::JankType::None); + EXPECT_THAT(mDelegate->mAtomTags, UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, android::util::SURFACEFLINGER_STATS_LAYER_INFO)); @@ -944,6 +1024,12 @@ 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, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent)); } EXPECT_EQ(AStatsManager_PULL_SUCCESS, @@ -975,6 +1061,15 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { } insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::SurfaceFlingerGpuDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::Display); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), + TimeStats::JankType::AppDeadlineMissed); + mTimeStats->incrementJankyFrames(UID_0, genLayerName(LAYER_ID_0), TimeStats::JankType::None); + EXPECT_THAT(mDelegate->mAtomTags, UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO, android::util::SURFACEFLINGER_STATS_LAYER_INFO)); @@ -1033,6 +1128,14 @@ TEST_F(TimeStatsTest, layerStatsCallback_pullsAllAndClears) { EXPECT_CALL(*mDelegate, statsEventWriteInt64(mDelegate->mEvent, LATE_ACQUIRE_FRAMES)); 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, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventWriteInt32(mDelegate->mEvent, 1)); + EXPECT_CALL(*mDelegate, statsEventBuild(mDelegate->mEvent)); } EXPECT_EQ(AStatsManager_PULL_SUCCESS, diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 2a48a22f48..68cf330b9c 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -31,10 +31,9 @@ #include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" -#include "mock/MockDispSync.h" -#include "mock/MockEventControlThread.h" #include "mock/MockEventThread.h" #include "mock/MockMessageQueue.h" +#include "mock/MockVsyncController.h" namespace android { @@ -67,21 +66,23 @@ public: EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*eventThread, createEventConnection(_, _)) .WillOnce(Return( - new EventThreadConnection(eventThread.get(), ResyncCallback(), + new EventThreadConnection(eventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) .WillOnce(Return( - new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0, + ResyncCallback(), ISurfaceComposer::eConfigChangedSuppress))); - EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0, _)).WillRepeatedly(Return(0)); - EXPECT_CALL(*mPrimaryDispSync, getPeriod()) + EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*mVSyncTracker, currentPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); - mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync), - std::make_unique<mock::EventControlThread>(), + mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController), + std::unique_ptr<mock::VSyncTracker>(mVSyncTracker), std::move(eventThread), std::move(sfEventThread)); } @@ -89,10 +90,10 @@ public: TestableSurfaceFlinger mFlinger; std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>(); - mock::EventControlThread* mEventControlThread = new mock::EventControlThread(); mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); - mock::DispSync* mPrimaryDispSync = new mock::DispSync(); + mock::VsyncController* mVsyncController = new mock::VsyncController(); + mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker(); struct TransactionInfo { Vector<ComposerState> states; @@ -101,7 +102,9 @@ public: sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); InputWindowCommands inputWindowCommands; int64_t desiredPresentTime = -1; + int64_t frameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID; client_cache_t uncacheBuffer; + int64_t id = -1; }; void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) { @@ -115,26 +118,28 @@ public: } void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows, - int64_t desiredPresentTime) { + int64_t desiredPresentTime, int64_t frameTimelineVsyncId) { mTransactionNumber++; transaction.flags |= flags; // ISurfaceComposer::eSynchronous; transaction.inputWindowCommands.syncInputWindows = syncInputWindows; transaction.desiredPresentTime = desiredPresentTime; + transaction.frameTimelineVsyncId = frameTimelineVsyncId; } void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) { ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); // called in SurfaceFlinger::signalTransaction EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); - EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)).WillOnce(Return(systemTime())); + EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime())); TransactionInfo transaction; setupSingle(transaction, flags, syncInputWindows, - /*desiredPresentTime*/ -1); + /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID); nsecs_t applicationTime = systemTime(); - mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags, + mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states, + transaction.displays, transaction.flags, transaction.applyToken, transaction.inputWindowCommands, transaction.desiredPresentTime, transaction.uncacheBuffer, - mHasListenerCallbacks, mCallbacks); + mHasListenerCallbacks, mCallbacks, transaction.id); // This transaction should not have been placed on the transaction queue. // If transaction is synchronous or syncs input windows, SF @@ -159,16 +164,17 @@ public: // first check will see desired present time has not passed, // but afterwards it will look like the desired present time has passed nsecs_t time = systemTime(); - EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)) + EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)) .WillOnce(Return(time + nsecs_t(5 * 1e8))); TransactionInfo transaction; setupSingle(transaction, flags, syncInputWindows, - /*desiredPresentTime*/ time + s2ns(1)); + /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID); nsecs_t applicationSentTime = systemTime(); - mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags, + mFlinger.setTransactionState(transaction.frameTimelineVsyncId, transaction.states, + transaction.displays, transaction.flags, transaction.applyToken, transaction.inputWindowCommands, transaction.desiredPresentTime, transaction.uncacheBuffer, - mHasListenerCallbacks, mCallbacks); + mHasListenerCallbacks, mCallbacks, transaction.id); nsecs_t returnedTime = systemTime(); EXPECT_LE(returnedTime, applicationSentTime + s2ns(5)); @@ -182,24 +188,25 @@ public: // called in SurfaceFlinger::signalTransaction nsecs_t time = systemTime(); EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); - EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)) + EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)) .WillOnce(Return(time + nsecs_t(5 * 1e8))); // transaction that should go on the pending thread TransactionInfo transactionA; setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false, - /*desiredPresentTime*/ time + s2ns(1)); + /*desiredPresentTime*/ time + s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID); // transaction that would not have gone on the pending thread if not // blocked TransactionInfo transactionB; setupSingle(transactionB, flags, syncInputWindows, - /*desiredPresentTime*/ -1); + /*desiredPresentTime*/ -1, ISurfaceComposer::INVALID_VSYNC_ID); nsecs_t applicationSentTime = systemTime(); - mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags, + mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states, + transactionA.displays, transactionA.flags, transactionA.applyToken, transactionA.inputWindowCommands, transactionA.desiredPresentTime, transactionA.uncacheBuffer, - mHasListenerCallbacks, mCallbacks); + mHasListenerCallbacks, mCallbacks, transactionA.id); // This thread should not have been blocked by the above transaction // (5s is the timeout period that applyTransactionState waits for SF to @@ -207,10 +214,11 @@ public: EXPECT_LE(systemTime(), applicationSentTime + s2ns(5)); applicationSentTime = systemTime(); - mFlinger.setTransactionState(transactionB.states, transactionB.displays, transactionB.flags, + mFlinger.setTransactionState(transactionB.frameTimelineVsyncId, transactionB.states, + transactionB.displays, transactionB.flags, transactionB.applyToken, transactionB.inputWindowCommands, transactionB.desiredPresentTime, transactionB.uncacheBuffer, - mHasListenerCallbacks, mCallbacks); + mHasListenerCallbacks, mCallbacks, transactionB.id); // this thread should have been blocked by the above transaction // if this is an animation, this thread should be blocked for 5s @@ -247,16 +255,17 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); // nsecs_t time = systemTime(); - EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime(_)) + EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)) .WillOnce(Return(nsecs_t(5 * 1e8))) .WillOnce(Return(s2ns(2))); TransactionInfo transactionA; // transaction to go on pending queue setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false, - /*desiredPresentTime*/ s2ns(1)); - mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags, - transactionA.applyToken, transactionA.inputWindowCommands, - transactionA.desiredPresentTime, transactionA.uncacheBuffer, - mHasListenerCallbacks, mCallbacks); + /*desiredPresentTime*/ s2ns(1), ISurfaceComposer::INVALID_VSYNC_ID); + mFlinger.setTransactionState(transactionA.frameTimelineVsyncId, transactionA.states, + transactionA.displays, transactionA.flags, transactionA.applyToken, + transactionA.inputWindowCommands, transactionA.desiredPresentTime, + transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks, + transactionA.id); auto& transactionQueue = mFlinger.getTransactionQueue(); ASSERT_EQ(1, transactionQueue.size()); @@ -272,9 +281,10 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { // different process) to re-query and reset the cached expected present time TransactionInfo empty; empty.applyToken = sp<IBinder>(); - mFlinger.setTransactionState(empty.states, empty.displays, empty.flags, empty.applyToken, - empty.inputWindowCommands, empty.desiredPresentTime, - empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks); + mFlinger.setTransactionState(empty.frameTimelineVsyncId, empty.states, empty.displays, + empty.flags, empty.applyToken, empty.inputWindowCommands, + empty.desiredPresentTime, empty.uncacheBuffer, + mHasListenerCallbacks, mCallbacks, empty.id); // flush transaction queue should flush as desiredPresentTime has // passed diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index c2a77527a1..0af5f30649 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -52,6 +52,7 @@ public: void setPeriod(nsecs_t) final {} void resetModel() final {} bool needsMoreSamples() const final { return false; } + bool isVSyncInPhase(nsecs_t, int) const final { return false; } void dump(std::string&) const final {} private: @@ -65,7 +66,7 @@ public: bool addVsyncTimestamp(nsecs_t) final { return true; } nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t time_point) const final { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); auto const normalized_to_base = time_point - mBase; auto const floor = (normalized_to_base) % mPeriod; if (floor == 0) { @@ -75,19 +76,20 @@ public: } void set_interval(nsecs_t interval, nsecs_t last_known) { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); mPeriod = interval; mBase = last_known; } nsecs_t currentPeriod() const final { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); return mPeriod; } void setPeriod(nsecs_t) final {} void resetModel() final {} bool needsMoreSamples() const final { return false; } + bool isVSyncInPhase(nsecs_t, int) const final { return false; } void dump(std::string&) const final {} private: @@ -104,30 +106,36 @@ struct VSyncDispatchRealtimeTest : testing::Test { class RepeatingCallbackReceiver { public: - RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t wl) - : mWorkload(wl), + RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t workload, nsecs_t readyDuration) + : mWorkload(workload), + mReadyDuration(readyDuration), mCallback( - dispatch, [&](auto time, auto) { callback_called(time); }, "repeat0") {} + dispatch, [&](auto time, auto, auto) { callback_called(time); }, "repeat0") {} void repeatedly_schedule(size_t iterations, std::function<void(nsecs_t)> const& onEachFrame) { mCallbackTimes.reserve(iterations); - mCallback.schedule(mWorkload, systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload); + mCallback.schedule( + {.workDuration = mWorkload, + .readyDuration = mReadyDuration, + .earliestVsync = systemTime(SYSTEM_TIME_MONOTONIC) + mWorkload + mReadyDuration}); for (auto i = 0u; i < iterations - 1; i++) { - std::unique_lock<decltype(mMutex)> lk(mMutex); - mCv.wait(lk, [&] { return mCalled; }); + std::unique_lock lock(mMutex); + mCv.wait(lock, [&] { return mCalled; }); mCalled = false; auto last = mLastTarget; - lk.unlock(); + lock.unlock(); onEachFrame(last); - mCallback.schedule(mWorkload, last + mWorkload); + mCallback.schedule({.workDuration = mWorkload, + .readyDuration = mReadyDuration, + .earliestVsync = last + mWorkload + mReadyDuration}); } // wait for the last callback. - std::unique_lock<decltype(mMutex)> lk(mMutex); - mCv.wait(lk, [&] { return mCalled; }); + std::unique_lock lock(mMutex); + mCv.wait(lock, [&] { return mCalled; }); } void with_callback_times(std::function<void(std::vector<nsecs_t> const&)> const& fn) const { @@ -136,7 +144,7 @@ public: private: void callback_called(nsecs_t time) { - std::lock_guard<decltype(mMutex)> lk(mMutex); + std::lock_guard lock(mMutex); mCallbackTimes.push_back(time); mCalled = true; mLastTarget = time; @@ -144,6 +152,7 @@ private: } nsecs_t const mWorkload; + nsecs_t const mReadyDuration; VSyncCallbackRegistration mCallback; std::mutex mMutex; @@ -160,9 +169,9 @@ TEST_F(VSyncDispatchRealtimeTest, triple_alarm) { static size_t constexpr num_clients = 3; std::array<RepeatingCallbackReceiver, num_clients> - cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us)), - RepeatingCallbackReceiver(dispatch, toNs(0h)), - RepeatingCallbackReceiver(dispatch, toNs(1ms))}; + cb_receiver{RepeatingCallbackReceiver(dispatch, toNs(1500us), toNs(2500us)), + RepeatingCallbackReceiver(dispatch, toNs(0h), toNs(0h)), + RepeatingCallbackReceiver(dispatch, toNs(1ms), toNs(3ms))}; auto const on_each_frame = [](nsecs_t) {}; std::array<std::thread, num_clients> threads{ @@ -187,7 +196,7 @@ TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) { VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold, mVsyncMoveThreshold); - RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms)); + RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms)); auto const on_each_frame = [&](nsecs_t last_known) { tracker.set_interval(next_vsync_interval += toNs(1ms), last_known); @@ -205,7 +214,7 @@ TEST_F(VSyncDispatchRealtimeTest, fixed_jump) { VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold, mVsyncMoveThreshold); - RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms)); + RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms)); auto jump_frame_counter = 0u; auto constexpr jump_frame_at = 10u; diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index f630e3bb46..72b5396047 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -48,6 +48,7 @@ public: MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const { @@ -109,22 +110,24 @@ public: CountingCallback(VSyncDispatch& dispatch) : mDispatch(dispatch), mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this, - std::placeholders::_1, - std::placeholders::_2), + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3), "test")) {} ~CountingCallback() { mDispatch.unregisterCallback(mToken); } operator VSyncDispatch::CallbackToken() const { return mToken; } - void counter(nsecs_t time, nsecs_t wakeup_time) { + void counter(nsecs_t time, nsecs_t wakeup_time, nsecs_t readyTime) { mCalls.push_back(time); mWakeupTime.push_back(wakeup_time); + mReadyTime.push_back(readyTime); } VSyncDispatch& mDispatch; VSyncDispatch::CallbackToken mToken; std::vector<nsecs_t> mCalls; std::vector<nsecs_t> mWakeupTime; + std::vector<nsecs_t> mReadyTime; }; class PausingCallback { @@ -142,18 +145,18 @@ public: operator VSyncDispatch::CallbackToken() const { return mToken; } void pause(nsecs_t, nsecs_t) { - std::unique_lock<std::mutex> lk(mMutex); + std::unique_lock lock(mMutex); mPause = true; mCv.notify_all(); - mCv.wait_for(lk, mPauseAmount, [this] { return !mPause; }); + mCv.wait_for(lock, mPauseAmount, [this] { return !mPause; }); mResourcePresent = (mResource.lock() != nullptr); } bool waitForPause() { - std::unique_lock<std::mutex> lk(mMutex); - auto waiting = mCv.wait_for(lk, 10s, [this] { return mPause; }); + std::unique_lock lock(mMutex); + auto waiting = mCv.wait_for(lock, 10s, [this] { return mPause; }); return waiting; } @@ -162,7 +165,7 @@ public: bool resourcePresent() { return mResourcePresent; } void unpause() { - std::unique_lock<std::mutex> lk(mMutex); + std::unique_lock lock(mMutex); mPause = false; mCv.notify_all(); } @@ -228,7 +231,11 @@ TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) { VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold, mVsyncMoveThreshold}; CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = 1000}), + ScheduleResult::Scheduled); } } @@ -237,7 +244,11 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, 100, intended), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = intended}), + ScheduleResult::Scheduled); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -249,7 +260,7 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueV EXPECT_CALL(mMockClock, alarmAt(_, 1050)); CountingCallback cb(mDispatch); - mDispatch.schedule(cb, 100, mPeriod); + mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -265,7 +276,11 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) { EXPECT_CALL(mMockClock, alarmAt(_, mPeriod)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, workDuration, mPeriod), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = workDuration, + .readyDuration = 0, + .earliestVsync = mPeriod}), + ScheduleResult::Scheduled); } TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { @@ -273,7 +288,11 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = mPeriod}), + ScheduleResult::Scheduled); EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled); } @@ -282,7 +301,11 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = mPeriod}), + ScheduleResult::Scheduled); mMockClock.advanceBy(950); EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate); } @@ -292,7 +315,11 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) { EXPECT_CALL(mMockClock, alarmCancel()); PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s)); - EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = mPeriod}), + ScheduleResult::Scheduled); std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); EXPECT_TRUE(cb.waitForPause()); @@ -309,7 +336,11 @@ TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { PausingCallback cb(mDispatch, 50ms); cb.stashResource(resource); - EXPECT_EQ(mDispatch.schedule(cb, 100, mPeriod), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = mPeriod}), + ScheduleResult::Scheduled); std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); EXPECT_TRUE(cb.waitForPause()); @@ -339,8 +370,8 @@ TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, 100, mPeriod); - mDispatch.schedule(cb1, 250, mPeriod); + mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); advanceToNextCallback(); advanceToNextCallback(); @@ -367,8 +398,9 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, 100, mPeriod * 10); - mDispatch.schedule(cb1, 250, mPeriod); + mDispatch.schedule(cb0, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10}); + mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); mDispatch.cancel(cb1); } @@ -380,9 +412,9 @@ TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, 400, 1000); - mDispatch.schedule(cb1, 200, 1000); - mDispatch.schedule(cb1, 300, 1000); + mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); } @@ -395,9 +427,9 @@ TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, 400, 1000); - mDispatch.schedule(cb1, 200, 1000); - mDispatch.schedule(cb1, 500, 1000); + mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); } @@ -415,9 +447,10 @@ TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, 400, 1000); - mDispatch.schedule(cb1, 200, 1000); - mDispatch.schedule(cb1, closeOffset, 1000); + mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, + {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); ASSERT_THAT(cb0.mCalls.size(), Eq(1)); @@ -425,8 +458,9 @@ TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { ASSERT_THAT(cb1.mCalls.size(), Eq(1)); EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod)); - mDispatch.schedule(cb0, 400, 2000); - mDispatch.schedule(cb1, notCloseOffset, 2000); + mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch.schedule(cb1, + {.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000}); advanceToNextCallback(); ASSERT_THAT(cb1.mCalls.size(), Eq(2)); EXPECT_THAT(cb1.mCalls[1], Eq(2000)); @@ -446,8 +480,8 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, 100, 1000); - mDispatch.schedule(cb1, 200, 1000); + mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled); } @@ -460,18 +494,18 @@ TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) .WillOnce(Return(2950)); CountingCallback cb(mDispatch); - mDispatch.schedule(cb, 100, 920); + mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920}); mMockClock.advanceBy(850); EXPECT_THAT(cb.mCalls.size(), Eq(1)); - mDispatch.schedule(cb, 100, 1900); + mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900}); mMockClock.advanceBy(900); EXPECT_THAT(cb.mCalls.size(), Eq(1)); mMockClock.advanceBy(125); EXPECT_THAT(cb.mCalls.size(), Eq(2)); - mDispatch.schedule(cb, 100, 2900); + mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900}); mMockClock.advanceBy(975); EXPECT_THAT(cb.mCalls.size(), Eq(3)); } @@ -482,10 +516,16 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) { EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); VSyncDispatch::CallbackToken tmp; - tmp = mDispatch.registerCallback([&](auto, auto) { mDispatch.schedule(tmp, 100, 2000); }, - "o.o"); + tmp = mDispatch.registerCallback( + [&](auto, auto, auto) { + mDispatch.schedule(tmp, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = 2000}); + }, + "o.o"); - mDispatch.schedule(tmp, 100, 1000); + mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); } @@ -493,17 +533,27 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) { VSyncDispatch::CallbackToken tmp; std::optional<nsecs_t> lastTarget; tmp = mDispatch.registerCallback( - [&](auto timestamp, auto) { - EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold), + [&](auto timestamp, auto, auto) { + EXPECT_EQ(mDispatch.schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .earliestVsync = timestamp - mVsyncMoveThreshold}), + ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .earliestVsync = timestamp}), ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold), + EXPECT_EQ(mDispatch.schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .earliestVsync = timestamp + mVsyncMoveThreshold}), ScheduleResult::Scheduled); lastTarget = timestamp; }, "oo"); - mDispatch.schedule(tmp, 999, 1000); + mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); EXPECT_THAT(lastTarget, Eq(1000)); @@ -519,16 +569,16 @@ TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) { EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); CountingCallback cb(mDispatch); - mDispatch.schedule(cb, 0, 1000); + mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000}); mMockClock.advanceBy(750); - mDispatch.schedule(cb, 50, 1000); + mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); - mDispatch.schedule(cb, 50, 2000); + mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000}); mMockClock.advanceBy(800); - mDispatch.schedule(cb, 100, 2000); + mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); } TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { @@ -541,12 +591,12 @@ TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, 500, 1000); - mDispatch.schedule(cb1, 100, 1000); + mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); - mDispatch.schedule(cb0, 200, 2000); - mDispatch.schedule(cb1, 150, 1000); + mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); advanceToNextCallback(); @@ -558,8 +608,8 @@ TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, 500, 1000); - mDispatch.schedule(cb1, 500, 20000); + mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000}); } TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { @@ -569,31 +619,43 @@ TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); CountingCallback cb0(mDispatch); - mDispatch.schedule(cb0, 500, 1000); + mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); mDispatch.cancel(cb0); - mDispatch.schedule(cb0, 100, 1000); + mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); } TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) { VSyncDispatch::CallbackToken token(100); - EXPECT_THAT(mDispatch.schedule(token, 100, 1000), Eq(ScheduleResult::Error)); + EXPECT_THAT(mDispatch.schedule(token, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = 1000}), + Eq(ScheduleResult::Error)); EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error)); } TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) { CountingCallback cb0(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb0, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); } // b/1450138150 TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) { EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); mMockClock.advanceBy(400); - EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); } @@ -604,16 +666,24 @@ TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedul .WillOnce(Return(1000)) .WillOnce(Return(1002)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); mMockClock.advanceBy(400); - EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); } TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) { CountingCallback cb0(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); advanceToNextCallback(); - EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb0, + {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}), + ScheduleResult::Scheduled); } TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { @@ -621,18 +691,26 @@ TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq); CountingCallback cb0(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); advanceToNextCallback(); - EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb0, + {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}), + ScheduleResult::Scheduled); } TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) { EXPECT_CALL(mMockClock, alarmAt(_, 600)); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); advanceToNextCallback(); } @@ -642,12 +720,12 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMove) { EXPECT_CALL(mMockClock, alarmCancel()).Times(1); VSyncCallbackRegistration cb( - mDispatch, [](auto, auto) {}, ""); + mDispatch, [](auto, auto, auto) {}, ""); VSyncCallbackRegistration cb1(std::move(cb)); - cb.schedule(100, 1000); + cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); cb.cancel(); - cb1.schedule(500, 1000); + cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); cb1.cancel(); } @@ -656,14 +734,14 @@ TEST_F(VSyncDispatchTimerQueueTest, helperMoveAssign) { EXPECT_CALL(mMockClock, alarmCancel()).Times(1); VSyncCallbackRegistration cb( - mDispatch, [](auto, auto) {}, ""); + mDispatch, [](auto, auto, auto) {}, ""); VSyncCallbackRegistration cb1( - mDispatch, [](auto, auto) {}, ""); + mDispatch, [](auto, auto, auto) {}, ""); cb1 = std::move(cb); - cb.schedule(100, 1000); + cb.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); cb.cancel(); - cb1.schedule(500, 1000); + cb1.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); cb1.cancel(); } @@ -675,12 +753,16 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent CountingCallback cb1(mDispatch); CountingCallback cb2(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(620); - EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}), + ScheduleResult::Scheduled); mMockClock.advanceBy(80); EXPECT_THAT(cb1.mCalls.size(), Eq(1)); @@ -696,12 +778,16 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent EXPECT_CALL(mMockClock, alarmAt(_, 1630)).InSequence(seq); CountingCallback cb(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(620); - EXPECT_EQ(mDispatch.schedule(cb, 370, 2000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}), + ScheduleResult::Scheduled); mMockClock.advanceBy(80); EXPECT_THAT(cb.mCalls.size(), Eq(1)); @@ -715,8 +801,12 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) { CountingCallback cb1(mDispatch); CountingCallback cb2(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}), + ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(620); @@ -737,8 +827,12 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) { CountingCallback cb1(mDispatch); CountingCallback cb2(mDispatch); - EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb2, 100, 2000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}), + ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(620); @@ -766,16 +860,44 @@ TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) { .InSequence(seq) .WillOnce(Return(1000)); - EXPECT_EQ(mDispatch.schedule(cb1, 400, 1000), ScheduleResult::Scheduled); - EXPECT_EQ(mDispatch.schedule(cb2, 390, 1000), ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); + EXPECT_EQ(mDispatch.schedule(cb2, + {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}), + ScheduleResult::Scheduled); mMockClock.setLag(100); mMockClock.advanceBy(700); ASSERT_THAT(cb1.mWakeupTime.size(), Eq(1)); EXPECT_THAT(cb1.mWakeupTime[0], Eq(600)); + ASSERT_THAT(cb1.mReadyTime.size(), Eq(1)); + EXPECT_THAT(cb1.mReadyTime[0], Eq(1000)); ASSERT_THAT(cb2.mWakeupTime.size(), Eq(1)); EXPECT_THAT(cb2.mWakeupTime[0], Eq(610)); + ASSERT_THAT(cb2.mReadyTime.size(), Eq(1)); + EXPECT_THAT(cb2.mReadyTime[0], Eq(1000)); +} + +TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) { + auto intended = mPeriod - 230; + EXPECT_CALL(mMockClock, alarmAt(_, 900)); + + CountingCallback cb(mDispatch); + EXPECT_EQ(mDispatch.schedule(cb, + {.workDuration = 70, + .readyDuration = 30, + .earliestVsync = intended}), + ScheduleResult::Scheduled); + advanceToNextCallback(); + + ASSERT_THAT(cb.mCalls.size(), Eq(1)); + EXPECT_THAT(cb.mCalls[0], Eq(mPeriod)); + ASSERT_THAT(cb.mWakeupTime.size(), Eq(1)); + EXPECT_THAT(cb.mWakeupTime[0], 900); + ASSERT_THAT(cb.mReadyTime.size(), Eq(1)); + EXPECT_THAT(cb.mReadyTime[0], 970); } class VSyncDispatchTimerQueueEntryTest : public testing::Test { @@ -788,7 +910,7 @@ protected: TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) { std::string name("basicname"); VSyncDispatchTimerQueueEntry entry( - name, [](auto, auto) {}, mVsyncMoveThreshold); + name, [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_THAT(entry.name(), Eq(name)); EXPECT_FALSE(entry.lastExecutedVsyncTarget()); EXPECT_FALSE(entry.wakeupTime()); @@ -796,10 +918,12 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) { TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) { VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto) {}, mVsyncMoveThreshold); + "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(900)); @@ -816,10 +940,12 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) .Times(1) .WillOnce(Return(10000)); VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto) {}, mVsyncMoveThreshold); + "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994}, + mStubTracker, now), + Eq(ScheduleResult::Scheduled)); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(9500)); @@ -829,21 +955,29 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { auto callCount = 0; auto vsyncCalledTime = 0; auto wakeupCalledTime = 0; + auto readyCalledTime = 0; VSyncDispatchTimerQueueEntry entry( "test", - [&](auto vsyncTime, auto wakeupTime) { + [&](auto vsyncTime, auto wakeupTime, auto readyTime) { callCount++; vsyncCalledTime = vsyncTime; wakeupCalledTime = wakeupTime; + readyCalledTime = readyTime; }, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(900)); - entry.callback(entry.executing(), *wakeup); + auto const ready = entry.readyTime(); + ASSERT_TRUE(ready); + EXPECT_THAT(*ready, Eq(1000)); + + entry.callback(entry.executing(), *wakeup, *ready); EXPECT_THAT(callCount, Eq(1)); EXPECT_THAT(vsyncCalledTime, Eq(mPeriod)); @@ -861,13 +995,15 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { .WillOnce(Return(1020)); VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto) {}, mVsyncMoveThreshold); + "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); entry.update(mStubTracker, 0); EXPECT_FALSE(entry.wakeupTime()); - EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); auto wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(wakeup, Eq(900)); @@ -880,8 +1016,10 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto) {}, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); + EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); entry.update(mStubTracker, 0); auto const wakeup = entry.wakeupTime(); @@ -891,24 +1029,35 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) { VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto) {}, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); + EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); entry.executing(); // 1000 is executing // had 1000 not been executing, this could have been scheduled for time 800. - EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); + EXPECT_THAT(*entry.readyTime(), Eq(2000)); - EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); EXPECT_THAT(*entry.wakeupTime(), Eq(1950)); + EXPECT_THAT(*entry.readyTime(), Eq(2000)); - EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); + EXPECT_THAT(*entry.readyTime(), Eq(2000)); } TEST_F(VSyncDispatchTimerQueueEntryTest, willRequestNextEstimateWhenSnappingToNextTargettableVSync) { VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto) {}, mVsyncMoveThreshold); + "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); Sequence seq; EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500)) @@ -921,35 +1070,85 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, .InSequence(seq) .WillOnce(Return(2000)); - EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); entry.executing(); // 1000 is executing - EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); } TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) { VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto) {}, mVsyncMoveThreshold); - EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); - EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); - EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); - EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled)); + "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); + EXPECT_THAT(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); + EXPECT_THAT(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); } TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) { static constexpr auto effectualOffset = 200; VSyncDispatchTimerQueueEntry entry( - "test", [](auto, auto) {}, mVsyncMoveThreshold); + "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); - entry.addPendingWorkloadUpdate(100, 400); - entry.addPendingWorkloadUpdate(effectualOffset, 700); + entry.addPendingWorkloadUpdate({.workDuration = 100, .readyDuration = 0, .earliestVsync = 400}); + entry.addPendingWorkloadUpdate( + {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400}); EXPECT_TRUE(entry.hasPendingWorkloadUpdate()); entry.update(mStubTracker, 0); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset)); } +TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) { + auto callCount = 0; + auto vsyncCalledTime = 0; + auto wakeupCalledTime = 0; + auto readyCalledTime = 0; + VSyncDispatchTimerQueueEntry entry( + "test", + [&](auto vsyncTime, auto wakeupTime, auto readyTime) { + callCount++; + vsyncCalledTime = vsyncTime; + wakeupCalledTime = wakeupTime; + readyCalledTime = readyTime; + }, + mVsyncMoveThreshold); + + EXPECT_THAT(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500}, + mStubTracker, 0), + Eq(ScheduleResult::Scheduled)); + auto const wakeup = entry.wakeupTime(); + ASSERT_TRUE(wakeup); + EXPECT_THAT(*wakeup, Eq(900)); + + auto const ready = entry.readyTime(); + ASSERT_TRUE(ready); + EXPECT_THAT(*ready, Eq(970)); + + entry.callback(entry.executing(), *wakeup, *ready); + + EXPECT_THAT(callCount, Eq(1)); + EXPECT_THAT(vsyncCalledTime, Eq(mPeriod)); + EXPECT_THAT(wakeupCalledTime, Eq(*wakeup)); + EXPECT_FALSE(entry.wakeupTime()); + auto lastCalledTarget = entry.lastExecutedVsyncTarget(); + ASSERT_TRUE(lastCalledTarget); + EXPECT_THAT(*lastCalledTarget, Eq(mPeriod)); +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp deleted file mode 100644 index 9c1ec07084..0000000000 --- a/services/surfaceflinger/tests/unittests/VSyncModulatorTest.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "LibSurfaceFlingerUnittests" -#define LOG_NDEBUG 0 - -#include "Scheduler/VSyncModulator.h" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -using namespace testing; - -namespace android::scheduler { - -class MockScheduler : public IPhaseOffsetControl { -public: - void setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) { - mPhaseOffset[handle] = phaseOffset; - } - - nsecs_t getOffset(ConnectionHandle handle) { return mPhaseOffset[handle]; } - -private: - std::unordered_map<ConnectionHandle, nsecs_t> mPhaseOffset; -}; - -class VSyncModulatorTest : public testing::Test { -protected: - static constexpr auto MIN_EARLY_FRAME_COUNT_TRANSACTION = - VSyncModulator::MIN_EARLY_FRAME_COUNT_TRANSACTION; - // Add a 1ms slack to avoid strange timer race conditions. - static constexpr auto MARGIN_FOR_TX_APPLY = VSyncModulator::MARGIN_FOR_TX_APPLY + 1ms; - - // Used to enumerate the different offsets we have - enum { - SF_LATE, - APP_LATE, - SF_EARLY, - APP_EARLY, - SF_EARLY_GL, - APP_EARLY_GL, - }; - - std::unique_ptr<VSyncModulator> mVSyncModulator; - MockScheduler mMockScheduler; - ConnectionHandle mAppConnection{1}; - ConnectionHandle mSfConnection{2}; - VSyncModulator::OffsetsConfig mOffsets = {{SF_EARLY, APP_EARLY}, - {SF_EARLY_GL, APP_EARLY_GL}, - {SF_LATE, APP_LATE}}; - - void SetUp() override { - mVSyncModulator = std::make_unique<VSyncModulator>(mMockScheduler, mAppConnection, - mSfConnection, mOffsets); - mVSyncModulator->setPhaseOffsets(mOffsets); - - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); - }; - - void TearDown() override { mVSyncModulator.reset(); } -}; - -TEST_F(VSyncModulatorTest, Normal) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); - } -} - -TEST_F(VSyncModulatorTest, EarlyEnd) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); -} - -TEST_F(VSyncModulatorTest, EarlyStart) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); -} - -TEST_F(VSyncModulatorTest, EarlyStartWithEarly) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Early); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); -} - -TEST_F(VSyncModulatorTest, EarlyStartWithMoreTransactions) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); -} - -TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEnd) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); -} - -TEST_F(VSyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyStart); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < 5 * MIN_EARLY_FRAME_COUNT_TRANSACTION; i++) { - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::Normal); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->setTransactionStart(Scheduler::TransactionStart::EarlyEnd); - std::this_thread::sleep_for(MARGIN_FOR_TX_APPLY); - mVSyncModulator->onTransactionHandled(); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - - for (int i = 0; i < MIN_EARLY_FRAME_COUNT_TRANSACTION - 1; i++) { - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_EARLY, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_EARLY, mMockScheduler.getOffset(mSfConnection)); - } - - mVSyncModulator->onRefreshed(false); - EXPECT_EQ(APP_LATE, mMockScheduler.getOffset(mAppConnection)); - EXPECT_EQ(SF_LATE, mMockScheduler.getOffset(mSfConnection)); -} - -} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index d4cd11dbe1..3d60479111 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -59,16 +59,16 @@ struct VSyncPredictorTest : testing::Test { }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { - auto [slope, intercept] = tracker.getVSyncPredictionModel(); + auto model = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(mPeriod)); - EXPECT_THAT(intercept, Eq(0)); + EXPECT_THAT(model.slope, Eq(mPeriod)); + EXPECT_THAT(model.intercept, Eq(0)); auto const changedPeriod = 2000; tracker.setPeriod(changedPeriod); - std::tie(slope, intercept) = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(changedPeriod)); - EXPECT_THAT(intercept, Eq(0)); + model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, Eq(changedPeriod)); + EXPECT_THAT(model.intercept, Eq(0)); } TEST_F(VSyncPredictorTest, reportsSamplesNeededWhenHasNoDataPoints) { @@ -264,17 +264,17 @@ TEST_F(VSyncPredictorTest, handlesVsyncChange) { } auto const mMaxRoundingError = 100; - auto [slope, intercept] = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, IsCloseTo(fastPeriod, mMaxRoundingError)); - EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError)); + auto model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, IsCloseTo(fastPeriod, mMaxRoundingError)); + EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError)); tracker.setPeriod(slowPeriod); for (auto const& timestamp : simulatedVsyncsSlow) { tracker.addVsyncTimestamp(timestamp); } - std::tie(slope, intercept) = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, IsCloseTo(slowPeriod, mMaxRoundingError)); - EXPECT_THAT(intercept, IsCloseTo(0, mMaxRoundingError)); + model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, IsCloseTo(slowPeriod, mMaxRoundingError)); + EXPECT_THAT(model.intercept, IsCloseTo(0, mMaxRoundingError)); } TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { @@ -296,9 +296,9 @@ TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { for (auto const& timestamp : simulatedVsyncsFast) { tracker.addVsyncTimestamp(timestamp); } - auto [slope, intercept] = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(fastPeriod)); - EXPECT_THAT(intercept, Eq(0)); + auto model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, Eq(fastPeriod)); + EXPECT_THAT(model.intercept, Eq(0)); tracker.setPeriod(slowPeriod); for (auto const& timestamp : simulatedVsyncsSlow) { @@ -308,16 +308,16 @@ TEST_F(VSyncPredictorTest, willBeAccurateUsingPriorResultsForRate) { // we had a model for 100ns mPeriod before, use that until the new samples are // sufficiently built up tracker.setPeriod(idealPeriod); - std::tie(slope, intercept) = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(fastPeriod)); - EXPECT_THAT(intercept, Eq(0)); + model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, Eq(fastPeriod)); + EXPECT_THAT(model.intercept, Eq(0)); for (auto const& timestamp : simulatedVsyncsFast2) { tracker.addVsyncTimestamp(timestamp); } - std::tie(slope, intercept) = tracker.getVSyncPredictionModel(); - EXPECT_THAT(slope, Eq(fastPeriod2)); - EXPECT_THAT(intercept, Eq(0)); + model = tracker.getVSyncPredictionModel(); + EXPECT_THAT(model.slope, Eq(fastPeriod2)); + EXPECT_THAT(model.intercept, Eq(0)); } TEST_F(VSyncPredictorTest, idealModelPredictionsBeforeRegressionModelIsBuilt) { @@ -407,11 +407,9 @@ TEST_F(VSyncPredictorTest, resetsWhenInstructed) { tracker.addVsyncTimestamp(i * realPeriod); } - EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()), - IsCloseTo(realPeriod, mMaxRoundingError)); + EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(realPeriod, mMaxRoundingError)); tracker.resetModel(); - EXPECT_THAT(std::get<0>(tracker.getVSyncPredictionModel()), - IsCloseTo(idealPeriod, mMaxRoundingError)); + EXPECT_THAT(tracker.getVSyncPredictionModel().slope, IsCloseTo(idealPeriod, mMaxRoundingError)); } TEST_F(VSyncPredictorTest, slopeAlwaysValid) { @@ -450,6 +448,33 @@ TEST_F(VSyncPredictorTest, aPhoneThatHasBeenAroundAWhileCanStillComputePeriod) { EXPECT_THAT(intercept, Eq(0)); } +TEST_F(VSyncPredictorTest, isVSyncInPhase) { + auto last = mNow; + auto const bias = 10; + for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); + mNow += mPeriod - bias; + last = mNow; + tracker.addVsyncTimestamp(mNow); + mNow += bias; + } + + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod - bias)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod - bias)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 990), Eq(mNow + 2 * mPeriod - bias)); + + const auto maxDivider = 5; + const auto maxPeriods = 15; + for (int divider = 1; divider < maxDivider; divider++) { + for (int i = 0; i < maxPeriods; i++) { + const bool expectedInPhase = (i % divider) == 0; + EXPECT_THAT(expectedInPhase, tracker.isVSyncInPhase(mNow + i * mPeriod - bias, divider)) + << "vsync at " << mNow + (i + 1) * mPeriod - bias << " is " + << (expectedInPhase ? "not " : "") << "in phase for divider " << divider; + } + } +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 6856612b78..a7568e4293 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -42,29 +42,10 @@ public: MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; -class VSyncTrackerWrapper : public VSyncTracker { -public: - VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {} - - bool addVsyncTimestamp(nsecs_t timestamp) final { - return mTracker->addVsyncTimestamp(timestamp); - } - nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final { - return mTracker->nextAnticipatedVSyncTimeFrom(timePoint); - } - nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); } - void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); } - void resetModel() final { mTracker->resetModel(); } - bool needsMoreSamples() const final { return mTracker->needsMoreSamples(); } - void dump(std::string& result) const final { mTracker->dump(result); } - -private: - std::shared_ptr<VSyncTracker> const mTracker; -}; - class MockClock : public Clock { public: MOCK_CONST_METHOD0(now, nsecs_t()); @@ -83,89 +64,46 @@ private: class MockVSyncDispatch : public VSyncDispatch { public: MOCK_METHOD2(registerCallback, - CallbackToken(std::function<void(nsecs_t, nsecs_t)> const&, std::string)); + CallbackToken(std::function<void(nsecs_t, nsecs_t, nsecs_t)> const&, std::string)); MOCK_METHOD1(unregisterCallback, void(CallbackToken)); - MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t)); + MOCK_METHOD2(schedule, ScheduleResult(CallbackToken, ScheduleTiming)); MOCK_METHOD1(cancel, CancelResult(CallbackToken token)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; -class VSyncDispatchWrapper : public VSyncDispatch { -public: - VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {} - CallbackToken registerCallback(std::function<void(nsecs_t, nsecs_t)> const& callbackFn, - std::string callbackName) final { - return mDispatch->registerCallback(callbackFn, callbackName); - } - - void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); } - - ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, - nsecs_t earliestVsync) final { - return mDispatch->schedule(token, workDuration, earliestVsync); - } - - CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); } - - void dump(std::string& result) const final { return mDispatch->dump(result); } - -private: - std::shared_ptr<VSyncDispatch> const mDispatch; -}; - -std::shared_ptr<FenceTime> generateInvalidFence() { +std::shared_ptr<android::FenceTime> generateInvalidFence() { sp<Fence> fence = new Fence(); - return std::make_shared<FenceTime>(fence); + return std::make_shared<android::FenceTime>(fence); } -std::shared_ptr<FenceTime> generatePendingFence() { +std::shared_ptr<android::FenceTime> generatePendingFence() { sp<Fence> fence = new Fence(dup(fileno(tmpfile()))); - return std::make_shared<FenceTime>(fence); + return std::make_shared<android::FenceTime>(fence); } -void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) { - FenceTime::Snapshot snap(time); +void signalFenceWithTime(std::shared_ptr<android::FenceTime> const& fence, nsecs_t time) { + android::FenceTime::Snapshot snap(time); fence->applyTrustedSnapshot(snap); } -std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) { +std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) { sp<Fence> fence = new Fence(dup(fileno(tmpfile()))); - std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence); + std::shared_ptr<android::FenceTime> ft = std::make_shared<android::FenceTime>(fence); signalFenceWithTime(ft, time); return ft; } -class StubCallback : public DispSync::Callback { -public: - void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final { - std::lock_guard<std::mutex> lk(mMutex); - mLastCallTime = when; - } - std::optional<nsecs_t> lastCallTime() const { - std::lock_guard<std::mutex> lk(mMutex); - return mLastCallTime; - } - -private: - std::mutex mutable mMutex; - std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex); -}; - class VSyncReactorTest : public testing::Test { protected: VSyncReactorTest() - : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()), - mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()), + : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()), mMockClock(std::make_shared<NiceMock<MockClock>>()), - mReactor(std::make_unique<ClockWrapper>(mMockClock), - std::make_unique<VSyncDispatchWrapper>(mMockDispatch), - std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit, + mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit, false /* supportKernelIdleTimer */) { ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow)); ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period)); } - std::shared_ptr<MockVSyncDispatch> mMockDispatch; std::shared_ptr<MockVSyncTracker> mMockTracker; std::shared_ptr<MockClock> mMockClock; static constexpr size_t kPendingLimit = 3; @@ -180,7 +118,7 @@ protected: VSyncDispatch::CallbackToken const mFakeToken{2398}; nsecs_t lastCallbackTime = 0; - StubCallback outerCb; + // StubCallback outerCb; std::function<void(nsecs_t, nsecs_t)> innerCb; VSyncReactor mReactor; @@ -215,7 +153,7 @@ TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) { } TEST_F(VSyncReactorTest, limitsPendingFences) { - std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences; + std::array<std::shared_ptr<android::FenceTime>, kPendingLimit * 2> fences; std::array<nsecs_t, fences.size()> fakeTimes; std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); }); std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable { @@ -256,86 +194,48 @@ TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); - EXPECT_TRUE(mReactor.addResyncSample(0, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - EXPECT_FALSE(mReactor.addResyncSample(newPeriod, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } -TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) { - nsecs_t const fakeTimestamp = 4839; - EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0); - EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_)) - .Times(1) - .WillOnce(Return(fakeTimestamp)); - - EXPECT_THAT(mReactor.computeNextRefresh(0, mMockClock->now()), Eq(fakeTimestamp)); -} - -TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) { - nsecs_t const fakeTimestamp = 4839; - EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0); - EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_)) - .Times(1) - .WillOnce(Return(fakeTimestamp)); - - EXPECT_THAT(mReactor.expectedPresentTime(mMockClock->now()), Eq(fakeTimestamp)); -} - -TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) { - nsecs_t const fakeTimestamp = 4839; - nsecs_t const fakePeriod = 1010; - nsecs_t const mFakeNow = 2214; - int const numPeriodsOut = 3; - EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow)); - EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod)); - EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod)) - .WillOnce(Return(fakeTimestamp)); - EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut, mMockClock->now()), Eq(fakeTimestamp)); -} - -TEST_F(VSyncReactorTest, getPeriod) { - nsecs_t const fakePeriod = 1010; - EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod)); - EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod)); -} - TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { nsecs_t const newPeriod = 5000; EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); bool periodFlushed = true; - EXPECT_TRUE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - EXPECT_TRUE(mReactor.addResyncSample(20000, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(20000, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); Mock::VerifyAndClearExpectations(mMockTracker.get()); EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1); - EXPECT_FALSE(mReactor.addResyncSample(25000, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(25000, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); } TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) { nsecs_t sampleTime = 0; nsecs_t const newPeriod = 5000; - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); bool periodFlushed = true; - EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - mReactor.setPeriod(period); - EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed)); + mReactor.startPeriodTransition(period); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } @@ -344,16 +244,18 @@ TEST_F(VSyncReactorTest, changingToAThirdPeriodWillWaitForLastPeriod) { nsecs_t const secondPeriod = 5000; nsecs_t const thirdPeriod = 2000; - mReactor.setPeriod(secondPeriod); + mReactor.startPeriodTransition(secondPeriod); bool periodFlushed = true; - EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - EXPECT_TRUE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - mReactor.setPeriod(thirdPeriod); - EXPECT_TRUE(mReactor.addResyncSample(sampleTime += secondPeriod, std::nullopt, &periodFlushed)); + mReactor.startPeriodTransition(thirdPeriod); + EXPECT_TRUE( + mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - EXPECT_FALSE(mReactor.addResyncSample(sampleTime += thirdPeriod, std::nullopt, &periodFlushed)); + EXPECT_FALSE( + mReactor.addHwVsyncTimestamp(sampleTime += thirdPeriod, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); } @@ -368,9 +270,10 @@ TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSync) nsecs_t skewyPeriod = period >> 1; bool periodFlushed = false; nsecs_t sampleTime = 0; - EXPECT_TRUE(mReactor.addResyncSample(sampleTime += skewyPeriod, std::nullopt, &periodFlushed)); + EXPECT_TRUE( + mReactor.addHwVsyncTimestamp(sampleTime += skewyPeriod, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - EXPECT_FALSE(mReactor.addResyncSample(sampleTime += period, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } @@ -388,22 +291,22 @@ TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncP TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) { nsecs_t const newPeriod = 5000; - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) { nsecs_t const newPeriod = 5000; EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); bool periodFlushed = true; - EXPECT_TRUE(mReactor.addResyncSample(5000, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); Mock::VerifyAndClearExpectations(mMockTracker.get()); EXPECT_CALL(*mMockTracker, setPeriod(newPeriod)).Times(1); - EXPECT_FALSE(mReactor.addResyncSample(10000, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); } @@ -412,7 +315,7 @@ TEST_F(VSyncReactorTest, addResyncSampleTypical) { bool periodFlushed = false; EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp)); - EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(fakeTimestamp, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } @@ -420,23 +323,23 @@ TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) { bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); auto time = 0; auto constexpr numTimestampSubmissions = 10; for (auto i = 0; i < numTimestampSubmissions; i++) { time += period; - EXPECT_TRUE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } time += newPeriod; - EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); EXPECT_TRUE(periodFlushed); for (auto i = 0; i < numTimestampSubmissions; i++) { time += newPeriod; - EXPECT_FALSE(mReactor.addResyncSample(time, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } } @@ -445,14 +348,14 @@ TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsH auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); time += period; - mReactor.addResyncSample(time, std::nullopt, &periodFlushed); + mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); time += newPeriod; - mReactor.addResyncSample(time, std::nullopt, &periodFlushed); + mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed); EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } @@ -461,7 +364,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); static auto constexpr numSamplesWithNewPeriod = 4; Sequence seq; @@ -475,20 +378,20 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) { .WillRepeatedly(Return(false)); EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(numSamplesWithNewPeriod); - EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); - EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); // confirmed period, but predictor wants numRequest samples. This one and prior are valid. - EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed)); - EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed)); - EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed)); } TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) @@ -497,9 +400,9 @@ TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) .WillRepeatedly(Return(false)); EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2); - EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); - EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); - EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod, std::nullopt, &periodFlushed)); } TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { @@ -508,7 +411,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { nsecs_t const newPeriod1 = 4000; nsecs_t const newPeriod2 = 7000; - mReactor.setPeriod(newPeriod1); + mReactor.startPeriodTransition(newPeriod1); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) @@ -521,208 +424,17 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { .WillRepeatedly(Return(false)); EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(7); - EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); - EXPECT_TRUE(mReactor.addResyncSample(time += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += period, std::nullopt, &periodFlushed)); // confirmed period, but predictor wants numRequest samples. This one and prior are valid. - EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed)); - EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed)); - - mReactor.setPeriod(newPeriod2); - EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod1, std::nullopt, &periodFlushed)); - EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed)); - EXPECT_TRUE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed)); - EXPECT_FALSE(mReactor.addResyncSample(time += newPeriod2, std::nullopt, &periodFlushed)); -} - -static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) { - return period - phase; -} - -TEST_F(VSyncReactorTest, addEventListener) { - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(Return(mFakeToken)); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq); - EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - mReactor.removeEventListener(&outerCb, &lastCallbackTime); -} - -TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) { - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(Return(mFakeToken)); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, - schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq); - EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime); -} - -TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) { - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken))); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, - schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime)) - .Times(2) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq); - EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - ASSERT_TRUE(innerCb); - innerCb(mFakeVSyncTime, mFakeWakeupTime); - innerCb(mFakeVSyncTime, mFakeWakeupTime); -} - -TEST_F(VSyncReactorTest, callbackTimestampDistributedIsWakeupTime) { - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, _)) - .InSequence(seq) - .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken))); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, - schedule(mFakeToken, computeWorkload(period, mPhase), mFakeVSyncTime)) - .InSequence(seq); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - ASSERT_TRUE(innerCb); - innerCb(mFakeVSyncTime, mFakeWakeupTime); - EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeWakeupTime)); -} - -TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) { - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(Return(mFakeToken)); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq); - EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); -} - -// b/149221293 -TEST_F(VSyncReactorTest, selfRemovingEventListenerStopsCallbacks) { - class SelfRemovingCallback : public DispSync::Callback { - public: - SelfRemovingCallback(VSyncReactor& vsr) : mVsr(vsr) {} - void onDispSyncEvent(nsecs_t when, nsecs_t /*expectedVSyncTimestamp*/) final { - mVsr.removeEventListener(this, &when); - } + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); - private: - VSyncReactor& mVsr; - } selfRemover(mReactor); - - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken))); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq); - EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq); - - mReactor.addEventListener(mName, mPhase, &selfRemover, lastCallbackTime); - innerCb(0, 0); -} - -TEST_F(VSyncReactorTest, addEventListenerChangePeriod) { - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(Return(mFakeToken)); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, - schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq); - EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime); -} - -TEST_F(VSyncReactorTest, changingPeriodChangesOffsetsOnNextCb) { - static constexpr nsecs_t anotherPeriod = 23333; - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(Return(mFakeToken)); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow)) - .InSequence(seq); - EXPECT_CALL(*mMockTracker, setPeriod(anotherPeriod)); - EXPECT_CALL(*mMockDispatch, - schedule(mFakeToken, computeWorkload(anotherPeriod, mPhase), mFakeNow)) - .InSequence(seq); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - - bool periodFlushed = false; - mReactor.setPeriod(anotherPeriod); - EXPECT_TRUE(mReactor.addResyncSample(anotherPeriod, std::nullopt, &periodFlushed)); - EXPECT_FALSE(mReactor.addResyncSample(anotherPeriod * 2, std::nullopt, &periodFlushed)); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); -} - -TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) { - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken))); - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _)) - .InSequence(seq) - .WillOnce(Return(ScheduleResult::Scheduled)); - - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) - .InSequence(seq) - .WillOnce(Return(ScheduleResult::Scheduled)); - - EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) - .InSequence(seq) - .WillOnce(Return(ScheduleResult::Scheduled)); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - mReactor.changePhaseOffset(&outerCb, mAnotherPhase); - ASSERT_TRUE(innerCb); - innerCb(mFakeVSyncTime, mFakeWakeupTime); -} - -TEST_F(VSyncReactorTest, negativeOffsetsApplied) { - nsecs_t const negativePhase = -4000; - Sequence seq; - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .InSequence(seq) - .WillOnce(Return(mFakeToken)); - EXPECT_CALL(*mMockDispatch, - schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow)) - .InSequence(seq); - mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime); -} - -TEST_F(VSyncReactorTest, beginResyncResetsModel) { - EXPECT_CALL(*mMockTracker, resetModel()); - mReactor.beginResync(); + mReactor.startPeriodTransition(newPeriod2); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); } TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { @@ -731,13 +443,13 @@ TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; - mReactor.setPeriod(newPeriod); + mReactor.startPeriodTransition(newPeriod); - EXPECT_TRUE(mReactor.addResyncSample(0, 0, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); - EXPECT_TRUE(mReactor.addResyncSample(newPeriod, 0, &periodFlushed)); + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(newPeriod, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); - EXPECT_FALSE(mReactor.addResyncSample(newPeriod, newPeriod, &periodFlushed)); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, newPeriod, &periodFlushed)); EXPECT_TRUE(periodFlushed); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); @@ -745,9 +457,7 @@ TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // Create a reactor which supports the kernel idle timer - auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), - std::make_unique<VSyncDispatchWrapper>(mMockDispatch), - std::make_unique<VSyncTrackerWrapper>(mMockTracker), + auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit, true /* supportKernelIdleTimer */); bool periodFlushed = true; @@ -756,66 +466,28 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // First, set the same period, which should only be confirmed when we receive two // matching callbacks - idleReactor.setPeriod(10000); - EXPECT_TRUE(idleReactor.addResyncSample(0, 0, &periodFlushed)); + idleReactor.startPeriodTransition(10000); + EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Correct period but incorrect timestamp delta - EXPECT_TRUE(idleReactor.addResyncSample(0, 10000, &periodFlushed)); + EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 10000, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Correct period and correct timestamp delta - EXPECT_FALSE(idleReactor.addResyncSample(10000, 10000, &periodFlushed)); + EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(10000, 10000, &periodFlushed)); EXPECT_TRUE(periodFlushed); // Then, set a new period, which should be confirmed as soon as we receive a callback // reporting the new period nsecs_t const newPeriod = 5000; - idleReactor.setPeriod(newPeriod); + idleReactor.startPeriodTransition(newPeriod); // Incorrect timestamp delta and period - EXPECT_TRUE(idleReactor.addResyncSample(20000, 10000, &periodFlushed)); + EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Incorrect timestamp delta but correct period - EXPECT_FALSE(idleReactor.addResyncSample(20000, 5000, &periodFlushed)); + EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(20000, 5000, &periodFlushed)); EXPECT_TRUE(periodFlushed); EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0))); } -using VSyncReactorDeathTest = VSyncReactorTest; -TEST_F(VSyncReactorDeathTest, invalidRemoval) { - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - mReactor.removeEventListener(&outerCb, &lastCallbackTime); - EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*"); -} - -TEST_F(VSyncReactorDeathTest, invalidChange) { - EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*"); - - // the current DispSync-interface usage pattern has evolved around an implementation quirk, - // which is a callback is assumed to always exist, and it is valid api usage to change the - // offset of an object that is in the removed state. - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - mReactor.removeEventListener(&outerCb, &lastCallbackTime); - mReactor.changePhaseOffset(&outerCb, mPhase); -} - -TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) { - ON_CALL(*mMockDispatch, schedule(_, _, _)) - .WillByDefault(Return(ScheduleResult::CannotSchedule)); - EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*"); -} - -TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) { - EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName))) - .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken))); - EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled)); - - mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime); - ASSERT_TRUE(innerCb); - Mock::VerifyAndClearExpectations(mMockDispatch.get()); - - ON_CALL(*mMockDispatch, schedule(_, _, _)) - .WillByDefault(Return(ScheduleResult::CannotSchedule)); - EXPECT_DEATH(innerCb(mFakeVSyncTime, mFakeWakeupTime), ".*"); -} - } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp new file mode 100644 index 0000000000..72ee6db737 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp @@ -0,0 +1,306 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "SchedulerUnittests" + +#include <gmock/gmock.h> +#include <log/log.h> +#include <thread> + +#include "Scheduler/VsyncConfiguration.h" + +using namespace testing; + +namespace android::scheduler { + +class TestableWorkDuration : public impl::WorkDuration { +public: + TestableWorkDuration(float currentFps, nsecs_t sfDuration, nsecs_t appDuration, + nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, + nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration) + : impl::WorkDuration({60.0f, 90.0f}, currentFps, sfDuration, appDuration, sfEarlyDuration, + appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration) {} +}; + +class WorkDurationTest : public testing::Test { +protected: + WorkDurationTest() + : mWorkDuration(60.0f, 10'500'000, 20'500'000, 16'000'000, 16'500'000, 13'500'000, + 21'000'000) {} + + ~WorkDurationTest() = default; + + TestableWorkDuration mWorkDuration; +}; + +/* ------------------------------------------------------------------------ + * Test cases + */ +TEST_F(WorkDurationTest, getConfigsForRefreshRate_60Hz) { + mWorkDuration.setRefreshRateFps(60.0f); + auto currentOffsets = mWorkDuration.getCurrentConfigs(); + auto offsets = mWorkDuration.getConfigsForRefreshRate(60.0f); + + EXPECT_EQ(currentOffsets, offsets); + EXPECT_EQ(offsets.late.sfOffset, 6'166'667); + EXPECT_EQ(offsets.late.appOffset, 2'333'334); + + EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns); + EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns); + + EXPECT_EQ(offsets.early.sfOffset, 666'667); + EXPECT_EQ(offsets.early.appOffset, 833'334); + + EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns); + EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'166'667); + EXPECT_EQ(offsets.earlyGpu.appOffset, 15'500'001); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns); +} + +TEST_F(WorkDurationTest, getConfigsForRefreshRate_90Hz) { + mWorkDuration.setRefreshRateFps(90.0f); + auto currentOffsets = mWorkDuration.getCurrentConfigs(); + auto offsets = mWorkDuration.getConfigsForRefreshRate(90.0f); + + EXPECT_EQ(currentOffsets, offsets); + EXPECT_EQ(offsets.late.sfOffset, 611'111); + EXPECT_EQ(offsets.late.appOffset, 2'333'333); + + EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns); + EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns); + + EXPECT_EQ(offsets.early.sfOffset, -4'888'889); + EXPECT_EQ(offsets.early.appOffset, 833'333); + + EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns); + EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, -2'388'889); + EXPECT_EQ(offsets.earlyGpu.appOffset, 9'944'444); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns); +} + +TEST_F(WorkDurationTest, getConfigsForRefreshRate_DefaultOffsets) { + TestableWorkDuration phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1); + + auto validateOffsets = [](const auto& offsets, std::chrono::nanoseconds vsyncPeriod) { + EXPECT_EQ(offsets.late.sfOffset, 1'000'000); + EXPECT_EQ(offsets.late.appOffset, 1'000'000); + + EXPECT_EQ(offsets.late.sfWorkDuration, vsyncPeriod - 1'000'000ns); + EXPECT_EQ(offsets.late.appWorkDuration, vsyncPeriod); + + EXPECT_EQ(offsets.early.sfOffset, 1'000'000); + EXPECT_EQ(offsets.early.appOffset, 1'000'000); + + EXPECT_EQ(offsets.early.sfWorkDuration, vsyncPeriod - 1'000'000ns); + EXPECT_EQ(offsets.early.appWorkDuration, vsyncPeriod); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000); + EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, vsyncPeriod - 1'000'000ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, vsyncPeriod); + }; + + const auto testForRefreshRate = [&](float refreshRate) { + phaseOffsetsWithDefaultValues.setRefreshRateFps(refreshRate); + auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentConfigs(); + auto offsets = phaseOffsetsWithDefaultValues.getConfigsForRefreshRate(refreshRate); + EXPECT_EQ(currentOffsets, offsets); + validateOffsets(offsets, + std::chrono::nanoseconds(static_cast<nsecs_t>(1e9f / refreshRate))); + }; + + testForRefreshRate(90.0f); + testForRefreshRate(60.0f); +} + +TEST_F(WorkDurationTest, getConfigsForRefreshRate_unknownRefreshRate) { + auto offsets = mWorkDuration.getConfigsForRefreshRate(14.7f); + + EXPECT_EQ(offsets.late.sfOffset, 57'527'208); + EXPECT_EQ(offsets.late.appOffset, 37'027'208); + + EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns); + EXPECT_EQ(offsets.late.appWorkDuration, 20'500'000ns); + + EXPECT_EQ(offsets.early.sfOffset, 52'027'208); + EXPECT_EQ(offsets.early.appOffset, 35'527'208); + + EXPECT_EQ(offsets.early.sfWorkDuration, 16'000'000ns); + EXPECT_EQ(offsets.early.appWorkDuration, 16'500'000ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, 54'527'208); + EXPECT_EQ(offsets.earlyGpu.appOffset, 33'527'208); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 13'500'000ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'000'000ns); +} + +class TestablePhaseOffsets : public impl::PhaseOffsets { +public: + TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs, + std::optional<nsecs_t> earlySfOffsetNs, + std::optional<nsecs_t> earlyGpuSfOffsetNs, + std::optional<nsecs_t> earlyAppOffsetNs, + std::optional<nsecs_t> earlyGpuAppOffsetNs, + nsecs_t highFpsVsyncPhaseOffsetNs, nsecs_t highFpsSfVSyncPhaseOffsetNs, + std::optional<nsecs_t> highFpsEarlySfOffsetNs, + std::optional<nsecs_t> highFpsEarlyGpuSfOffsetNs, + std::optional<nsecs_t> highFpsEarlyAppOffsetNs, + std::optional<nsecs_t> highFpsEarlyGpuAppOffsetNs, + nsecs_t thresholdForNextVsync) + : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, vsyncPhaseOffsetNs, sfVSyncPhaseOffsetNs, + earlySfOffsetNs, earlyGpuSfOffsetNs, earlyAppOffsetNs, + earlyGpuAppOffsetNs, highFpsVsyncPhaseOffsetNs, + highFpsSfVSyncPhaseOffsetNs, highFpsEarlySfOffsetNs, + highFpsEarlyGpuSfOffsetNs, highFpsEarlyAppOffsetNs, + highFpsEarlyGpuAppOffsetNs, thresholdForNextVsync) {} +}; + +class PhaseOffsetsTest : public testing::Test { +protected: + PhaseOffsetsTest() = default; + ~PhaseOffsetsTest() = default; + + TestablePhaseOffsets mPhaseOffsets{2'000'000, 6'000'000, 7'000'000, 8'000'000, 3'000'000, + 4'000'000, 2'000'000, 1'000'000, 2'000'000, 3'000'000, + 3'000'000, 4'000'000, 10'000'000}; +}; + +TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_unknownRefreshRate) { + auto offsets = mPhaseOffsets.getConfigsForRefreshRate(14.7f); + + EXPECT_EQ(offsets.late.sfOffset, 6'000'000); + EXPECT_EQ(offsets.late.appOffset, 2'000'000); + + EXPECT_EQ(offsets.late.sfWorkDuration, 62'027'208ns); + EXPECT_EQ(offsets.late.appWorkDuration, 72'027'208ns); + + EXPECT_EQ(offsets.early.sfOffset, 7'000'000); + EXPECT_EQ(offsets.early.appOffset, 3'000'000); + + EXPECT_EQ(offsets.early.sfWorkDuration, 61'027'208ns); + EXPECT_EQ(offsets.early.appWorkDuration, 72'027'208ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000); + EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 60'027'208ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 72'027'208ns); +} + +TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_60Hz) { + auto offsets = mPhaseOffsets.getConfigsForRefreshRate(60.0f); + + EXPECT_EQ(offsets.late.sfOffset, 6'000'000); + EXPECT_EQ(offsets.late.appOffset, 2'000'000); + + EXPECT_EQ(offsets.late.sfWorkDuration, 10'666'667ns); + EXPECT_EQ(offsets.late.appWorkDuration, 20'666'667ns); + + EXPECT_EQ(offsets.early.sfOffset, 7'000'000); + EXPECT_EQ(offsets.early.appOffset, 3'000'000); + + EXPECT_EQ(offsets.early.sfWorkDuration, 9'666'667ns); + EXPECT_EQ(offsets.early.appWorkDuration, 20'666'667ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, 8'000'000); + EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'666'667ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 20'666'667ns); +} + +TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_90Hz) { + auto offsets = mPhaseOffsets.getConfigsForRefreshRate(90.0f); + + EXPECT_EQ(offsets.late.sfOffset, 1'000'000); + EXPECT_EQ(offsets.late.appOffset, 2'000'000); + + EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns); + EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns); + + EXPECT_EQ(offsets.early.sfOffset, 2'000'000); + EXPECT_EQ(offsets.early.appOffset, 3'000'000); + + EXPECT_EQ(offsets.early.sfWorkDuration, 9'111'111ns); + EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, 3'000'000); + EXPECT_EQ(offsets.earlyGpu.appOffset, 4'000'000); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 8'111'111ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns); +} + +TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_60Hz) { + TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000, + 1'000'000, {}, {}, {}, {}, 10'000'000}; + auto offsets = phaseOffsets.getConfigsForRefreshRate(60.0f); + + EXPECT_EQ(offsets.late.sfOffset, 1'000'000); + EXPECT_EQ(offsets.late.appOffset, 1'000'000); + + EXPECT_EQ(offsets.late.sfWorkDuration, 15'666'667ns); + EXPECT_EQ(offsets.late.appWorkDuration, 16'666'667ns); + + EXPECT_EQ(offsets.early.sfOffset, 1'000'000); + EXPECT_EQ(offsets.early.appOffset, 1'000'000); + + EXPECT_EQ(offsets.early.sfWorkDuration, 15'666'667ns); + EXPECT_EQ(offsets.early.appWorkDuration, 16'666'667ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000); + EXPECT_EQ(offsets.earlyGpu.appOffset, 1'000'000); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 15'666'667ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'666'667ns); +} + +TEST_F(PhaseOffsetsTest, getConfigsForRefreshRate_DefaultValues_90Hz) { + TestablePhaseOffsets phaseOffsets{1'000'000, 1'000'000, {}, {}, {}, {}, 2'000'000, + 1'000'000, {}, {}, {}, {}, 10'000'000}; + auto offsets = phaseOffsets.getConfigsForRefreshRate(90.0f); + + EXPECT_EQ(offsets.late.sfOffset, 1'000'000); + EXPECT_EQ(offsets.late.appOffset, 2'000'000); + + EXPECT_EQ(offsets.late.sfWorkDuration, 10'111'111ns); + EXPECT_EQ(offsets.late.appWorkDuration, 21'222'222ns); + + EXPECT_EQ(offsets.early.sfOffset, 1'000'000); + EXPECT_EQ(offsets.early.appOffset, 2'000'000); + + EXPECT_EQ(offsets.early.sfWorkDuration, 10'111'111ns); + EXPECT_EQ(offsets.early.appWorkDuration, 21'222'222ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, 1'000'000); + EXPECT_EQ(offsets.earlyGpu.appOffset, 2'000'000); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 10'111'111ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 21'222'222ns); +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp new file mode 100644 index 0000000000..106da81076 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/VsyncModulatorTest.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "Scheduler/VsyncModulator.h" + +namespace android::scheduler { + +class VsyncModulatorTest : public testing::Test { + enum { + SF_OFFSET_LATE, + APP_OFFSET_LATE, + SF_DURATION_LATE, + APP_DURATION_LATE, + SF_OFFSET_EARLY, + APP_OFFSET_EARLY, + SF_DURATION_EARLY, + APP_DURATION_EARLY, + SF_OFFSET_EARLY_GPU, + APP_OFFSET_EARLY_GPU, + SF_DURATION_EARLY_GPU, + APP_DURATION_EARLY_GPU, + }; + + static VsyncModulator::TimePoint Now() { + static VsyncModulator::TimePoint now; + return now += VsyncModulator::MIN_EARLY_TRANSACTION_TIME; + } + +protected: + static constexpr auto MIN_EARLY_TRANSACTION_FRAMES = + VsyncModulator::MIN_EARLY_TRANSACTION_FRAMES; + + using Schedule = scheduler::TransactionSchedule; + using nanos = std::chrono::nanoseconds; + const VsyncModulator::VsyncConfig kEarly{SF_OFFSET_EARLY, APP_OFFSET_EARLY, + nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)}; + const VsyncModulator::VsyncConfig kEarlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU, + nanos(SF_DURATION_EARLY), + nanos(APP_DURATION_EARLY)}; + const VsyncModulator::VsyncConfig kLate{SF_OFFSET_LATE, APP_OFFSET_LATE, + nanos(SF_DURATION_EARLY_GPU), + nanos(APP_DURATION_EARLY_GPU)}; + + const VsyncModulator::VsyncConfigSet mOffsets = {kEarly, kEarlyGpu, kLate}; + VsyncModulator mVsyncModulator{mOffsets, Now}; + + void SetUp() override { EXPECT_EQ(kLate, mVsyncModulator.setVsyncConfigSet(mOffsets)); } +}; + +#define CHECK_COMMIT(result, configs) \ + EXPECT_EQ(result, mVsyncModulator.onTransactionCommit()); \ + EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig()); + +#define CHECK_REFRESH(N, result, configs) \ + for (int i = 0; i < N; i++) { \ + EXPECT_EQ(result, mVsyncModulator.onDisplayRefresh(false)); \ + EXPECT_EQ(configs, mVsyncModulator.getVsyncConfig()); \ + } + +TEST_F(VsyncModulatorTest, Late) { + EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late)); + + CHECK_COMMIT(std::nullopt, kLate); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kLate); +} + +TEST_F(VsyncModulatorTest, EarlyEnd) { + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly); + CHECK_REFRESH(1, kLate, kLate); +} + +TEST_F(VsyncModulatorTest, EarlyStart) { + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly); + + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly); + CHECK_REFRESH(1, kLate, kLate); +} + +TEST_F(VsyncModulatorTest, EarlyStartWithEarly) { + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly); + + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::Early)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly); + + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly); + CHECK_REFRESH(1, kLate, kLate); +} + +TEST_F(VsyncModulatorTest, EarlyStartWithMoreTransactions) { + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart)); + + CHECK_COMMIT(kEarly, kEarly); + + for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) { + EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late)); + CHECK_REFRESH(1, std::nullopt, kEarly); + } + + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly); + CHECK_REFRESH(1, kLate, kLate); +} + +TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEnd) { + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly); + + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(1, kEarly, kEarly); + CHECK_REFRESH(5 * MIN_EARLY_TRANSACTION_FRAMES, std::nullopt, kEarly); + + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly); + CHECK_REFRESH(1, kLate, kLate); +} + +TEST_F(VsyncModulatorTest, EarlyStartAfterEarlyEndWithMoreTransactions) { + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly); + + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyStart)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(1, kEarly, kEarly); + + for (int i = 0; i < 5 * MIN_EARLY_TRANSACTION_FRAMES; i++) { + EXPECT_FALSE(mVsyncModulator.setTransactionSchedule(Schedule::Late)); + CHECK_REFRESH(1, std::nullopt, kEarly); + } + + EXPECT_EQ(kEarly, mVsyncModulator.setTransactionSchedule(Schedule::EarlyEnd)); + + CHECK_COMMIT(kEarly, kEarly); + CHECK_REFRESH(MIN_EARLY_TRANSACTION_FRAMES - 1, kEarly, kEarly); + CHECK_REFRESH(1, kLate, kLate); +} + +} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp index 0780af1f26..251ab36d14 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.cpp @@ -20,17 +20,13 @@ #include "mock/DisplayHardware/MockComposer.h" -namespace android { -namespace Hwc2 { -namespace mock { +namespace android::Hwc2::mock { // Explicit default instantiation is recommended. Composer::Composer() = default; Composer::~Composer() = default; -} // namespace mock -} // namespace Hwc2 -} // namespace android +} // namespace android::Hwc2::mock // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index c2c5072b24..1ba3c0f56e 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -24,8 +24,7 @@ namespace android { class GraphicBuffer; -namespace Hwc2 { -namespace mock { +namespace Hwc2::mock { using android::hardware::graphics::common::V1_0::ColorTransform; using android::hardware::graphics::common::V1_0::Transform; @@ -52,11 +51,9 @@ public: MOCK_METHOD0(getCapabilities, std::vector<IComposer::Capability>()); MOCK_METHOD0(dumpDebugInfo, std::string()); MOCK_METHOD1(registerCallback, void(const sp<IComposerCallback>&)); - MOCK_METHOD0(isRemote, bool()); MOCK_METHOD0(resetCommands, void()); MOCK_METHOD0(executeCommands, Error()); MOCK_METHOD0(getMaxVirtualDisplayCount, uint32_t()); - MOCK_CONST_METHOD0(isUsingVrComposer, bool()); MOCK_METHOD4(createVirtualDisplay, Error(uint32_t, uint32_t, PixelFormat*, Display*)); MOCK_METHOD1(destroyVirtualDisplay, Error(Display)); MOCK_METHOD1(acceptDisplayChanges, Error(Display)); @@ -110,7 +107,6 @@ public: MOCK_METHOD3(setLayerVisibleRegion, Error(Display, Layer, const std::vector<IComposerClient::Rect>&)); MOCK_METHOD3(setLayerZOrder, Error(Display, Layer, uint32_t)); - MOCK_METHOD4(setLayerInfo, Error(Display, Layer, uint32_t, uint32_t)); MOCK_METHOD3(getRenderIntents, Error(Display, ColorMode, std::vector<RenderIntent>*)); MOCK_METHOD3(setLayerColorTransform, Error(Display, Layer, const float*)); MOCK_METHOD4(getDisplayedContentSamplingAttributes, @@ -143,6 +139,5 @@ public: MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*)); }; -} // namespace mock -} // namespace Hwc2 +} // namespace Hwc2::mock } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp index 2ec37c1e76..c9788afbe4 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.cpp @@ -16,14 +16,10 @@ #include "mock/DisplayHardware/MockDisplay.h" -namespace android { -namespace Hwc2 { -namespace mock { +namespace android::Hwc2::mock { // Explicit default instantiation is recommended. Display::Display() = default; Display::~Display() = default; -} // namespace mock -} // namespace Hwc2 -} // namespace android
\ No newline at end of file +} // namespace android::Hwc2::mock
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h index fe99e77158..a96d9dbe2a 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h @@ -22,9 +22,7 @@ using android::HWC2::Layer; -namespace android { -namespace Hwc2 { -namespace mock { +namespace android::Hwc2::mock { namespace hal = android::hardware::graphics::composer::hal; @@ -98,6 +96,4 @@ public: MOCK_CONST_METHOD0(isVsyncPeriodSwitchSupported, bool()); }; -} // namespace mock -} // namespace Hwc2 -} // namespace android +} // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp index 8be707750b..1ba38a822a 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp @@ -16,14 +16,10 @@ #include "MockPowerAdvisor.h" -namespace android { -namespace Hwc2 { -namespace mock { +namespace android::Hwc2::mock { // Explicit default instantiation is recommended. PowerAdvisor::PowerAdvisor() = default; PowerAdvisor::~PowerAdvisor() = default; -} // namespace mock -} // namespace Hwc2 -} // namespace android +} // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h index e22d0cf74c..7450b5dfa4 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h @@ -20,9 +20,7 @@ #include "DisplayHardware/PowerAdvisor.h" -namespace android { -namespace Hwc2 { -namespace mock { +namespace android::Hwc2::mock { class PowerAdvisor : public android::Hwc2::PowerAdvisor { public: @@ -34,6 +32,4 @@ public: MOCK_METHOD0(notifyDisplayUpdateImminent, void()); }; -} // namespace mock -} // namespace Hwc2 -} // namespace android +} // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp deleted file mode 100644 index 1c8c44dca0..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mock/MockDispSync.h" -#include <thread> - -using namespace std::chrono_literals; -namespace android { -namespace mock { - -// Explicit default instantiation is recommended. -DispSync::DispSync() = default; -DispSync::~DispSync() = default; - -status_t DispSync::addEventListener(const char* /*name*/, nsecs_t phase, Callback* callback, - nsecs_t /*lastCallbackTime*/) { - if (mCallback.callback != nullptr) { - return BAD_VALUE; - } - - mCallback = {callback, phase}; - return NO_ERROR; -} -status_t DispSync::removeEventListener(Callback* callback, nsecs_t* /*outLastCallback*/) { - if (mCallback.callback != callback) { - return BAD_VALUE; - } - - mCallback = {nullptr, 0}; - return NO_ERROR; -} - -status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { - if (mCallback.callback != callback) { - return BAD_VALUE; - } - - mCallback.phase = phase; - return NO_ERROR; -} - -void DispSync::triggerCallback() { - if (mCallback.callback == nullptr) return; - - const std::chrono::nanoseconds now = std::chrono::steady_clock::now().time_since_epoch(); - const auto expectedVSyncTime = now + 16ms; - mCallback.callback->onDispSyncEvent(now.count(), expectedVSyncTime.count()); -} - -} // namespace mock -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h deleted file mode 100644 index b39487ccc1..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <gmock/gmock.h> - -#include "Scheduler/DispSync.h" - -namespace android { -namespace mock { - -class DispSync : public android::DispSync { -public: - DispSync(); - ~DispSync() override; - - MOCK_METHOD0(reset, void()); - MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&)); - MOCK_METHOD0(beginResync, void()); - MOCK_METHOD3(addResyncSample, bool(nsecs_t, std::optional<nsecs_t>, bool*)); - MOCK_METHOD0(endResync, void()); - MOCK_METHOD1(setPeriod, void(nsecs_t)); - MOCK_METHOD0(getPeriod, nsecs_t()); - MOCK_METHOD0(getIntendedPeriod, nsecs_t()); - MOCK_METHOD1(setRefreshSkipCount, void(int)); - MOCK_CONST_METHOD2(computeNextRefresh, nsecs_t(int, nsecs_t)); - MOCK_METHOD1(setIgnorePresentFences, void(bool)); - MOCK_METHOD1(expectedPresentTime, nsecs_t(nsecs_t)); - - MOCK_CONST_METHOD1(dump, void(std::string&)); - - status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, - nsecs_t lastCallbackTime) override; - status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) override; - status_t changePhaseOffset(Callback* callback, nsecs_t phase) override; - - nsecs_t getCallbackPhase() { return mCallback.phase; } - - void triggerCallback(); - -private: - struct CallbackType { - Callback* callback = nullptr; - nsecs_t phase; - }; - CallbackType mCallback; -}; - -} // namespace mock -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h new file mode 100644 index 0000000000..cfc37ea8d9 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayIdGenerator.h @@ -0,0 +1,36 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "DisplayIdGenerator.h" + +namespace android::mock { + +template <typename T> +class DisplayIdGenerator : public android::DisplayIdGenerator<T> { +public: + // Explicit default instantiation is recommended. + DisplayIdGenerator() = default; + virtual ~DisplayIdGenerator() = default; + + MOCK_METHOD0(nextId, std::optional<T>()); + MOCK_METHOD1(markUnused, void(T)); +}; + +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp index 408cd35f29..302dc01b23 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.cpp @@ -16,12 +16,10 @@ #include "mock/MockEventThread.h" -namespace android { -namespace mock { +namespace android::mock { // Explicit default instantiation is recommended. EventThread::EventThread() = default; EventThread::~EventThread() = default; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 054aaf8ae1..b4594c183d 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -20,8 +20,7 @@ #include "Scheduler/EventThread.h" -namespace android { -namespace mock { +namespace android::mock { class EventThread : public android::EventThread { public: @@ -35,7 +34,9 @@ public: MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool)); MOCK_METHOD3(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType, nsecs_t)); MOCK_CONST_METHOD1(dump, void(std::string&)); - MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset)); + MOCK_METHOD2(setDuration, + void(std::chrono::nanoseconds workDuration, + std::chrono::nanoseconds readyDuration)); MOCK_METHOD1(registerDisplayEventConnection, status_t(const sp<android::EventThreadConnection> &)); MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &)); @@ -45,5 +46,4 @@ public: MOCK_METHOD0(getEventThreadConnectionCount, size_t()); }; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp index 358dfdb856..417dcb0869 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp @@ -16,12 +16,10 @@ #include "mock/MockFrameTracer.h" -namespace android { -namespace mock { +namespace android::mock { // Explicit default instantiation is recommended. FrameTracer::FrameTracer() = default; FrameTracer::~FrameTracer() = default; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h index f768b8114d..305cb1c7c6 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h @@ -20,8 +20,7 @@ #include "FrameTracer/FrameTracer.h" -namespace android { -namespace mock { +namespace android::mock { class FrameTracer : public android::FrameTracer { public: @@ -39,5 +38,4 @@ public: MOCK_METHOD0(miniDump, std::string()); }; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h index a82b583d6a..efaa9fa4eb 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h +++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h @@ -18,6 +18,7 @@ #include <gmock/gmock.h> +#include "FrameTimeline.h" #include "Scheduler/EventThread.h" #include "Scheduler/MessageQueue.h" @@ -34,6 +35,10 @@ public: MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&)); MOCK_METHOD0(invalidate, void()); MOCK_METHOD0(refresh, void()); + MOCK_METHOD3(initVsync, + void(scheduler::VSyncDispatch&, frametimeline::TokenManager&, + std::chrono::nanoseconds)); + MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h new file mode 100644 index 0000000000..72bc89cb85 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -0,0 +1,42 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "Scheduler/Scheduler.h" + +namespace android::mock { + +struct SchedulerCallback final : ISchedulerCallback { + MOCK_METHOD1(setVsyncEnabled, void(bool)); + MOCK_METHOD2(changeRefreshRate, + void(const scheduler::RefreshRateConfigs::RefreshRate&, + scheduler::RefreshRateConfigEvent)); + MOCK_METHOD0(repaintEverythingForHWC, void()); + MOCK_METHOD1(kernelTimerChanged, void(bool)); +}; + +struct NoOpSchedulerCallback final : ISchedulerCallback { + void setVsyncEnabled(bool) override {} + void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&, + scheduler::RefreshRateConfigEvent) override {} + void repaintEverythingForHWC() override {} + void kernelTimerChanged(bool) override {} +}; + +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp index 7e925b94ee..0a0e7b5861 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.cpp @@ -20,15 +20,13 @@ #include "mock/MockSurfaceInterceptor.h" -namespace android { -namespace mock { +namespace android::mock { // Explicit default instantiation is recommended. SurfaceInterceptor::SurfaceInterceptor() = default; SurfaceInterceptor::~SurfaceInterceptor() = default; -} // namespace mock -} // namespace android +} // namespace android::mock // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h index 5beee1c0ec..b085027397 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h @@ -20,8 +20,7 @@ #include "SurfaceInterceptor.h" -namespace android { -namespace mock { +namespace android::mock { class SurfaceInterceptor : public android::SurfaceInterceptor { public: @@ -33,10 +32,12 @@ public: const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&)); MOCK_METHOD0(disable, void()); MOCK_METHOD0(isEnabled, bool()); - MOCK_METHOD4(saveTransaction, + MOCK_METHOD1(addTransactionTraceListener, void(const sp<gui::ITransactionTraceListener>&)); + MOCK_METHOD1(binderDied, void(const wp<IBinder>&)); + MOCK_METHOD7(saveTransaction, void(const Vector<ComposerState>&, const DefaultKeyedVector<wp<IBinder>, DisplayDeviceState>&, - const Vector<DisplayState>&, uint32_t)); + const Vector<DisplayState>&, uint32_t, int, int, uint64_t)); MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&)); MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&)); MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t)); @@ -46,5 +47,4 @@ public: MOCK_METHOD1(saveVSyncEvent, void(nsecs_t)); }; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp index d686939b3a..f8e76b290b 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp @@ -16,12 +16,10 @@ #include "mock/MockTimeStats.h" -namespace android { -namespace mock { +namespace android::mock { // Explicit default instantiation is recommended. TimeStats::TimeStats() = default; TimeStats::~TimeStats() = default; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h index 4186e2b574..99ec353095 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -20,8 +20,7 @@ #include "TimeStats/TimeStats.h" -namespace android { -namespace mock { +namespace android::mock { class TimeStats : public android::TimeStats { public: @@ -42,7 +41,7 @@ public: MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t)); MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t)); MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&)); - MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t)); + MOCK_METHOD5(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t)); MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason)); MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId)); MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t)); @@ -51,6 +50,8 @@ public: 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_METHOD1(onDestroy, void(int32_t)); MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t)); MOCK_METHOD1(setPowerMode, @@ -59,5 +60,4 @@ public: MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&)); }; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp index f9bacc8b20..bcccae5b1b 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.cpp @@ -14,14 +14,12 @@ * limitations under the License. */ -#include "mock/MockEventControlThread.h" +#include "mock/MockVSyncTracker.h" -namespace android { -namespace mock { +namespace android::mock { // Explicit default instantiation is recommended. -EventControlThread::EventControlThread() = default; -EventControlThread::~EventControlThread() = default; +VSyncTracker::VSyncTracker() = default; +VSyncTracker::~VSyncTracker() = default; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h new file mode 100644 index 0000000000..de98025fd8 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -0,0 +1,40 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "Scheduler/VSyncTracker.h" + +namespace android::mock { + +class VSyncTracker : public android::scheduler::VSyncTracker { +public: + VSyncTracker(); + ~VSyncTracker() override; + + MOCK_METHOD1(addVsyncTimestamp, bool(nsecs_t)); + MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t)); + MOCK_CONST_METHOD0(currentPeriod, nsecs_t()); + MOCK_METHOD1(setPeriod, void(nsecs_t)); + MOCK_METHOD0(resetModel, void()); + MOCK_CONST_METHOD0(needsMoreSamples, bool()); + MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, int)); + MOCK_CONST_METHOD1(dump, void(std::string&)); +}; + +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp index 6ef352a548..25ae1bd312 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventControlThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.cpp @@ -14,22 +14,14 @@ * limitations under the License. */ -#pragma once +#include "mock/MockVsyncController.h" +#include <thread> -#include <gmock/gmock.h> +using namespace std::chrono_literals; +namespace android::mock { -#include "Scheduler/EventControlThread.h" +// Explicit default instantiation is recommended. +VsyncController::VsyncController() = default; +VsyncController::~VsyncController() = default; -namespace android { -namespace mock { - -class EventControlThread : public android::EventControlThread { -public: - EventControlThread(); - ~EventControlThread() override; - - MOCK_METHOD1(setVsyncEnabled, void(bool)); -}; - -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h new file mode 100644 index 0000000000..94d99665ce --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "Scheduler/VsyncController.h" + +namespace android::mock { + +class VsyncController : public android::scheduler::VsyncController { +public: + VsyncController(); + ~VsyncController() override; + + MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&)); + MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*)); + MOCK_METHOD1(startPeriodTransition, void(nsecs_t)); + MOCK_METHOD1(setIgnorePresentFences, void(bool)); + + MOCK_CONST_METHOD1(dump, void(std::string&)); +}; + +} // namespace android::mock diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h index 5480b00a4d..a13f93bb4c 100644 --- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h +++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h @@ -15,8 +15,10 @@ */ #pragma once +#include <gui/SyncScreenCaptureListener.h> #include <ui/Rect.h> #include <utils/String8.h> +#include <functional> #include "TransactionUtils.h" namespace android { @@ -27,51 +29,58 @@ namespace { // individual pixel values for testing purposes. class ScreenCapture : public RefBase { public: + static status_t captureDisplay(DisplayCaptureArgs& captureArgs, + ScreenCaptureResults& captureResults) { + const auto sf = ComposerService::getComposerService(); + SurfaceComposerClient::Transaction().apply(true); + + captureArgs.dataspace = ui::Dataspace::V0_SRGB; + const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + status_t status = sf->captureDisplay(captureArgs, captureListener); + + if (status != NO_ERROR) { + return status; + } + captureResults = captureListener->waitForResults(); + return captureResults.result; + } + static void captureScreen(std::unique_ptr<ScreenCapture>* sc) { captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken()); } static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) { - const auto sf = ComposerService::getComposerService(); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false)); - *sc = std::make_unique<ScreenCapture>(outBuffer); + DisplayCaptureArgs args; + args.displayToken = displayToken; + captureDisplay(sc, args); } - static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, - Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale)); - *sc = std::make_unique<ScreenCapture>(outBuffer); + static void captureDisplay(std::unique_ptr<ScreenCapture>* sc, + DisplayCaptureArgs& captureArgs) { + ScreenCaptureResults captureResults; + ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults)); + *sc = std::make_unique<ScreenCapture>(captureResults.buffer); } - static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, - Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + static status_t captureLayers(LayerCaptureArgs& captureArgs, + ScreenCaptureResults& captureResults) { + const auto sf = ComposerService::getComposerService(); SurfaceComposerClient::Transaction().apply(true); - sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true)); - *sc = std::make_unique<ScreenCapture>(outBuffer); + captureArgs.dataspace = ui::Dataspace::V0_SRGB; + const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener(); + status_t status = sf->captureLayers(captureArgs, captureListener); + if (status != NO_ERROR) { + return status; + } + captureResults = captureListener->waitForResults(); + return captureResults.result; } - static void captureChildLayersExcluding( - std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, - std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, - sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers, - 1.0f, true)); - *sc = std::make_unique<ScreenCapture>(outBuffer); + static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) { + ScreenCaptureResults captureResults; + ASSERT_EQ(NO_ERROR, captureLayers(captureArgs, captureResults)); + *sc = std::make_unique<ScreenCapture>(captureResults.buffer); } void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) { diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h index 8e1f94397f..5c5b18ec1c 100644 --- a/services/surfaceflinger/tests/utils/TransactionUtils.h +++ b/services/surfaceflinger/tests/utils/TransactionUtils.h @@ -16,6 +16,9 @@ #pragma once +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" + #include <chrono> #include <android/native_window.h> @@ -181,3 +184,6 @@ public: }; } // namespace } // namespace android + +// TODO(b/129481165): remove the #pragma below and fix conversion issues +#pragma clang diagnostic pop // ignored "-Wconversion"
\ No newline at end of file diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp new file mode 100644 index 0000000000..fa742c593a --- /dev/null +++ b/services/vibratorservice/Android.bp @@ -0,0 +1,55 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_shared { + name: "libvibratorservice", + + srcs: [ + "VibratorCallbackScheduler.cpp", + "VibratorHalController.cpp", + "VibratorHalWrapper.cpp", + "VibratorManagerHalWrapper.cpp", + ], + + aidl: { + local_include_dirs: ["include"], + include_dirs: [ + "hardware/interfaces/vibrator/aidl/android/hardware/vibrator", + ], + export_aidl_headers: true + }, + + shared_libs: [ + "libbinder", + "libhidlbase", + "liblog", + "libutils", + "android.hardware.vibrator-cpp", + "android.hardware.vibrator@1.0", + "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", + "android.hardware.vibrator@1.3", + ], + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], + + local_include_dirs: ["include"], + + export_include_dirs: ["include"], +} diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING new file mode 100644 index 0000000000..b033adbd05 --- /dev/null +++ b/services/vibratorservice/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libvibratorservice_test" + } + ] +} diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp new file mode 100644 index 0000000000..f2870b021d --- /dev/null +++ b/services/vibratorservice/VibratorCallbackScheduler.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <chrono> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +bool DelayedCallback::isExpired() const { + return mExpiration <= std::chrono::steady_clock::now(); +} + +DelayedCallback::Timestamp DelayedCallback::getExpiration() const { + return mExpiration; +} + +void DelayedCallback::run() const { + mCallback(); +} + +bool DelayedCallback::operator<(const DelayedCallback& other) const { + return mExpiration < other.mExpiration; +} + +bool DelayedCallback::operator>(const DelayedCallback& other) const { + return mExpiration > other.mExpiration; +} + +// ------------------------------------------------------------------------------------------------- + +CallbackScheduler::~CallbackScheduler() { + { + std::lock_guard<std::mutex> lock(mMutex); + mFinished = true; + } + mCondition.notify_all(); + if (mCallbackThread && mCallbackThread->joinable()) { + mCallbackThread->join(); + } +} + +void CallbackScheduler::schedule(std::function<void()> callback, std::chrono::milliseconds delay) { + { + std::lock_guard<std::mutex> lock(mMutex); + if (mCallbackThread == nullptr) { + mCallbackThread = std::make_unique<std::thread>(&CallbackScheduler::loop, this); + } + mQueue.emplace(DelayedCallback(callback, delay)); + } + mCondition.notify_all(); +} + +void CallbackScheduler::loop() { + while (true) { + std::unique_lock<std::mutex> lock(mMutex); + if (mFinished) { + // Destructor was called, so let the callback thread die. + break; + } + while (!mQueue.empty() && mQueue.top().isExpired()) { + DelayedCallback callback = mQueue.top(); + mQueue.pop(); + lock.unlock(); + callback.run(); + lock.lock(); + } + if (mQueue.empty()) { + // Wait until a new callback is scheduled. + mCondition.wait(mMutex); + } else { + // Wait until next callback expires, or a new one is scheduled. + mCondition.wait_until(mMutex, mQueue.top().getExpiration()); + } + } +} + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android diff --git a/services/vibratorservice/VibratorHalController.cpp b/services/vibratorservice/VibratorHalController.cpp new file mode 100644 index 0000000000..e8606caffd --- /dev/null +++ b/services/vibratorservice/VibratorHalController.cpp @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalController" + +#include <android/hardware/vibrator/1.3/IVibrator.h> +#include <android/hardware/vibrator/IVibrator.h> +#include <binder/IServiceManager.h> +#include <hardware/vibrator.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalController.h> +#include <vibratorservice/VibratorHalWrapper.h> + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using std::chrono::milliseconds; + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; +namespace V1_2 = android::hardware::vibrator::V1_2; +namespace V1_3 = android::hardware::vibrator::V1_3; +namespace Aidl = android::hardware::vibrator; + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +std::shared_ptr<HalWrapper> HalConnector::connect(std::shared_ptr<CallbackScheduler> scheduler) { + static bool gHalExists = true; + if (!gHalExists) { + // We already tried to connect to all of the vibrator HAL versions and none was available. + return nullptr; + } + + sp<Aidl::IVibrator> aidlHal = waitForVintfService<Aidl::IVibrator>(); + if (aidlHal) { + ALOGV("Successfully connected to Vibrator HAL AIDL service."); + return std::make_shared<AidlHalWrapper>(std::move(scheduler), aidlHal); + } + + sp<V1_0::IVibrator> halV1_0 = V1_0::IVibrator::getService(); + if (halV1_0 == nullptr) { + ALOGV("Vibrator HAL service not available."); + gHalExists = false; + return nullptr; + } + + sp<V1_3::IVibrator> halV1_3 = V1_3::IVibrator::castFrom(halV1_0); + if (halV1_3) { + ALOGV("Successfully connected to Vibrator HAL v1.3 service."); + return std::make_shared<HidlHalWrapperV1_3>(std::move(scheduler), halV1_3); + } + sp<V1_2::IVibrator> halV1_2 = V1_2::IVibrator::castFrom(halV1_0); + if (halV1_2) { + ALOGV("Successfully connected to Vibrator HAL v1.2 service."); + return std::make_shared<HidlHalWrapperV1_2>(std::move(scheduler), halV1_2); + } + sp<V1_1::IVibrator> halV1_1 = V1_1::IVibrator::castFrom(halV1_0); + if (halV1_1) { + ALOGV("Successfully connected to Vibrator HAL v1.1 service."); + return std::make_shared<HidlHalWrapperV1_1>(std::move(scheduler), halV1_1); + } + ALOGV("Successfully connected to Vibrator HAL v1.0 service."); + return std::make_shared<HidlHalWrapperV1_0>(std::move(scheduler), halV1_0); +} + +// ------------------------------------------------------------------------------------------------- + +static constexpr int MAX_RETRIES = 1; + +template <typename T> +HalResult<T> HalController::processHalResult(HalResult<T> result, const char* functionName) { + if (result.isFailed()) { + ALOGE("%s failed: %s", functionName, result.errorMessage()); + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + mConnectedHal->tryReconnect(); + } + return result; +} + +template <typename T> +HalResult<T> HalController::apply(HalController::hal_fn<T>& halFn, const char* functionName) { + std::shared_ptr<HalWrapper> hal = nullptr; + { + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + if (mConnectedHal == nullptr) { + // Init was never called, so connect to HAL for the first time during this call. + mConnectedHal = mHalConnector->connect(mCallbackScheduler); + + if (mConnectedHal == nullptr) { + ALOGV("Skipped %s because Vibrator HAL is not available", functionName); + return HalResult<T>::unsupported(); + } + } + hal = mConnectedHal; + } + + HalResult<T> ret = processHalResult(halFn(hal), functionName); + for (int i = 0; i < MAX_RETRIES && ret.isFailed(); i++) { + ret = processHalResult(halFn(hal), functionName); + } + + return ret; +} + +// ------------------------------------------------------------------------------------------------- + +bool HalController::init() { + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + if (mConnectedHal == nullptr) { + mConnectedHal = mHalConnector->connect(mCallbackScheduler); + } + return mConnectedHal != nullptr; +} + +HalResult<void> HalController::ping() { + hal_fn<void> pingFn = [](std::shared_ptr<HalWrapper> hal) { return hal->ping(); }; + return apply(pingFn, "ping"); +} + +void HalController::tryReconnect() { + std::lock_guard<std::mutex> lock(mConnectedHalMutex); + if (mConnectedHal == nullptr) { + mConnectedHal = mHalConnector->connect(mCallbackScheduler); + } else { + mConnectedHal->tryReconnect(); + } +} + +HalResult<void> HalController::on(milliseconds timeout, + const std::function<void()>& completionCallback) { + hal_fn<void> onFn = [&](std::shared_ptr<HalWrapper> hal) { + return hal->on(timeout, completionCallback); + }; + return apply(onFn, "on"); +} + +HalResult<void> HalController::off() { + hal_fn<void> offFn = [](std::shared_ptr<HalWrapper> hal) { return hal->off(); }; + return apply(offFn, "off"); +} + +HalResult<void> HalController::setAmplitude(int32_t amplitude) { + hal_fn<void> setAmplitudeFn = [&](std::shared_ptr<HalWrapper> hal) { + return hal->setAmplitude(amplitude); + }; + return apply(setAmplitudeFn, "setAmplitude"); +} + +HalResult<void> HalController::setExternalControl(bool enabled) { + hal_fn<void> setExternalControlFn = [&](std::shared_ptr<HalWrapper> hal) { + return hal->setExternalControl(enabled); + }; + return apply(setExternalControlFn, "setExternalControl"); +} + +HalResult<void> HalController::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) { + hal_fn<void> alwaysOnEnableFn = [&](std::shared_ptr<HalWrapper> hal) { + return hal->alwaysOnEnable(id, effect, strength); + }; + return apply(alwaysOnEnableFn, "alwaysOnEnable"); +} + +HalResult<void> HalController::alwaysOnDisable(int32_t id) { + hal_fn<void> alwaysOnDisableFn = [&](std::shared_ptr<HalWrapper> hal) { + return hal->alwaysOnDisable(id); + }; + return apply(alwaysOnDisableFn, "alwaysOnDisable"); +} + +HalResult<Capabilities> HalController::getCapabilities() { + hal_fn<Capabilities> getCapabilitiesFn = [](std::shared_ptr<HalWrapper> hal) { + return hal->getCapabilities(); + }; + return apply(getCapabilitiesFn, "getCapabilities"); +} + +HalResult<std::vector<Effect>> HalController::getSupportedEffects() { + hal_fn<std::vector<Effect>> getSupportedEffectsFn = [](std::shared_ptr<HalWrapper> hal) { + return hal->getSupportedEffects(); + }; + return apply(getSupportedEffectsFn, "getSupportedEffects"); +} + +HalResult<std::vector<CompositePrimitive>> HalController::getSupportedPrimitives() { + hal_fn<std::vector<CompositePrimitive>> getSupportedPrimitivesFn = + [](std::shared_ptr<HalWrapper> hal) { return hal->getSupportedPrimitives(); }; + return apply(getSupportedPrimitivesFn, "getSupportedPrimitives"); +} + +HalResult<milliseconds> HalController::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + hal_fn<milliseconds> performEffectFn = [&](std::shared_ptr<HalWrapper> hal) { + return hal->performEffect(effect, strength, completionCallback); + }; + return apply(performEffectFn, "performEffect"); +} + +HalResult<void> HalController::performComposedEffect( + const std::vector<CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) { + hal_fn<void> performComposedEffectFn = [&](std::shared_ptr<HalWrapper> hal) { + return hal->performComposedEffect(primitiveEffects, completionCallback); + }; + return apply(performComposedEffectFn, "performComposedEffect"); +} + +}; // namespace vibrator + +}; // namespace android diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp new file mode 100644 index 0000000000..9672644d8d --- /dev/null +++ b/services/vibratorservice/VibratorHalWrapper.cpp @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapper" + +#include <android/hardware/vibrator/1.3/IVibrator.h> +#include <android/hardware/vibrator/BnVibratorCallback.h> +#include <android/hardware/vibrator/IVibrator.h> +#include <hardware/vibrator.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using std::chrono::milliseconds; + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; +namespace V1_2 = android::hardware::vibrator::V1_2; +namespace V1_3 = android::hardware::vibrator::V1_3; +namespace Aidl = android::hardware::vibrator; + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +template <class T> +HalResult<T> loadCached(const std::function<HalResult<T>()>& loadFn, std::optional<T>& cache) { + if (cache.has_value()) { + // Return copy of cached value. + return HalResult<T>::ok(*cache); + } + HalResult<T> ret = loadFn(); + if (ret.isOk()) { + // Cache copy of returned value. + cache.emplace(ret.value()); + } + return ret; +} + +template <class T> +bool isStaticCastValid(Effect effect) { + T castEffect = static_cast<T>(effect); + auto iter = hardware::hidl_enum_range<T>(); + return castEffect >= *iter.begin() && castEffect <= *std::prev(iter.end()); +} + +// ------------------------------------------------------------------------------------------------- + +const constexpr char* STATUS_T_ERROR_MESSAGE_PREFIX = "status_t = "; +const constexpr char* STATUS_V_1_0_ERROR_MESSAGE_PREFIX = + "android::hardware::vibrator::V1_0::Status = "; + +template <typename T> +HalResult<T> HalResult<T>::fromStatus(binder::Status status, T data) { + if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult<T>::unsupported(); + } + if (status.isOk()) { + return HalResult<T>::ok(data); + } + return HalResult<T>::failed(std::string(status.toString8().c_str())); +} + +template <typename T> +HalResult<T> HalResult<T>::fromStatus(V1_0::Status status, T data) { + switch (status) { + case V1_0::Status::OK: + return HalResult<T>::ok(data); + case V1_0::Status::UNSUPPORTED_OPERATION: + return HalResult<T>::unsupported(); + default: + return HalResult<T>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status)); + } +} + +template <typename T> +template <typename R> +HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, T data) { + return ret.isOk() ? HalResult<T>::ok(data) : HalResult<T>::failed(ret.description()); +} + +template <typename T> +template <typename R> +HalResult<T> HalResult<T>::fromReturn(hardware::Return<R>& ret, V1_0::Status status, T data) { + return ret.isOk() ? HalResult<T>::fromStatus(status, data) + : HalResult<T>::failed(ret.description()); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> HalResult<void>::fromStatus(status_t status) { + if (status == android::OK) { + return HalResult<void>::ok(); + } + return HalResult<void>::failed(STATUS_T_ERROR_MESSAGE_PREFIX + statusToString(status)); +} + +HalResult<void> HalResult<void>::fromStatus(binder::Status status) { + if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) { + return HalResult<void>::unsupported(); + } + if (status.isOk()) { + return HalResult<void>::ok(); + } + return HalResult<void>::failed(std::string(status.toString8().c_str())); +} + +HalResult<void> HalResult<void>::fromStatus(V1_0::Status status) { + switch (status) { + case V1_0::Status::OK: + return HalResult<void>::ok(); + case V1_0::Status::UNSUPPORTED_OPERATION: + return HalResult<void>::unsupported(); + default: + return HalResult<void>::failed(STATUS_V_1_0_ERROR_MESSAGE_PREFIX + toString(status)); + } +} + +template <typename R> +HalResult<void> HalResult<void>::fromReturn(hardware::Return<R>& ret) { + return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description()); +} + +// ------------------------------------------------------------------------------------------------- + +class HalCallbackWrapper : public Aidl::BnVibratorCallback { +public: + HalCallbackWrapper(std::function<void()> completionCallback) + : mCompletionCallback(completionCallback) {} + + binder::Status onComplete() override { + mCompletionCallback(); + return binder::Status::ok(); + } + +private: + const std::function<void()> mCompletionCallback; +}; + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> AidlHalWrapper::ping() { + return HalResult<void>::fromStatus(IInterface::asBinder(getHal())->pingBinder()); +} + +void AidlHalWrapper::tryReconnect() { + sp<Aidl::IVibrator> newHandle = checkVintfService<Aidl::IVibrator>(); + if (newHandle) { + std::lock_guard<std::mutex> lock(mHandleMutex); + mHandle = std::move(newHandle); + } +} + +HalResult<void> AidlHalWrapper::on(milliseconds timeout, + const std::function<void()>& completionCallback) { + HalResult<Capabilities> capabilities = getCapabilities(); + bool supportsCallback = capabilities.isOk() && + static_cast<int32_t>(capabilities.value() & Capabilities::ON_CALLBACK); + auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; + + auto ret = HalResult<void>::fromStatus(getHal()->on(timeout.count(), cb)); + if (!supportsCallback && ret.isOk()) { + mCallbackScheduler->schedule(completionCallback, timeout); + } + + return ret; +} + +HalResult<void> AidlHalWrapper::off() { + return HalResult<void>::fromStatus(getHal()->off()); +} + +HalResult<void> AidlHalWrapper::setAmplitude(int32_t amplitude) { + float convertedAmplitude = static_cast<float>(amplitude) / std::numeric_limits<uint8_t>::max(); + return HalResult<void>::fromStatus(getHal()->setAmplitude(convertedAmplitude)); +} + +HalResult<void> AidlHalWrapper::setExternalControl(bool enabled) { + return HalResult<void>::fromStatus(getHal()->setExternalControl(enabled)); +} + +HalResult<void> AidlHalWrapper::alwaysOnEnable(int32_t id, Effect effect, EffectStrength strength) { + return HalResult<void>::fromStatus(getHal()->alwaysOnEnable(id, effect, strength)); +} + +HalResult<void> AidlHalWrapper::alwaysOnDisable(int32_t id) { + return HalResult<void>::fromStatus(getHal()->alwaysOnDisable(id)); +} + +HalResult<Capabilities> AidlHalWrapper::getCapabilities() { + std::lock_guard<std::mutex> lock(mCapabilitiesMutex); + return loadCached<Capabilities>(std::bind(&AidlHalWrapper::getCapabilitiesInternal, this), + mCapabilities); +} + +HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffects() { + std::lock_guard<std::mutex> lock(mSupportedEffectsMutex); + return loadCached<std::vector<Effect>>(std::bind(&AidlHalWrapper::getSupportedEffectsInternal, + this), + mSupportedEffects); +} + +HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitives() { + std::lock_guard<std::mutex> lock(mSupportedPrimitivesMutex); + return loadCached<std::vector< + CompositePrimitive>>(std::bind(&AidlHalWrapper::getSupportedPrimitivesInternal, this), + mSupportedPrimitives); +} + +HalResult<milliseconds> AidlHalWrapper::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + HalResult<Capabilities> capabilities = getCapabilities(); + bool supportsCallback = capabilities.isOk() && + static_cast<int32_t>(capabilities.value() & Capabilities::PERFORM_CALLBACK); + auto cb = supportsCallback ? new HalCallbackWrapper(completionCallback) : nullptr; + + int32_t lengthMs; + auto result = getHal()->perform(effect, strength, cb, &lengthMs); + milliseconds length = milliseconds(lengthMs); + + auto ret = HalResult<milliseconds>::fromStatus(result, length); + if (!supportsCallback && ret.isOk()) { + mCallbackScheduler->schedule(completionCallback, length); + } + + return ret; +} + +HalResult<void> AidlHalWrapper::performComposedEffect( + const std::vector<CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) { + // This method should always support callbacks, so no need to double check. + auto cb = new HalCallbackWrapper(completionCallback); + return HalResult<void>::fromStatus(getHal()->compose(primitiveEffects, cb)); +} + +HalResult<Capabilities> AidlHalWrapper::getCapabilitiesInternal() { + int32_t capabilities = 0; + auto result = getHal()->getCapabilities(&capabilities); + return HalResult<Capabilities>::fromStatus(result, static_cast<Capabilities>(capabilities)); +} + +HalResult<std::vector<Effect>> AidlHalWrapper::getSupportedEffectsInternal() { + std::vector<Effect> supportedEffects; + auto result = getHal()->getSupportedEffects(&supportedEffects); + return HalResult<std::vector<Effect>>::fromStatus(result, supportedEffects); +} + +HalResult<std::vector<CompositePrimitive>> AidlHalWrapper::getSupportedPrimitivesInternal() { + std::vector<CompositePrimitive> supportedPrimitives; + auto result = getHal()->getSupportedPrimitives(&supportedPrimitives); + return HalResult<std::vector<CompositePrimitive>>::fromStatus(result, supportedPrimitives); +} + +sp<Aidl::IVibrator> AidlHalWrapper::getHal() { + std::lock_guard<std::mutex> lock(mHandleMutex); + return mHandle; +} + +// ------------------------------------------------------------------------------------------------- + +template <typename I> +HalResult<void> HidlHalWrapper<I>::ping() { + auto result = getHal()->ping(); + return HalResult<void>::fromReturn(result); +} + +template <typename I> +void HidlHalWrapper<I>::tryReconnect() { + sp<I> newHandle = I::tryGetService(); + if (newHandle) { + std::lock_guard<std::mutex> lock(mHandleMutex); + mHandle = std::move(newHandle); + } +} + +template <typename I> +HalResult<void> HidlHalWrapper<I>::on(milliseconds timeout, + const std::function<void()>& completionCallback) { + auto result = getHal()->on(timeout.count()); + auto ret = HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); + if (ret.isOk()) { + mCallbackScheduler->schedule(completionCallback, timeout); + } + return ret; +} + +template <typename I> +HalResult<void> HidlHalWrapper<I>::off() { + auto result = getHal()->off(); + return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); +} + +template <typename I> +HalResult<void> HidlHalWrapper<I>::setAmplitude(int32_t amplitude) { + auto result = getHal()->setAmplitude(static_cast<uint8_t>(amplitude)); + return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); +} + +template <typename I> +HalResult<void> HidlHalWrapper<I>::setExternalControl(bool) { + ALOGV("Skipped setExternalControl because Vibrator HAL does not support it"); + return HalResult<void>::unsupported(); +} + +template <typename I> +HalResult<void> HidlHalWrapper<I>::alwaysOnEnable(int32_t, Effect, EffectStrength) { + ALOGV("Skipped alwaysOnEnable because Vibrator HAL AIDL is not available"); + return HalResult<void>::unsupported(); +} + +template <typename I> +HalResult<void> HidlHalWrapper<I>::alwaysOnDisable(int32_t) { + ALOGV("Skipped alwaysOnDisable because Vibrator HAL AIDL is not available"); + return HalResult<void>::unsupported(); +} + +template <typename I> +HalResult<Capabilities> HidlHalWrapper<I>::getCapabilities() { + std::lock_guard<std::mutex> lock(mCapabilitiesMutex); + return loadCached<Capabilities>(std::bind(&HidlHalWrapper<I>::getCapabilitiesInternal, this), + mCapabilities); +} + +template <typename I> +HalResult<std::vector<Effect>> HidlHalWrapper<I>::getSupportedEffects() { + ALOGV("Skipped getSupportedEffects because Vibrator HAL AIDL is not available"); + return HalResult<std::vector<Effect>>::unsupported(); +} + +template <typename I> +HalResult<std::vector<CompositePrimitive>> HidlHalWrapper<I>::getSupportedPrimitives() { + ALOGV("Skipped getSupportedPrimitives because Vibrator HAL AIDL is not available"); + return HalResult<std::vector<CompositePrimitive>>::unsupported(); +} + +template <typename I> +HalResult<void> HidlHalWrapper<I>::performComposedEffect(const std::vector<CompositeEffect>&, + const std::function<void()>&) { + ALOGV("Skipped composed effect because Vibrator HAL AIDL is not available"); + return HalResult<void>::unsupported(); +} + +template <typename I> +HalResult<Capabilities> HidlHalWrapper<I>::getCapabilitiesInternal() { + hardware::Return<bool> result = getHal()->supportsAmplitudeControl(); + Capabilities capabilities = + result.withDefault(false) ? Capabilities::AMPLITUDE_CONTROL : Capabilities::NONE; + return HalResult<Capabilities>::fromReturn(result, capabilities); +} + +template <typename I> +template <typename T> +HalResult<milliseconds> HidlHalWrapper<I>::performInternal( + perform_fn<T> performFn, sp<I> handle, T effect, EffectStrength strength, + const std::function<void()>& completionCallback) { + V1_0::Status status; + int32_t lengthMs; + auto effectCallback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) { + status = retStatus; + lengthMs = retLengthMs; + }; + + V1_0::EffectStrength effectStrength = static_cast<V1_0::EffectStrength>(strength); + auto result = std::invoke(performFn, handle, effect, effectStrength, effectCallback); + milliseconds length = milliseconds(lengthMs); + + auto ret = HalResult<milliseconds>::fromReturn(result, status, length); + if (ret.isOk()) { + mCallbackScheduler->schedule(completionCallback, length); + } + + return ret; +} + +template <typename I> +sp<I> HidlHalWrapper<I>::getHal() { + std::lock_guard<std::mutex> lock(mHandleMutex); + return mHandle; +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<milliseconds> HidlHalWrapperV1_0::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + if (isStaticCastValid<V1_0::Effect>(effect)) { + return performInternal(&V1_0::IVibrator::perform, getHal(), + static_cast<V1_0::Effect>(effect), strength, completionCallback); + } + + ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", + Aidl::toString(effect).c_str()); + return HalResult<milliseconds>::unsupported(); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<milliseconds> HidlHalWrapperV1_1::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + if (isStaticCastValid<V1_0::Effect>(effect)) { + return performInternal(&V1_1::IVibrator::perform, getHal(), + static_cast<V1_0::Effect>(effect), strength, completionCallback); + } + if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { + return performInternal(&V1_1::IVibrator::perform_1_1, getHal(), + static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback); + } + + ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", + Aidl::toString(effect).c_str()); + return HalResult<milliseconds>::unsupported(); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<milliseconds> HidlHalWrapperV1_2::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + if (isStaticCastValid<V1_0::Effect>(effect)) { + return performInternal(&V1_2::IVibrator::perform, getHal(), + static_cast<V1_0::Effect>(effect), strength, completionCallback); + } + if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { + return performInternal(&V1_2::IVibrator::perform_1_1, getHal(), + static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback); + } + if (isStaticCastValid<V1_2::Effect>(effect)) { + return performInternal(&V1_2::IVibrator::perform_1_2, getHal(), + static_cast<V1_2::Effect>(effect), strength, completionCallback); + } + + ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", + Aidl::toString(effect).c_str()); + return HalResult<milliseconds>::unsupported(); +} + +// ------------------------------------------------------------------------------------------------- + +HalResult<void> HidlHalWrapperV1_3::setExternalControl(bool enabled) { + auto result = getHal()->setExternalControl(static_cast<uint32_t>(enabled)); + return HalResult<void>::fromStatus(result.withDefault(V1_0::Status::UNKNOWN_ERROR)); +} + +HalResult<milliseconds> HidlHalWrapperV1_3::performEffect( + Effect effect, EffectStrength strength, const std::function<void()>& completionCallback) { + if (isStaticCastValid<V1_0::Effect>(effect)) { + return performInternal(&V1_3::IVibrator::perform, getHal(), + static_cast<V1_0::Effect>(effect), strength, completionCallback); + } + if (isStaticCastValid<V1_1::Effect_1_1>(effect)) { + return performInternal(&V1_3::IVibrator::perform_1_1, getHal(), + static_cast<V1_1::Effect_1_1>(effect), strength, completionCallback); + } + if (isStaticCastValid<V1_2::Effect>(effect)) { + return performInternal(&V1_3::IVibrator::perform_1_2, getHal(), + static_cast<V1_2::Effect>(effect), strength, completionCallback); + } + if (isStaticCastValid<V1_3::Effect>(effect)) { + return performInternal(&V1_3::IVibrator::perform_1_3, getHal(), + static_cast<V1_3::Effect>(effect), strength, completionCallback); + } + + ALOGV("Skipped performEffect because Vibrator HAL does not support effect %s", + Aidl::toString(effect).c_str()); + return HalResult<milliseconds>::unsupported(); +} + +HalResult<Capabilities> HidlHalWrapperV1_3::getCapabilitiesInternal() { + Capabilities capabilities = Capabilities::NONE; + + sp<V1_3::IVibrator> hal = getHal(); + auto amplitudeResult = hal->supportsAmplitudeControl(); + if (!amplitudeResult.isOk()) { + return HalResult<Capabilities>::fromReturn(amplitudeResult, capabilities); + } + + auto externalControlResult = hal->supportsExternalControl(); + if (amplitudeResult.withDefault(false)) { + capabilities |= Capabilities::AMPLITUDE_CONTROL; + } + if (externalControlResult.withDefault(false)) { + capabilities |= Capabilities::EXTERNAL_CONTROL; + + if (amplitudeResult.withDefault(false)) { + capabilities |= Capabilities::EXTERNAL_AMPLITUDE_CONTROL; + } + } + + return HalResult<Capabilities>::fromReturn(externalControlResult, capabilities); +} + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp new file mode 100644 index 0000000000..71955af31e --- /dev/null +++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorManagerHalWrapper" + +#include <utils/Log.h> + +#include <vibratorservice/VibratorManagerHalWrapper.h> + +namespace android { + +namespace vibrator { + +constexpr int32_t SINGLE_VIBRATOR_ID = 0; + +HalResult<void> LegacyManagerHalWrapper::ping() { + return mController->ping(); +} + +void LegacyManagerHalWrapper::tryReconnect() { + mController->tryReconnect(); +} + +HalResult<std::vector<int32_t>> LegacyManagerHalWrapper::getVibratorIds() { + if (mController->init()) { + return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>(1, SINGLE_VIBRATOR_ID)); + } + // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator. + return HalResult<std::vector<int32_t>>::ok(std::vector<int32_t>()); +} + +HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(int32_t id) { + if (id == SINGLE_VIBRATOR_ID && mController->init()) { + return HalResult<std::shared_ptr<HalController>>::ok(mController); + } + // Controller.init did not connect to any vibrator HAL service, so the device has no vibrator. + return HalResult<std::shared_ptr<HalController>>::failed("No vibrator with id = " + + std::to_string(id)); +} + +HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) { + return HalResult<void>::unsupported(); +} + +HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) { + return HalResult<void>::unsupported(); +} + +HalResult<void> LegacyManagerHalWrapper::cancelSynced() { + return HalResult<void>::unsupported(); +} + +}; // namespace vibrator + +}; // namespace android diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp new file mode 100644 index 0000000000..c1a03a16a7 --- /dev/null +++ b/services/vibratorservice/benchmarks/Android.bp @@ -0,0 +1,37 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_benchmark { + name: "libvibratorservice_benchmarks", + srcs: [ + "VibratorHalControllerBenchmarks.cpp", + ], + shared_libs: [ + "libbinder", + "libhidlbase", + "liblog", + "libutils", + "libvibratorservice", + "android.hardware.vibrator-cpp", + "android.hardware.vibrator@1.0", + "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", + "android.hardware.vibrator@1.3", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], +} diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp new file mode 100644 index 0000000000..6589f74d9a --- /dev/null +++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PowerHalControllerBenchmarks" + +#include <benchmark/benchmark.h> +#include <vibratorservice/VibratorHalController.h> + +using ::android::enum_range; +using ::benchmark::Counter; +using ::benchmark::Fixture; +using ::benchmark::kMicrosecond; +using ::benchmark::State; +using ::benchmark::internal::Benchmark; + +using namespace android; +using namespace std::chrono_literals; + +class VibratorBench : public Fixture { +public: + void SetUp(State& /*state*/) override { mController.init(); } + + void TearDown(State& /*state*/) override { mController.off(); } + + static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); } + + static void DefaultArgs(Benchmark* /*b*/) { + // none + } + +protected: + vibrator::HalController mController; + + auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); } + + bool hasCapabilities(const vibrator::HalResult<vibrator::Capabilities>& result, + vibrator::Capabilities&& query, State& state) { + if (result.isFailed()) { + state.SkipWithError(result.errorMessage()); + return false; + } + if (!result.isOk()) { + return false; + } + return (result.value() & query) == query; + } + + template <class R> + bool checkHalResult(const vibrator::HalResult<R>& result, State& state) { + if (result.isFailed()) { + state.SkipWithError(result.errorMessage()); + return false; + } + return true; + } +}; + +#define BENCHMARK_WRAPPER(fixt, test, code) \ + BENCHMARK_DEFINE_F(fixt, test) \ + /* NOLINTNEXTLINE */ \ + (State& state){code} BENCHMARK_REGISTER_F(fixt, test) \ + ->Apply(fixt::DefaultConfig) \ + ->Apply(fixt::DefaultArgs) + +BENCHMARK_WRAPPER(VibratorBench, init, { + for (auto _ : state) { + state.PauseTiming(); + vibrator::HalController controller; + state.ResumeTiming(); + controller.init(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, initCached, { + for (auto _ : state) { + mController.init(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, ping, { + for (auto _ : state) { + state.ResumeTiming(); + auto ret = mController.ping(); + state.PauseTiming(); + checkHalResult(ret, state); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, tryReconnect, { + for (auto _ : state) { + mController.tryReconnect(); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, on, { + auto duration = 60s; + auto callback = []() {}; + + for (auto _ : state) { + state.ResumeTiming(); + auto ret = mController.on(duration, callback); + state.PauseTiming(); + if (checkHalResult(ret, state)) { + checkHalResult(mController.off(), state); + } + } +}); + +BENCHMARK_WRAPPER(VibratorBench, off, { + auto duration = 60s; + auto callback = []() {}; + + for (auto _ : state) { + state.PauseTiming(); + if (!checkHalResult(mController.on(duration, callback), state)) { + continue; + } + state.ResumeTiming(); + checkHalResult(mController.off(), state); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { + auto result = mController.getCapabilities(); + + if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { + return; + } + + auto duration = 60s; + auto callback = []() {}; + auto amplitude = UINT8_MAX; + + for (auto _ : state) { + state.PauseTiming(); + vibrator::HalController controller; + controller.init(); + if (!checkHalResult(controller.on(duration, callback), state)) { + continue; + } + state.ResumeTiming(); + auto ret = controller.setAmplitude(amplitude); + state.PauseTiming(); + if (checkHalResult(ret, state)) { + checkHalResult(controller.off(), state); + } + } +}); + +BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { + auto result = mController.getCapabilities(); + + if (!hasCapabilities(result, vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { + return; + } + + auto duration = 6000s; + auto callback = []() {}; + auto amplitude = UINT8_MAX; + + checkHalResult(mController.on(duration, callback), state); + + for (auto _ : state) { + checkHalResult(mController.setAmplitude(amplitude), state); + } + + checkHalResult(mController.off(), state); +}); + +BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { + auto result = mController.getCapabilities(); + + if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) { + return; + } + + for (auto _ : state) { + state.PauseTiming(); + vibrator::HalController controller; + controller.init(); + state.ResumeTiming(); + auto ret = controller.setExternalControl(true); + state.PauseTiming(); + if (checkHalResult(ret, state)) { + checkHalResult(controller.setExternalControl(false), state); + } + } +}); + +BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, { + auto result = mController.getCapabilities(); + + if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_CONTROL, state)) { + return; + } + + for (auto _ : state) { + state.ResumeTiming(); + auto ret = mController.setExternalControl(true); + state.PauseTiming(); + if (checkHalResult(ret, state)) { + checkHalResult(mController.setExternalControl(false), state); + } + } +}); + +BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, { + auto result = mController.getCapabilities(); + + if (!hasCapabilities(result, vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) { + return; + } + + auto amplitude = UINT8_MAX; + + checkHalResult(mController.setExternalControl(true), state); + + for (auto _ : state) { + checkHalResult(mController.setAmplitude(amplitude), state); + } + + checkHalResult(mController.setExternalControl(false), state); +}); + +BENCHMARK_WRAPPER(VibratorBench, getCapabilities, { + for (auto _ : state) { + state.PauseTiming(); + vibrator::HalController controller; + controller.init(); + state.ResumeTiming(); + checkHalResult(controller.getCapabilities(), state); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, getCapabilitiesCached, { + // First call to cache values. + checkHalResult(mController.getCapabilities(), state); + + for (auto _ : state) { + checkHalResult(mController.getCapabilities(), state); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, getSupportedEffects, { + for (auto _ : state) { + state.PauseTiming(); + vibrator::HalController controller; + controller.init(); + state.ResumeTiming(); + checkHalResult(controller.getSupportedEffects(), state); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, getSupportedEffectsCached, { + // First call to cache values. + checkHalResult(mController.getSupportedEffects(), state); + + for (auto _ : state) { + checkHalResult(mController.getSupportedEffects(), state); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitives, { + for (auto _ : state) { + state.PauseTiming(); + vibrator::HalController controller; + controller.init(); + state.ResumeTiming(); + checkHalResult(controller.getSupportedPrimitives(), state); + } +}); + +BENCHMARK_WRAPPER(VibratorBench, getSupportedPrimitivesCached, { + // First call to cache values. + checkHalResult(mController.getSupportedPrimitives(), state); + + for (auto _ : state) { + checkHalResult(mController.getSupportedPrimitives(), state); + } +}); + +class VibratorEffectsBench : public VibratorBench { +public: + static void DefaultArgs(Benchmark* b) { + vibrator::HalController controller; + auto effectsResult = controller.getSupportedEffects(); + if (!effectsResult.isOk()) { + return; + } + + std::vector<hardware::vibrator::Effect> supported = effectsResult.value(); + if (supported.empty()) { + return; + } + + b->ArgNames({"Effect", "Strength"}); + for (const auto& effect : enum_range<hardware::vibrator::Effect>()) { + if (std::find(supported.begin(), supported.end(), effect) == supported.end()) { + continue; + } + for (const auto& strength : enum_range<hardware::vibrator::EffectStrength>()) { + b->Args({static_cast<long>(effect), static_cast<long>(strength)}); + } + } + } + +protected: + auto getEffect(const State& state) const { + return static_cast<hardware::vibrator::Effect>(this->getOtherArg(state, 0)); + } + + auto getStrength(const State& state) const { + return static_cast<hardware::vibrator::EffectStrength>(this->getOtherArg(state, 1)); + } +}; + +BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { + auto result = mController.getCapabilities(); + + if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { + return; + } + + int32_t id = 1; + auto effect = getEffect(state); + auto strength = getStrength(state); + + for (auto _ : state) { + state.ResumeTiming(); + auto ret = mController.alwaysOnEnable(id, effect, strength); + state.PauseTiming(); + if (checkHalResult(ret, state)) { + checkHalResult(mController.alwaysOnDisable(id), state); + } + } +}); + +BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { + auto result = mController.getCapabilities(); + + if (!hasCapabilities(result, vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { + return; + } + + int32_t id = 1; + auto effect = getEffect(state); + auto strength = getStrength(state); + + for (auto _ : state) { + state.PauseTiming(); + if (!checkHalResult(mController.alwaysOnEnable(id, effect, strength), state)) { + continue; + } + state.ResumeTiming(); + checkHalResult(mController.alwaysOnDisable(id), state); + } +}); + +BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, { + auto effect = getEffect(state); + auto strength = getStrength(state); + auto callback = []() {}; + + for (auto _ : state) { + state.ResumeTiming(); + auto ret = mController.performEffect(effect, strength, callback); + state.PauseTiming(); + if (checkHalResult(ret, state)) { + checkHalResult(mController.off(), state); + } + } +}); + +class VibratorPrimitivesBench : public VibratorBench { +public: + static void DefaultArgs(Benchmark* b) { + vibrator::HalController controller; + auto primitivesResult = controller.getSupportedPrimitives(); + if (!primitivesResult.isOk()) { + return; + } + + std::vector<hardware::vibrator::CompositePrimitive> supported = primitivesResult.value(); + if (supported.empty()) { + return; + } + + b->ArgNames({"Primitive"}); + for (const auto& primitive : enum_range<hardware::vibrator::CompositePrimitive>()) { + if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) { + continue; + } + if (primitive == hardware::vibrator::CompositePrimitive::NOOP) { + continue; + } + b->Args({static_cast<long>(primitive)}); + } + } + +protected: + auto getPrimitive(const State& state) const { + return static_cast<hardware::vibrator::CompositePrimitive>(this->getOtherArg(state, 0)); + } +}; + +BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, { + auto result = mController.getCapabilities(); + + if (!hasCapabilities(result, vibrator::Capabilities::COMPOSE_EFFECTS, state)) { + return; + } + + hardware::vibrator::CompositeEffect effect; + effect.primitive = getPrimitive(state); + effect.scale = 1.0f; + effect.delayMs = static_cast<int32_t>(0); + + std::vector<hardware::vibrator::CompositeEffect> effects; + effects.push_back(effect); + auto callback = []() {}; + + for (auto _ : state) { + state.ResumeTiming(); + auto ret = mController.performComposedEffect(effects, callback); + state.PauseTiming(); + if (checkHalResult(ret, state)) { + checkHalResult(mController.off(), state); + } + } +}); + +BENCHMARK_MAIN();
\ No newline at end of file diff --git a/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h new file mode 100644 index 0000000000..2c194b5526 --- /dev/null +++ b/services/vibratorservice/include/vibratorservice/VibratorCallbackScheduler.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_VIBRATOR_CALLBACK_SCHEDULER_H +#define ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H + +#include <android-base/thread_annotations.h> +#include <chrono> +#include <condition_variable> +#include <queue> +#include <thread> + +namespace android { + +namespace vibrator { + +// Wrapper for a callback to be executed after a delay. +class DelayedCallback { +public: + using Timestamp = std::chrono::time_point<std::chrono::steady_clock>; + + DelayedCallback(std::function<void()> callback, std::chrono::milliseconds delay) + : mCallback(callback), mExpiration(std::chrono::steady_clock::now() + delay) {} + ~DelayedCallback() = default; + + void run() const; + bool isExpired() const; + Timestamp getExpiration() const; + + // Compare by expiration time, where A < B when A expires first. + bool operator<(const DelayedCallback& other) const; + bool operator>(const DelayedCallback& other) const; + +private: + std::function<void()> mCallback; + Timestamp mExpiration; +}; + +// Schedules callbacks to be executed after a delay. +class CallbackScheduler { +public: + CallbackScheduler() : mCallbackThread(nullptr), mFinished(false) {} + virtual ~CallbackScheduler(); + + virtual void schedule(std::function<void()> callback, std::chrono::milliseconds delay); + +private: + std::condition_variable_any mCondition; + std::mutex mMutex; + + // Lazily instantiated only at the first time this scheduler is used. + std::unique_ptr<std::thread> mCallbackThread; + + // Used to quit the callback thread when this instance is being destroyed. + bool mFinished GUARDED_BY(mMutex); + + // Priority queue with reverse comparator, so tasks that expire first will be on top. + std::priority_queue<DelayedCallback, std::vector<DelayedCallback>, + std::greater<DelayedCallback>> + mQueue GUARDED_BY(mMutex); + + void loop(); +}; + +}; // namespace vibrator + +}; // namespace android + +#endif // ANDROID_VIBRATOR_CALLBACK_SCHEDULER_H diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h new file mode 100644 index 0000000000..d1028a4519 --- /dev/null +++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_OS_VIBRATORHALCONTROLLER_H +#define ANDROID_OS_VIBRATORHALCONTROLLER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/vibrator/IVibrator.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +namespace android { + +namespace vibrator { + +// Handles the connection to he underlying HAL implementation available. +class HalConnector { +public: + HalConnector() = default; + virtual ~HalConnector() = default; + + virtual std::shared_ptr<HalWrapper> connect(std::shared_ptr<CallbackScheduler> scheduler); +}; + +// Controller for Vibrator HAL handle. +// This relies on HalConnector to connect to the underlying Vibrator HAL service and reconnects to +// it after each failed api call. This also ensures connecting to the service is thread-safe. +class HalController : public HalWrapper { +public: + HalController() + : HalController(std::make_unique<HalConnector>(), std::make_shared<CallbackScheduler>()) { + } + HalController(std::unique_ptr<HalConnector> halConnector, + std::shared_ptr<CallbackScheduler> callbackScheduler) + : HalWrapper(std::move(callbackScheduler)), + mHalConnector(std::move(halConnector)), + mConnectedHal(nullptr) {} + virtual ~HalController() = default; + + /* Connects to the newest HAL version available, possibly waiting for the registered service to + * become available. This will automatically be called at the first API usage if it was not + * manually called beforehand. Calling this manually during the setup phase can avoid slowing + * the first API call later on. Returns true if any HAL version is available, false otherwise. + */ + virtual bool init(); + + /* reloads HAL service instance without waiting. This relies on the HAL version found by init() + * to rapidly reconnect to the specific HAL service, or defers to init() if it was never called. + */ + virtual void tryReconnect() override; + + virtual HalResult<void> ping() override; + HalResult<void> on(std::chrono::milliseconds timeout, + const std::function<void()>& completionCallback) final override; + HalResult<void> off() final override; + + HalResult<void> setAmplitude(int32_t amplitude) final override; + HalResult<void> setExternalControl(bool enabled) final override; + + HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, + hardware::vibrator::EffectStrength strength) final override; + HalResult<void> alwaysOnDisable(int32_t id) final override; + + HalResult<Capabilities> getCapabilities() final override; + HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() final override; + HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives() + final override; + + HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) final override; + + HalResult<void> performComposedEffect( + const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) final override; + +private: + std::unique_ptr<HalConnector> mHalConnector; + std::mutex mConnectedHalMutex; + // Shared pointer to allow local copies to be used by different threads. + std::shared_ptr<HalWrapper> mConnectedHal GUARDED_BY(mConnectedHalMutex); + + template <typename T> + HalResult<T> processHalResult(HalResult<T> result, const char* functionName); + + template <typename T> + using hal_fn = std::function<HalResult<T>(std::shared_ptr<HalWrapper>)>; + + template <typename T> + HalResult<T> apply(hal_fn<T>& halFn, const char* functionName); +}; + +}; // namespace vibrator + +}; // namespace android + +#endif // ANDROID_OS_VIBRATORHALCONTROLLER_H diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h new file mode 100644 index 0000000000..bcb735d3c4 --- /dev/null +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_OS_VIBRATORHALWRAPPER_H +#define ANDROID_OS_VIBRATORHALWRAPPER_H + +#include <android-base/thread_annotations.h> +#include <android/hardware/vibrator/1.3/IVibrator.h> +#include <android/hardware/vibrator/IVibrator.h> +#include <binder/IServiceManager.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +// Result of a call to the Vibrator HAL wrapper, holding data if successful. +template <typename T> +class HalResult { +public: + static HalResult<T> ok(T value) { return HalResult(value); } + static HalResult<T> failed(std::string msg) { + return HalResult(std::move(msg), /* unsupported= */ false); + } + static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); } + + static HalResult<T> fromStatus(binder::Status status, T data); + static HalResult<T> fromStatus(hardware::vibrator::V1_0::Status status, T data); + + template <typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, T data); + + template <typename R> + static HalResult<T> fromReturn(hardware::Return<R>& ret, + hardware::vibrator::V1_0::Status status, T data); + + // This will throw std::bad_optional_access if this result is not ok. + const T& value() const { return mValue.value(); } + bool isOk() const { return !mUnsupported && mValue.has_value(); } + bool isFailed() const { return !mUnsupported && !mValue.has_value(); } + bool isUnsupported() const { return mUnsupported; } + const char* errorMessage() const { return mErrorMessage.c_str(); } + +private: + std::optional<T> mValue; + std::string mErrorMessage; + bool mUnsupported; + + explicit HalResult(T value) + : mValue(std::make_optional(value)), mErrorMessage(), mUnsupported(false) {} + explicit HalResult(std::string errorMessage, bool unsupported) + : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {} +}; + +// Empty result of a call to the Vibrator HAL wrapper. +template <> +class HalResult<void> { +public: + static HalResult<void> ok() { return HalResult(); } + static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); } + static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); } + + static HalResult<void> fromStatus(status_t status); + static HalResult<void> fromStatus(binder::Status status); + static HalResult<void> fromStatus(hardware::vibrator::V1_0::Status status); + + template <typename R> + static HalResult<void> fromReturn(hardware::Return<R>& ret); + + bool isOk() const { return !mUnsupported && !mFailed; } + bool isFailed() const { return !mUnsupported && mFailed; } + bool isUnsupported() const { return mUnsupported; } + const char* errorMessage() const { return mErrorMessage.c_str(); } + +private: + std::string mErrorMessage; + bool mFailed; + bool mUnsupported; + + explicit HalResult(bool unsupported = false) + : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {} + explicit HalResult(std::string errorMessage) + : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {} +}; + +// ------------------------------------------------------------------------------------------------- + +// Vibrator HAL capabilities. +enum class Capabilities : int32_t { + NONE = 0, + ON_CALLBACK = hardware::vibrator::IVibrator::CAP_ON_CALLBACK, + PERFORM_CALLBACK = hardware::vibrator::IVibrator::CAP_PERFORM_CALLBACK, + AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_AMPLITUDE_CONTROL, + EXTERNAL_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_CONTROL, + EXTERNAL_AMPLITUDE_CONTROL = hardware::vibrator::IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL, + COMPOSE_EFFECTS = hardware::vibrator::IVibrator::CAP_COMPOSE_EFFECTS, + ALWAYS_ON_CONTROL = hardware::vibrator::IVibrator::CAP_ALWAYS_ON_CONTROL +}; + +inline Capabilities operator|(Capabilities lhs, Capabilities rhs) { + using underlying = typename std::underlying_type<Capabilities>::type; + return static_cast<Capabilities>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs)); +} + +inline Capabilities& operator|=(Capabilities& lhs, Capabilities rhs) { + return lhs = lhs | rhs; +} + +inline Capabilities operator&(Capabilities lhs, Capabilities rhs) { + using underlying = typename std::underlying_type<Capabilities>::type; + return static_cast<Capabilities>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs)); +} + +inline Capabilities& operator&=(Capabilities& lhs, Capabilities rhs) { + return lhs = lhs & rhs; +} + +// ------------------------------------------------------------------------------------------------- + +// Wrapper for Vibrator HAL handlers. +class HalWrapper { +public: + explicit HalWrapper(std::shared_ptr<CallbackScheduler> scheduler) + : mCallbackScheduler(std::move(scheduler)) {} + virtual ~HalWrapper() = default; + + /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the + * service restarts, to rapidly retry after a failure. + */ + virtual void tryReconnect() = 0; + + virtual HalResult<void> ping() = 0; + virtual HalResult<void> on(std::chrono::milliseconds timeout, + const std::function<void()>& completionCallback) = 0; + virtual HalResult<void> off() = 0; + + virtual HalResult<void> setAmplitude(int32_t amplitude) = 0; + virtual HalResult<void> setExternalControl(bool enabled) = 0; + + virtual HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, + hardware::vibrator::EffectStrength strength) = 0; + virtual HalResult<void> alwaysOnDisable(int32_t id) = 0; + + virtual HalResult<Capabilities> getCapabilities() = 0; + virtual HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() = 0; + virtual HalResult<std::vector<hardware::vibrator::CompositePrimitive>> + getSupportedPrimitives() = 0; + + virtual HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) = 0; + + virtual HalResult<void> performComposedEffect( + const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) = 0; + +protected: + // Shared pointer to allow CallbackScheduler to outlive this wrapper. + const std::shared_ptr<CallbackScheduler> mCallbackScheduler; +}; + +// Wrapper for the AIDL Vibrator HAL. +class AidlHalWrapper : public HalWrapper { +public: + AidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::IVibrator> handle) + : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {} + virtual ~AidlHalWrapper() = default; + + HalResult<void> ping() override final; + void tryReconnect() override final; + + HalResult<void> on(std::chrono::milliseconds timeout, + const std::function<void()>& completionCallback) override final; + HalResult<void> off() override final; + + HalResult<void> setAmplitude(int32_t amplitude) override final; + HalResult<void> setExternalControl(bool enabled) override final; + + HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, + hardware::vibrator::EffectStrength strength) override final; + HalResult<void> alwaysOnDisable(int32_t id) override final; + + HalResult<Capabilities> getCapabilities() override final; + HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final; + HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives() + override final; + + HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override final; + + HalResult<void> performComposedEffect( + const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) override final; + +private: + std::mutex mHandleMutex; + std::mutex mCapabilitiesMutex; + std::mutex mSupportedEffectsMutex; + std::mutex mSupportedPrimitivesMutex; + sp<hardware::vibrator::IVibrator> mHandle GUARDED_BY(mHandleMutex); + std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex); + std::optional<std::vector<hardware::vibrator::Effect>> mSupportedEffects + GUARDED_BY(mSupportedEffectsMutex); + std::optional<std::vector<hardware::vibrator::CompositePrimitive>> mSupportedPrimitives + GUARDED_BY(mSupportedPrimitivesMutex); + + // Loads directly from IVibrator handle, skipping caches. + HalResult<Capabilities> getCapabilitiesInternal(); + HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffectsInternal(); + HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitivesInternal(); + sp<hardware::vibrator::IVibrator> getHal(); +}; + +// Wrapper for the HDIL Vibrator HALs. +template <typename I> +class HidlHalWrapper : public HalWrapper { +public: + HidlHalWrapper(std::shared_ptr<CallbackScheduler> scheduler, sp<I> handle) + : HalWrapper(std::move(scheduler)), mHandle(std::move(handle)) {} + virtual ~HidlHalWrapper() = default; + + HalResult<void> ping() override final; + void tryReconnect() override final; + + HalResult<void> on(std::chrono::milliseconds timeout, + const std::function<void()>& completionCallback) override final; + HalResult<void> off() override final; + + HalResult<void> setAmplitude(int32_t amplitude) override final; + virtual HalResult<void> setExternalControl(bool enabled) override; + + HalResult<void> alwaysOnEnable(int32_t id, hardware::vibrator::Effect effect, + hardware::vibrator::EffectStrength strength) override final; + HalResult<void> alwaysOnDisable(int32_t id) override final; + + HalResult<Capabilities> getCapabilities() override final; + HalResult<std::vector<hardware::vibrator::Effect>> getSupportedEffects() override final; + HalResult<std::vector<hardware::vibrator::CompositePrimitive>> getSupportedPrimitives() + override final; + + HalResult<void> performComposedEffect( + const std::vector<hardware::vibrator::CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback) override final; + +protected: + std::mutex mHandleMutex; + std::mutex mCapabilitiesMutex; + sp<I> mHandle GUARDED_BY(mHandleMutex); + std::optional<Capabilities> mCapabilities GUARDED_BY(mCapabilitiesMutex); + + // Loads directly from IVibrator handle, skipping the mCapabilities cache. + virtual HalResult<Capabilities> getCapabilitiesInternal(); + + template <class T> + using perform_fn = + hardware::Return<void> (I::*)(T, hardware::vibrator::V1_0::EffectStrength, + hardware::vibrator::V1_0::IVibrator::perform_cb); + + template <class T> + HalResult<std::chrono::milliseconds> performInternal( + perform_fn<T> performFn, sp<I> handle, T effect, + hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback); + + sp<I> getHal(); +}; + +// Wrapper for the HDIL Vibrator HAL v1.0. +class HidlHalWrapperV1_0 : public HidlHalWrapper<hardware::vibrator::V1_0::IVibrator> { +public: + HidlHalWrapperV1_0(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::V1_0::IVibrator> handle) + : HidlHalWrapper<hardware::vibrator::V1_0::IVibrator>(std::move(scheduler), + std::move(handle)) {} + virtual ~HidlHalWrapperV1_0() = default; + + HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override final; +}; + +// Wrapper for the HDIL Vibrator HAL v1.1. +class HidlHalWrapperV1_1 : public HidlHalWrapper<hardware::vibrator::V1_1::IVibrator> { +public: + HidlHalWrapperV1_1(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::V1_1::IVibrator> handle) + : HidlHalWrapper<hardware::vibrator::V1_1::IVibrator>(std::move(scheduler), + std::move(handle)) {} + virtual ~HidlHalWrapperV1_1() = default; + + HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override final; +}; + +// Wrapper for the HDIL Vibrator HAL v1.2. +class HidlHalWrapperV1_2 : public HidlHalWrapper<hardware::vibrator::V1_2::IVibrator> { +public: + HidlHalWrapperV1_2(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::V1_2::IVibrator> handle) + : HidlHalWrapper<hardware::vibrator::V1_2::IVibrator>(std::move(scheduler), + std::move(handle)) {} + virtual ~HidlHalWrapperV1_2() = default; + + HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override final; +}; + +// Wrapper for the HDIL Vibrator HAL v1.3. +class HidlHalWrapperV1_3 : public HidlHalWrapper<hardware::vibrator::V1_3::IVibrator> { +public: + HidlHalWrapperV1_3(std::shared_ptr<CallbackScheduler> scheduler, + sp<hardware::vibrator::V1_3::IVibrator> handle) + : HidlHalWrapper<hardware::vibrator::V1_3::IVibrator>(std::move(scheduler), + std::move(handle)) {} + virtual ~HidlHalWrapperV1_3() = default; + + HalResult<void> setExternalControl(bool enabled) override final; + + HalResult<std::chrono::milliseconds> performEffect( + hardware::vibrator::Effect effect, hardware::vibrator::EffectStrength strength, + const std::function<void()>& completionCallback) override final; + +protected: + HalResult<Capabilities> getCapabilitiesInternal() override final; +}; + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android + +#endif // ANDROID_OS_VIBRATORHALWRAPPER_H diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h new file mode 100644 index 0000000000..99947a51b7 --- /dev/null +++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H +#define ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H + +#include <vibratorservice/VibratorHalController.h> + +namespace android { + +namespace vibrator { + +// Wrapper for VibratorManager HAL handlers. +class ManagerHalWrapper { +public: + ManagerHalWrapper() = default; + virtual ~ManagerHalWrapper() = default; + + virtual HalResult<void> ping() = 0; + + /* reloads wrapped HAL service instance without waiting. This can be used to reconnect when the + * service restarts, to rapidly retry after a failure. + */ + virtual void tryReconnect() = 0; + + virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0; + virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0; + + virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0; + virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0; + virtual HalResult<void> cancelSynced() = 0; +}; + +// Wrapper for the VibratorManager over single Vibrator HAL. +class LegacyManagerHalWrapper : public ManagerHalWrapper { +public: + LegacyManagerHalWrapper() : LegacyManagerHalWrapper(std::make_shared<HalController>()) {} + explicit LegacyManagerHalWrapper(std::shared_ptr<HalController> controller) + : mController(std::move(controller)) {} + virtual ~LegacyManagerHalWrapper() = default; + + HalResult<void> ping() override final; + void tryReconnect() override final; + + HalResult<std::vector<int32_t>> getVibratorIds() override final; + HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final; + + HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final; + HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final; + HalResult<void> cancelSynced() override final; + +private: + const std::shared_ptr<HalController> mController; +}; + +}; // namespace vibrator + +}; // namespace android + +#endif // ANDROID_OS_VIBRATOR_MANAGER_HAL_WRAPPER_H diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp new file mode 100644 index 0000000000..5fc6d450c8 --- /dev/null +++ b/services/vibratorservice/test/Android.bp @@ -0,0 +1,49 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libvibratorservice_test", + test_suites: ["device-tests"], + srcs: [ + "VibratorCallbackSchedulerTest.cpp", + "VibratorHalControllerTest.cpp", + "VibratorHalWrapperAidlTest.cpp", + "VibratorHalWrapperHidlV1_0Test.cpp", + "VibratorHalWrapperHidlV1_1Test.cpp", + "VibratorHalWrapperHidlV1_2Test.cpp", + "VibratorHalWrapperHidlV1_3Test.cpp", + "VibratorManagerHalWrapperLegacyTest.cpp", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wextra", + ], + shared_libs: [ + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libvibratorservice", + "libutils", + "android.hardware.vibrator-cpp", + "android.hardware.vibrator@1.0", + "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", + "android.hardware.vibrator@1.3", + ], + static_libs: [ + "libgmock", + ], +} diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp new file mode 100644 index 0000000000..aaeb8f990a --- /dev/null +++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperAidlTest" + +#include <android-base/thread_annotations.h> +#include <android/hardware/vibrator/IVibrator.h> +#include <condition_variable> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> + +using std::chrono::milliseconds; +using std::chrono::steady_clock; +using std::chrono::time_point; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class VibratorCallbackSchedulerTest : public Test { +public: + void SetUp() override { + mScheduler = std::make_unique<vibrator::CallbackScheduler>(); + std::lock_guard<std::mutex> lock(mMutex); + mExpiredCallbacks.clear(); + } + +protected: + std::mutex mMutex; + std::condition_variable_any mCondition; + std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr; + std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex); + + std::function<void()> createCallback(int32_t id) { + return [=]() { + { + std::lock_guard<std::mutex> lock(mMutex); + mExpiredCallbacks.push_back(id); + } + mCondition.notify_all(); + }; + } + + std::vector<int32_t> getExpiredCallbacks() { + std::lock_guard<std::mutex> lock(mMutex); + return std::vector<int32_t>(mExpiredCallbacks); + } + + bool waitForCallbacks(uint32_t callbackCount, milliseconds timeout) { + time_point<steady_clock> expiration = steady_clock::now() + timeout; + while (steady_clock::now() < expiration) { + std::lock_guard<std::mutex> lock(mMutex); + if (callbackCount <= mExpiredCallbacks.size()) { + return true; + } + mCondition.wait_until(mMutex, expiration); + } + return false; + } +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) { + mScheduler->schedule(createCallback(1), 15ms); + + // Not triggered before delay. + ASSERT_FALSE(waitForCallbacks(1, 10ms)); + ASSERT_TRUE(getExpiredCallbacks().empty()); + + ASSERT_TRUE(waitForCallbacks(1, 10ms)); + ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1)); +} + +TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) { + mScheduler->schedule(createCallback(1), 10ms); + mScheduler->schedule(createCallback(2), 5ms); + mScheduler->schedule(createCallback(3), 1ms); + + ASSERT_TRUE(waitForCallbacks(3, 15ms)); + ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1)); +} + +TEST_F(VibratorCallbackSchedulerTest, TestScheduleInParallelRunsInDelayOrder) { + std::vector<std::thread> threads; + for (int i = 0; i < 5; i++) { + threads.push_back(std::thread( + [=]() { mScheduler->schedule(createCallback(i), milliseconds(10 + 2 * i)); })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + ASSERT_TRUE(waitForCallbacks(5, 25ms)); + ASSERT_THAT(getExpiredCallbacks(), ElementsAre(0, 1, 2, 3, 4)); +} + +TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) { + mScheduler->schedule(createCallback(1), 5ms); + mScheduler.reset(nullptr); + + // Should time out waiting for callback to run. + ASSERT_FALSE(waitForCallbacks(1, 10ms)); + ASSERT_TRUE(getExpiredCallbacks().empty()); +} diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp new file mode 100644 index 0000000000..cda5e9a30c --- /dev/null +++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalControllerTest" + +#include <android/hardware/vibrator/IVibrator.h> +#include <cutils/atomic.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalController.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using std::chrono::milliseconds; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +static constexpr int MAX_ATTEMPTS = 2; + +// ------------------------------------------------------------------------------------------------- + +class MockHalWrapper : public vibrator::HalWrapper { +public: + MockHalWrapper(std::shared_ptr<vibrator::CallbackScheduler> scheduler) + : HalWrapper(scheduler) {} + virtual ~MockHalWrapper() = default; + + MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override)); + MOCK_METHOD(void, tryReconnect, (), (override)); + MOCK_METHOD(vibrator::HalResult<void>, on, + (milliseconds timeout, const std::function<void()>& completionCallback), + (override)); + MOCK_METHOD(vibrator::HalResult<void>, off, (), (override)); + MOCK_METHOD(vibrator::HalResult<void>, setAmplitude, (int32_t amplitude), (override)); + MOCK_METHOD(vibrator::HalResult<void>, setExternalControl, (bool enabled), (override)); + MOCK_METHOD(vibrator::HalResult<void>, alwaysOnEnable, + (int32_t id, Effect effect, EffectStrength strength), (override)); + MOCK_METHOD(vibrator::HalResult<void>, alwaysOnDisable, (int32_t id), (override)); + MOCK_METHOD(vibrator::HalResult<vibrator::Capabilities>, getCapabilities, (), (override)); + MOCK_METHOD(vibrator::HalResult<std::vector<Effect>>, getSupportedEffects, (), (override)); + MOCK_METHOD(vibrator::HalResult<std::vector<CompositePrimitive>>, getSupportedPrimitives, (), + (override)); + MOCK_METHOD(vibrator::HalResult<milliseconds>, performEffect, + (Effect effect, EffectStrength strength, + const std::function<void()>& completionCallback), + (override)); + MOCK_METHOD(vibrator::HalResult<void>, performComposedEffect, + (const std::vector<CompositeEffect>& primitiveEffects, + const std::function<void()>& completionCallback), + (override)); + + vibrator::CallbackScheduler* getCallbackScheduler() { return mCallbackScheduler.get(); } +}; + +class TestHalConnector : public vibrator::HalConnector { +public: + TestHalConnector(int32_t* connectCounter, std::shared_ptr<MockHalWrapper> mockHal) + : mConnectCounter(connectCounter), mMockHal(std::move(mockHal)) {} + ~TestHalConnector() = default; + + std::shared_ptr<vibrator::HalWrapper> connect( + std::shared_ptr<vibrator::CallbackScheduler>) override final { + android_atomic_inc(mConnectCounter); + return mMockHal; + } + +private: + int32_t* mConnectCounter; + std::shared_ptr<MockHalWrapper> mMockHal; +}; + +class FailingHalConnector : public vibrator::HalConnector { +public: + FailingHalConnector(int32_t* connectCounter) : mConnectCounter(connectCounter) {} + ~FailingHalConnector() = default; + + std::shared_ptr<vibrator::HalWrapper> connect( + std::shared_ptr<vibrator::CallbackScheduler>) override final { + android_atomic_inc(mConnectCounter); + return nullptr; + } + +private: + int32_t* mConnectCounter; +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalControllerTest : public Test { +public: + void SetUp() override { + mConnectCounter = 0; + auto callbackScheduler = std::make_shared<vibrator::CallbackScheduler>(); + mMockHal = std::make_shared<StrictMock<MockHalWrapper>>(callbackScheduler); + auto halConnector = std::make_unique<TestHalConnector>(&mConnectCounter, mMockHal); + mController = std::make_unique<vibrator::HalController>(std::move(halConnector), + std::move(callbackScheduler)); + ASSERT_NE(mController, nullptr); + } + +protected: + int32_t mConnectCounter; + std::shared_ptr<MockHalWrapper> mMockHal; + std::unique_ptr<vibrator::HalController> mController; + + void setHalExpectations(int32_t cardinality, std::vector<CompositeEffect> compositeEffects, + vibrator::HalResult<void> voidResult, + vibrator::HalResult<vibrator::Capabilities> capabilitiesResult, + vibrator::HalResult<std::vector<Effect>> effectsResult, + vibrator::HalResult<std::vector<CompositePrimitive>> primitivesResult, + vibrator::HalResult<milliseconds> durationResult) { + EXPECT_CALL(*mMockHal.get(), ping()) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _)) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), off()) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(255))) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), + alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT))) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), getCapabilities()) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(capabilitiesResult)); + EXPECT_CALL(*mMockHal.get(), getSupportedEffects()) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(effectsResult)); + EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives()) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(primitivesResult)); + EXPECT_CALL(*mMockHal.get(), performEffect(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _)) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(durationResult)); + EXPECT_CALL(*mMockHal.get(), performComposedEffect(Eq(compositeEffects), _)) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); + + if (cardinality > 1) { + // One reconnection call after each failure. + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(12 * cardinality)); + } + } +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalControllerTest, TestInit) { + ASSERT_TRUE(mController->init()); + ASSERT_EQ(1, mConnectCounter); + + // Noop when wrapper was already initialized. + ASSERT_TRUE(mController->init()); + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestApiCallsAreForwardedToHal) { + std::vector<Effect> effects; + effects.push_back(Effect::CLICK); + effects.push_back(Effect::TICK); + std::vector<CompositePrimitive> primitives; + primitives.push_back(CompositePrimitive::CLICK); + primitives.push_back(CompositePrimitive::THUD); + std::vector<CompositeEffect> compositeEffects; + compositeEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f)); + compositeEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)); + + setHalExpectations(/* cardinality= */ 1, compositeEffects, vibrator::HalResult<void>::ok(), + vibrator::HalResult<vibrator::Capabilities>::ok( + vibrator::Capabilities::ON_CALLBACK), + vibrator::HalResult<std::vector<Effect>>::ok(effects), + vibrator::HalResult<std::vector<CompositePrimitive>>::ok(primitives), + vibrator::HalResult<milliseconds>::ok(100ms)); + + ASSERT_TRUE(mController->ping().isOk()); + ASSERT_TRUE(mController->on(10ms, []() {}).isOk()); + ASSERT_TRUE(mController->off().isOk()); + ASSERT_TRUE(mController->setAmplitude(255).isOk()); + ASSERT_TRUE(mController->setExternalControl(true).isOk()); + ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk()); + ASSERT_TRUE(mController->alwaysOnDisable(1).isOk()); + + auto getCapabilitiesResult = mController->getCapabilities(); + ASSERT_TRUE(getCapabilitiesResult.isOk()); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, getCapabilitiesResult.value()); + + auto getSupportedEffectsResult = mController->getSupportedEffects(); + ASSERT_TRUE(getSupportedEffectsResult.isOk()); + ASSERT_EQ(effects, getSupportedEffectsResult.value()); + + auto getSupportedPrimitivesResult = mController->getSupportedPrimitives(); + ASSERT_TRUE(getSupportedPrimitivesResult.isOk()); + ASSERT_EQ(primitives, getSupportedPrimitivesResult.value()); + + auto performEffectResult = + mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}); + ASSERT_TRUE(performEffectResult.isOk()); + ASSERT_EQ(100ms, performEffectResult.value()); + + ASSERT_TRUE(mController->performComposedEffect(compositeEffects, []() {}).isOk()); + + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestUnsupportedApiResultDoNotResetHalConnection) { + setHalExpectations(/* cardinality= */ 1, std::vector<CompositeEffect>(), + vibrator::HalResult<void>::unsupported(), + vibrator::HalResult<vibrator::Capabilities>::unsupported(), + vibrator::HalResult<std::vector<Effect>>::unsupported(), + vibrator::HalResult<std::vector<CompositePrimitive>>::unsupported(), + vibrator::HalResult<milliseconds>::unsupported()); + + ASSERT_EQ(0, mConnectCounter); + + ASSERT_TRUE(mController->ping().isUnsupported()); + ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported()); + ASSERT_TRUE(mController->off().isUnsupported()); + ASSERT_TRUE(mController->setAmplitude(255).isUnsupported()); + ASSERT_TRUE(mController->setExternalControl(true).isUnsupported()); + ASSERT_TRUE( + mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported()); + ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported()); + ASSERT_TRUE(mController->getCapabilities().isUnsupported()); + ASSERT_TRUE(mController->getSupportedEffects().isUnsupported()); + ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported()); + ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}) + .isUnsupported()); + ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}) + .isUnsupported()); + + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestFailedApiResultResetsHalConnection) { + setHalExpectations(MAX_ATTEMPTS, std::vector<CompositeEffect>(), + vibrator::HalResult<void>::failed("message"), + vibrator::HalResult<vibrator::Capabilities>::failed("message"), + vibrator::HalResult<std::vector<Effect>>::failed("message"), + vibrator::HalResult<std::vector<CompositePrimitive>>::failed("message"), + vibrator::HalResult<milliseconds>::failed("message")); + + ASSERT_EQ(0, mConnectCounter); + + ASSERT_TRUE(mController->ping().isFailed()); + ASSERT_TRUE(mController->on(10ms, []() {}).isFailed()); + ASSERT_TRUE(mController->off().isFailed()); + ASSERT_TRUE(mController->setAmplitude(255).isFailed()); + ASSERT_TRUE(mController->setExternalControl(true).isFailed()); + ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isFailed()); + ASSERT_TRUE(mController->alwaysOnDisable(1).isFailed()); + ASSERT_TRUE(mController->getCapabilities().isFailed()); + ASSERT_TRUE(mController->getSupportedEffects().isFailed()); + ASSERT_TRUE(mController->getSupportedPrimitives().isFailed()); + ASSERT_TRUE( + mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}).isFailed()); + ASSERT_TRUE( + mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}).isFailed()); + + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestFailedApiResultReturnsSuccessAfterRetries) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), ping()) + .Times(Exactly(1)) + .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), ping()) + .Times(Exactly(1)) + .WillRepeatedly(Return(vibrator::HalResult<void>::ok())); + } + + ASSERT_EQ(0, mConnectCounter); + ASSERT_TRUE(mController->ping().isOk()); + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestMultiThreadConnectsOnlyOnce) { + ASSERT_EQ(0, mConnectCounter); + + EXPECT_CALL(*mMockHal.get(), ping()) + .Times(Exactly(10)) + .WillRepeatedly(Return(vibrator::HalResult<void>::ok())); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { ASSERT_TRUE(mController->ping().isOk()); })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + // Connector was called only by the first thread to use the api. + ASSERT_EQ(1, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestNoVibratorReturnsUnsupportedAndAttemptsToReconnect) { + auto failingHalConnector = std::make_unique<FailingHalConnector>(&mConnectCounter); + mController = + std::make_unique<vibrator::HalController>(std::move(failingHalConnector), nullptr); + ASSERT_EQ(0, mConnectCounter); + + ASSERT_FALSE(mController->init()); + ASSERT_TRUE(mController->ping().isUnsupported()); + ASSERT_TRUE(mController->on(10ms, []() {}).isUnsupported()); + ASSERT_TRUE(mController->off().isUnsupported()); + ASSERT_TRUE(mController->setAmplitude(255).isUnsupported()); + ASSERT_TRUE(mController->setExternalControl(true).isUnsupported()); + ASSERT_TRUE( + mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported()); + ASSERT_TRUE(mController->alwaysOnDisable(1).isUnsupported()); + ASSERT_TRUE(mController->getCapabilities().isUnsupported()); + ASSERT_TRUE(mController->getSupportedEffects().isUnsupported()); + ASSERT_TRUE(mController->getSupportedPrimitives().isUnsupported()); + ASSERT_TRUE(mController->performEffect(Effect::CLICK, EffectStrength::LIGHT, []() {}) + .isUnsupported()); + ASSERT_TRUE(mController->performComposedEffect(std::vector<CompositeEffect>(), []() {}) + .isUnsupported()); + + // One connection attempt per api call. + ASSERT_EQ(13, mConnectCounter); +} + +TEST_F(VibratorHalControllerTest, TestScheduledCallbackSurvivesReconnection) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), on(Eq(10ms), _)) + .Times(Exactly(1)) + .WillRepeatedly([&](milliseconds timeout, std::function<void()> callback) { + mMockHal.get()->getCallbackScheduler()->schedule(callback, timeout); + return vibrator::HalResult<void>::ok(); + }); + EXPECT_CALL(*mMockHal.get(), ping()) + .Times(Exactly(1)) + .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), ping()) + .Times(Exactly(1)) + .WillRepeatedly(Return(vibrator::HalResult<void>::failed("message"))); + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(1)); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mController->on(10ms, callback).isOk()); + ASSERT_TRUE(mController->ping().isFailed()); + mMockHal.reset(); + ASSERT_EQ(0, *callbackCounter.get()); + + // Callback triggered even after HalWrapper was reconnected. + std::this_thread::sleep_for(15ms); + ASSERT_EQ(1, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp new file mode 100644 index 0000000000..96b76ba2fe --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperAidlTest" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +using android::binder::Status; + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; +using android::hardware::vibrator::IVibrator; +using android::hardware::vibrator::IVibratorCallback; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockBinder : public BBinder { +public: + MOCK_METHOD(status_t, linkToDeath, + (const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags), (override)); + MOCK_METHOD(status_t, unlinkToDeath, + (const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags, + wp<DeathRecipient>* outRecipient), + (override)); + MOCK_METHOD(status_t, pingBinder, (), (override)); +}; + +class MockIVibrator : public IVibrator { +public: + MOCK_METHOD(Status, getCapabilities, (int32_t * ret), (override)); + MOCK_METHOD(Status, off, (), (override)); + MOCK_METHOD(Status, on, (int32_t timeout, const sp<IVibratorCallback>& cb), (override)); + MOCK_METHOD(Status, perform, + (Effect e, EffectStrength s, const sp<IVibratorCallback>& cb, int32_t* ret), + (override)); + MOCK_METHOD(Status, getSupportedEffects, (std::vector<Effect> * ret), (override)); + MOCK_METHOD(Status, setAmplitude, (float amplitude), (override)); + MOCK_METHOD(Status, setExternalControl, (bool enabled), (override)); + MOCK_METHOD(Status, getCompositionDelayMax, (int32_t * ret), (override)); + MOCK_METHOD(Status, getCompositionSizeMax, (int32_t * ret), (override)); + MOCK_METHOD(Status, getSupportedPrimitives, (std::vector<CompositePrimitive> * ret), + (override)); + MOCK_METHOD(Status, getPrimitiveDuration, (CompositePrimitive p, int32_t* ret), (override)); + MOCK_METHOD(Status, compose, + (const std::vector<CompositeEffect>& e, const sp<IVibratorCallback>& cb), + (override)); + 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(int32_t, getInterfaceVersion, (), (override)); + MOCK_METHOD(std::string, getInterfaceHash, (), (override)); + MOCK_METHOD(IBinder*, onAsBinder, (), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperAidlTest : public Test { +public: + void SetUp() override { + mMockBinder = new StrictMock<MockBinder>(); + mMockHal = new StrictMock<MockIVibrator>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::AidlHalWrapper>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibrator>> mMockHal = nullptr; + sp<StrictMock<MockBinder>> mMockBinder = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +ACTION(TriggerCallbackInArg1) { + if (arg1 != nullptr) { + arg1->onComplete(); + } +} + +ACTION(TriggerCallbackInArg2) { + if (arg2 != nullptr) { + arg2->onComplete(); + } +} + +TEST_F(VibratorHalWrapperAidlTest, TestPing) { + EXPECT_CALL(*mMockHal.get(), onAsBinder()) + .Times(Exactly(2)) + .WillRepeatedly(Return(mMockBinder.get())); + EXPECT_CALL(*mMockBinder.get(), pingBinder()) + .Times(Exactly(2)) + .WillOnce(Return(android::OK)) + .WillRepeatedly(Return(android::DEAD_OBJECT)); + + ASSERT_TRUE(mWrapper->ping().isOk()); + ASSERT_TRUE(mWrapper->ping().isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestOnWithCallbackSupport) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), on(Eq(10), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), on(Eq(100), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), on(Eq(1000), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(10ms, callback).isOk()); + ASSERT_EQ(1, *callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(100ms, callback).isUnsupported()); + // Callback not triggered for unsupported + ASSERT_EQ(1, *callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(1000ms, callback).isFailed()); + // Callback not triggered on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestOnWithoutCallbackSupport) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(IVibrator::CAP_COMPOSE_EFFECTS), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), on(Eq(10), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status())); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), on(Eq(11), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), on(Eq(12), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(10ms, callback).isOk()); + ASSERT_EQ(1, *callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(11ms, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestOff) { + EXPECT_CALL(*mMockHal.get(), off()) + .Times(Exactly(3)) + .WillOnce(Return(Status())) + .WillOnce( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + + ASSERT_TRUE(mWrapper->off().isOk()); + ASSERT_TRUE(mWrapper->off().isUnsupported()); + ASSERT_TRUE(mWrapper->off().isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestSetAmplitude) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.1, 1e-2))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.2, 1e-2))) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), setAmplitude(FloatNear(0.5, 1e-2))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 10).isOk()); + ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 5).isUnsupported()); + ASSERT_TRUE(mWrapper->setAmplitude(std::numeric_limits<uint8_t>::max() / 2).isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestSetExternalControl) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false))) + .Times(Exactly(2)) + .WillOnce(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + ASSERT_TRUE(mWrapper->setExternalControl(true).isOk()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnEnable) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT))) + .Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), + alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM))) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), + alwaysOnEnable(Eq(3), Eq(Effect::POP), Eq(EffectStrength::STRONG))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + auto result = mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT); + ASSERT_TRUE(result.isOk()); + result = mWrapper->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM); + ASSERT_TRUE(result.isUnsupported()); + result = mWrapper->alwaysOnEnable(3, Effect::POP, EffectStrength::STRONG); + ASSERT_TRUE(result.isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestAlwaysOnDisable) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(1))).Times(Exactly(1)); + EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(2))) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), alwaysOnDisable(Eq(3))) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isOk()); + ASSERT_TRUE(mWrapper->alwaysOnDisable(2).isUnsupported()); + ASSERT_TRUE(mWrapper->alwaysOnDisable(3).isFailed()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) { + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(3)) + .WillOnce( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + + ASSERT_TRUE(mWrapper->getCapabilities().isUnsupported()); + ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetCapabilitiesCachesResult) { + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsDoesNotCacheFailedResult) { + std::vector<Effect> supportedEffects; + supportedEffects.push_back(Effect::CLICK); + supportedEffects.push_back(Effect::TICK); + + EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_)) + .Times(Exactly(3)) + .WillOnce( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status()))); + + ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported()); + ASSERT_TRUE(mWrapper->getSupportedEffects().isFailed()); + + auto result = mWrapper->getSupportedEffects(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedEffects, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedEffectsCachesResult) { + std::vector<Effect> supportedEffects; + supportedEffects.push_back(Effect::CLICK); + supportedEffects.push_back(Effect::TICK); + + EXPECT_CALL(*mMockHal.get(), getSupportedEffects(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedEffects), Return(Status()))); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getSupportedEffects(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedEffects, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getSupportedEffects(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedEffects, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesDoesNotCacheFailedResult) { + std::vector<CompositePrimitive> supportedPrimitives; + supportedPrimitives.push_back(CompositePrimitive::CLICK); + supportedPrimitives.push_back(CompositePrimitive::THUD); + + EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) + .Times(Exactly(3)) + .WillOnce( + Return(Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))) + .WillOnce(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); + + ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported()); + ASSERT_TRUE(mWrapper->getSupportedPrimitives().isFailed()); + + auto result = mWrapper->getSupportedPrimitives(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedPrimitives, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestGetSupportedPrimitivesCachesResult) { + std::vector<CompositePrimitive> supportedPrimitives; + supportedPrimitives.push_back(CompositePrimitive::CLICK); + supportedPrimitives.push_back(CompositePrimitive::THUD); + + EXPECT_CALL(*mMockHal.get(), getSupportedPrimitives(_)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<0>(supportedPrimitives), Return(Status()))); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getSupportedPrimitives(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedPrimitives, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getSupportedPrimitives(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(supportedPrimitives, result.value()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(IVibrator::CAP_PERFORM_CALLBACK), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<3>(1000), TriggerCallbackInArg2(), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(1000ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + // Callback not triggered for unsupported + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + // Callback not triggered on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithoutCallbackSupport) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), getCapabilities(_)) + .Times(Exactly(1)) + .WillRepeatedly( + DoAll(SetArgPointee<0>(IVibrator::CAP_ON_CALLBACK), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::CLICK), Eq(EffectStrength::LIGHT), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(SetArgPointee<3>(10), Return(Status()))); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::POP), Eq(EffectStrength::MEDIUM), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), perform(Eq(Effect::THUD), Eq(EffectStrength::STRONG), _, _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::POP, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperAidlTest, TestPerformComposedEffect) { + std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects; + singleEffect.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f)); + multipleEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f)); + multipleEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)); + + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), compose(Eq(emptyEffects), _)) + .Times(Exactly(1)) + .WillRepeatedly(DoAll(TriggerCallbackInArg1(), Return(Status()))); + EXPECT_CALL(*mMockHal.get(), compose(Eq(singleEffect), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return( + Status::fromExceptionCode(Status::Exception::EX_UNSUPPORTED_OPERATION))); + EXPECT_CALL(*mMockHal.get(), compose(Eq(multipleEffects), _)) + .Times(Exactly(1)) + .WillRepeatedly(Return(Status::fromExceptionCode(Status::Exception::EX_SECURITY))); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performComposedEffect(emptyEffects, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performComposedEffect(singleEffect, callback); + ASSERT_TRUE(result.isUnsupported()); + // Callback not triggered for unsupported + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performComposedEffect(multipleEffects, callback); + ASSERT_TRUE(result.isFailed()); + // Callback not triggered on failure + ASSERT_EQ(1, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp new file mode 100644 index 0000000000..06aa36f91a --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperHidlV1_0Test" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +namespace V1_0 = android::hardware::vibrator::V1_0; + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; +using android::hardware::vibrator::IVibrator; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibratorV1_0 : public V1_0::IVibrator { +public: + MOCK_METHOD(hardware::Return<void>, ping, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); + MOCK_METHOD(hardware::Return<void>, perform, + (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperHidlV1_0Test : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIVibratorV1_0>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_0>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibratorV1_0>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPing) { + EXPECT_CALL(*mMockHal.get(), ping()) + .Times(Exactly(2)) + .WillOnce([]() { return hardware::Return<void>(); }) + .WillRepeatedly([]() { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + + ASSERT_TRUE(mWrapper->ping().isOk()); + ASSERT_TRUE(mWrapper->ping().isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestOn) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(1)))) + .Times(Exactly(1)) + .WillRepeatedly( + [](uint32_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(1ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(10)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint32_t) { + return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); + }); + EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(11)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint32_t) { + return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); + }); + EXPECT_CALL(*mMockHal.get(), on(Eq(static_cast<uint32_t>(12)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint32_t) { + return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(1ms, callback).isOk()); + ASSERT_EQ(1, *callbackCounter.get()); + + ASSERT_TRUE(mWrapper->on(10ms, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->on(11ms, callback).isFailed()); + ASSERT_TRUE(mWrapper->on(12ms, callback).isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestOff) { + EXPECT_CALL(*mMockHal.get(), off()) + .Times(Exactly(4)) + .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }) + .WillOnce([]() { + return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); + }) + .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); }) + .WillRepeatedly([]() { + return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); + }); + + ASSERT_TRUE(mWrapper->off().isOk()); + ASSERT_TRUE(mWrapper->off().isUnsupported()); + ASSERT_TRUE(mWrapper->off().isFailed()); + ASSERT_TRUE(mWrapper->off().isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetAmplitude) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), setAmplitude(static_cast<uint8_t>(1))) + .Times(Exactly(1)) + .WillRepeatedly( + [](uint8_t) { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }); + EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(2)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint8_t) { + return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); + }); + EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(3)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint8_t) { + return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); + }); + EXPECT_CALL(*mMockHal.get(), setAmplitude(Eq(static_cast<uint8_t>(4)))) + .Times(Exactly(1)) + .WillRepeatedly([](uint8_t) { + return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); + }); + } + + ASSERT_TRUE(mWrapper->setAmplitude(1).isOk()); + ASSERT_TRUE(mWrapper->setAmplitude(2).isUnsupported()); + ASSERT_TRUE(mWrapper->setAmplitude(3).isFailed()); + ASSERT_TRUE(mWrapper->setAmplitude(4).isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestSetExternalControlUnsupported) { + ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnEnableUnsupported) { + ASSERT_TRUE(mWrapper->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestAlwaysOnDisableUnsupported) { + ASSERT_TRUE(mWrapper->alwaysOnDisable(1).isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesDoesNotCacheFailedResult) { + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(2)) + .WillOnce([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + + ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesWithoutAmplitudeControl) { + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() { + return hardware::Return<bool>(false); + }); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::NONE, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetCapabilitiesCachesResult) { + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillRepeatedly([]() { + return hardware::Return<bool>(true); + }); + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedEffectsUnsupported) { + ASSERT_TRUE(mWrapper->getSupportedEffects().isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetSupportedPrimitivesUnsupported) { + ASSERT_TRUE(mWrapper->getSupportedPrimitives().isUnsupported()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::MEDIUM), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { + cb(V1_0::Status::UNSUPPORTED_OPERATION, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::STRONG), _)) + .Times(Exactly(2)) + .WillOnce([](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb cb) { + cb(V1_0::Status::BAD_VALUE, 10); + return hardware::Return<void>(); + }) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_0::perform_cb) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::CLICK, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + result = mWrapper->performEffect(Effect::CLICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffectUnsupported) { + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + // Using TICK that is only available in v1.1 + auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isUnsupported()); + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformComposedEffectUnsupported) { + std::vector<CompositeEffect> emptyEffects, singleEffect, multipleEffects; + singleEffect.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::CLICK, 10ms, 0.0f)); + multipleEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::SPIN, 100ms, 0.5f)); + multipleEffects.push_back( + vibrator::TestFactory::createCompositeEffect(CompositePrimitive::THUD, 1000ms, 1.0f)); + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->performComposedEffect(singleEffect, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->performComposedEffect(multipleEffects, callback).isUnsupported()); + + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp new file mode 100644 index 0000000000..d887efce80 --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_1Test.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperHidlV1_1Test" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; + +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibratorV1_1 : public V1_1::IVibrator { +public: + MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); + MOCK_METHOD(hardware::Return<void>, perform, + (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_1, + (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), + (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperHidlV1_1Test : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIVibratorV1_1>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_1>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibratorV1_1>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_0) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectV1_1) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_1::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::MEDIUM), _)) + .Times(Exactly(1)) + .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_1::perform_cb cb) { + cb(V1_0::Status::UNSUPPORTED_OPERATION, 0); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::STRONG), _)) + .Times(Exactly(2)) + .WillOnce([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_1::perform_cb cb) { + cb(V1_0::Status::BAD_VALUE, 0); + return hardware::Return<void>(); + }) + .WillRepeatedly( + [](V1_1::Effect_1_1, V1_0::EffectStrength, MockIVibratorV1_1::perform_cb) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::TICK, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + result = mWrapper->performEffect(Effect::TICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_1Test, TestPerformEffectUnsupported) { + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + // Using THUD that is only available in v1.2 + auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isUnsupported()); + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp new file mode 100644 index 0000000000..26d93503c6 --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_2Test.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperHidlV1_2Test" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; +namespace V1_2 = android::hardware::vibrator::V1_2; + +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibratorV1_2 : public V1_2::IVibrator { +public: + MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); + MOCK_METHOD(hardware::Return<void>, perform, + (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_1, + (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), + (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_2, + (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperHidlV1_2Test : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIVibratorV1_2>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_2>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibratorV1_2>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_0) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_1) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectV1_2) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), + perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::MEDIUM), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::UNSUPPORTED_OPERATION, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockHal.get(), + perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::STRONG), _)) + .Times(Exactly(2)) + .WillOnce([](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb cb) { + cb(V1_0::Status::BAD_VALUE, 10); + return hardware::Return<void>(); + }) + .WillRepeatedly( + [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_2::perform_cb) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + result = mWrapper->performEffect(Effect::THUD, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_2Test, TestPerformEffectUnsupported) { + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + // Using TEXTURE_TICK that is only available in v1.3 + auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isUnsupported()); + // No callback is triggered. + ASSERT_EQ(0, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp new file mode 100644 index 0000000000..08652f4e24 --- /dev/null +++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_3Test.cpp @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorHalWrapperHidlV1_3Test" + +#include <android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorCallbackScheduler.h> +#include <vibratorservice/VibratorHalWrapper.h> + +#include "test_utils.h" + +namespace V1_0 = android::hardware::vibrator::V1_0; +namespace V1_1 = android::hardware::vibrator::V1_1; +namespace V1_2 = android::hardware::vibrator::V1_2; +namespace V1_3 = android::hardware::vibrator::V1_3; + +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; +using android::hardware::vibrator::IVibrator; + +using namespace android; +using namespace std::chrono_literals; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockIVibratorV1_3 : public V1_3::IVibrator { +public: + MOCK_METHOD(hardware::Return<V1_0::Status>, on, (uint32_t timeoutMs), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, off, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsAmplitudeControl, (), (override)); + MOCK_METHOD(hardware::Return<bool>, supportsExternalControl, (), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setAmplitude, (uint8_t amplitude), (override)); + MOCK_METHOD(hardware::Return<V1_0::Status>, setExternalControl, (bool enabled), (override)); + MOCK_METHOD(hardware::Return<void>, perform, + (V1_0::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_1, + (V1_1::Effect_1_1 effect, V1_0::EffectStrength strength, perform_cb cb), + (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_2, + (V1_2::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); + MOCK_METHOD(hardware::Return<void>, perform_1_3, + (V1_3::Effect effect, V1_0::EffectStrength strength, perform_cb cb), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorHalWrapperHidlV1_3Test : public Test { +public: + void SetUp() override { + mMockHal = new StrictMock<MockIVibratorV1_3>(); + mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); + mWrapper = std::make_unique<vibrator::HidlHalWrapperV1_3>(mMockScheduler, mMockHal); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<vibrator::MockCallbackScheduler>> mMockScheduler = nullptr; + std::unique_ptr<vibrator::HalWrapper> mWrapper = nullptr; + sp<StrictMock<MockIVibratorV1_3>> mMockHal = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestSetExternalControl) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(true))) + .Times(Exactly(2)) + .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::OK); }) + .WillRepeatedly([]() { + return hardware::Return<V1_0::Status>(V1_0::Status::UNSUPPORTED_OPERATION); + }); + EXPECT_CALL(*mMockHal.get(), setExternalControl(Eq(false))) + .Times(Exactly(2)) + .WillOnce([]() { return hardware::Return<V1_0::Status>(V1_0::Status::BAD_VALUE); }) + .WillRepeatedly([]() { + return hardware::Return<V1_0::Status>(hardware::Status::fromExceptionCode(-1)); + }); + } + + ASSERT_TRUE(mWrapper->setExternalControl(true).isOk()); + ASSERT_TRUE(mWrapper->setExternalControl(true).isUnsupported()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); + ASSERT_TRUE(mWrapper->setExternalControl(false).isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesSuccessful) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(true); + }); + } + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL | vibrator::Capabilities::EXTERNAL_CONTROL | + vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, + result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyAmplitudeControl) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(true); + }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(false); + }); + } + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesOnlyExternalControl) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(false); + }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(true); + }); + } + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::EXTERNAL_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesNone) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(false); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(false); + }); + } + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::NONE, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesFailed) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }); + } + + ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); + ASSERT_TRUE(mWrapper->getCapabilities().isFailed()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesCachesResult) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()).Times(Exactly(1)).WillOnce([]() { + return hardware::Return<bool>(false); + }); + } + + std::vector<std::thread> threads; + for (int i = 0; i < 10; i++) { + threads.push_back(std::thread([&]() { + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + })); + } + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestGetCapabilitiesDoesNotCacheFailedResult) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { + return hardware::Return<bool>(hardware::Status::fromExceptionCode(-1)); + }); + + EXPECT_CALL(*mMockHal.get(), supportsAmplitudeControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(true); }); + EXPECT_CALL(*mMockHal.get(), supportsExternalControl()) + .Times(Exactly(1)) + .WillRepeatedly([]() { return hardware::Return<bool>(false); }); + } + + // Call to supportsAmplitudeControl failed. + auto result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isFailed()); + + // Call to supportsExternalControl failed. + result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isFailed()); + + // Returns successful result from third call. + result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); + + // Returns cached successful result. + result = mWrapper->getCapabilities(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(vibrator::Capabilities::AMPLITUDE_CONTROL, result.value()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_0) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform(Eq(V1_0::Effect::CLICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_0::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::CLICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_1) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_1(Eq(V1_1::Effect_1_1::TICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly([](V1_1::Effect_1_1, V1_0::EffectStrength, + MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::TICK, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_2) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_2(Eq(V1_2::Effect::THUD), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_2::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + auto result = mWrapper->performEffect(Effect::THUD, EffectStrength::LIGHT, callback); + + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); +} + +TEST_F(VibratorHalWrapperHidlV1_3Test, TestPerformEffectV1_3) { + { + InSequence seq; + EXPECT_CALL(*mMockHal.get(), + perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::LIGHT), _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::OK, 10); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockScheduler.get(), schedule(_, Eq(10ms))) + .Times(Exactly(1)) + .WillRepeatedly(vibrator::TriggerSchedulerCallback()); + EXPECT_CALL(*mMockHal.get(), + perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::MEDIUM), + _)) + .Times(Exactly(1)) + .WillRepeatedly( + [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::UNSUPPORTED_OPERATION, 0); + return hardware::Return<void>(); + }); + EXPECT_CALL(*mMockHal.get(), + perform_1_3(Eq(V1_3::Effect::TEXTURE_TICK), Eq(V1_0::EffectStrength::STRONG), + _)) + .Times(Exactly(2)) + .WillOnce([](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb cb) { + cb(V1_0::Status::BAD_VALUE, 0); + return hardware::Return<void>(); + }) + .WillRepeatedly( + [](V1_3::Effect, V1_0::EffectStrength, MockIVibratorV1_3::perform_cb) { + return hardware::Return<void>(hardware::Status::fromExceptionCode(-1)); + }); + } + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + auto result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::LIGHT, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(10ms, result.value()); + ASSERT_EQ(1, *callbackCounter.get()); + + result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::MEDIUM, callback); + ASSERT_TRUE(result.isUnsupported()); + + result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + result = mWrapper->performEffect(Effect::TEXTURE_TICK, EffectStrength::STRONG, callback); + ASSERT_TRUE(result.isFailed()); + + // Callback not triggered for unsupported and on failure + ASSERT_EQ(1, *callbackCounter.get()); +} diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp new file mode 100644 index 0000000000..d5520a18d2 --- /dev/null +++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VibratorManagerHalWrapperLegacyTest" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorManagerHalWrapper.h> + +using android::hardware::vibrator::CompositeEffect; +using android::hardware::vibrator::CompositePrimitive; +using android::hardware::vibrator::Effect; +using android::hardware::vibrator::EffectStrength; + +using std::chrono::milliseconds; + +using namespace android; +using namespace testing; + +// ------------------------------------------------------------------------------------------------- + +class MockHalController : public vibrator::HalController { +public: + MockHalController() = default; + virtual ~MockHalController() = default; + + MOCK_METHOD(bool, init, (), (override)); + MOCK_METHOD(vibrator::HalResult<void>, ping, (), (override)); + MOCK_METHOD(void, tryReconnect, (), (override)); +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorManagerHalWrapperLegacyTest : public Test { +public: + void SetUp() override { + mMockController = std::make_shared<StrictMock<MockHalController>>(); + mWrapper = std::make_unique<vibrator::LegacyManagerHalWrapper>(mMockController); + ASSERT_NE(mWrapper, nullptr); + } + +protected: + std::shared_ptr<StrictMock<MockHalController>> mMockController = nullptr; + std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorManagerHalWrapperLegacyTest, TestPing) { + EXPECT_CALL(*mMockController.get(), ping()) + .Times(Exactly(2)) + .WillOnce(Return(vibrator::HalResult<void>::failed("message"))) + .WillRepeatedly(Return(vibrator::HalResult<void>::ok())); + + ASSERT_TRUE(mWrapper->ping().isFailed()); + ASSERT_TRUE(mWrapper->ping().isOk()); +} + +TEST_F(VibratorManagerHalWrapperLegacyTest, TestTryReconnect) { + EXPECT_CALL(*mMockController.get(), tryReconnect()).Times(Exactly(1)); + + mWrapper->tryReconnect(); +} + +TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorIds) { + std::vector<int32_t> expectedIds; + expectedIds.push_back(0); + + EXPECT_CALL(*mMockController.get(), init()) + .Times(Exactly(2)) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true)); + + auto result = mWrapper->getVibratorIds(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(std::vector<int32_t>(), result.value()); + + result = mWrapper->getVibratorIds(); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(expectedIds, result.value()); +} + +TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithValidIdReturnsController) { + EXPECT_CALL(*mMockController.get(), init()) + .Times(Exactly(2)) + .WillOnce(Return(false)) + .WillRepeatedly(Return(true)); + + ASSERT_TRUE(mWrapper->getVibrator(0).isFailed()); + + auto result = mWrapper->getVibrator(0); + ASSERT_TRUE(result.isOk()); + ASSERT_EQ(mMockController.get(), result.value().get()); +} + +TEST_F(VibratorManagerHalWrapperLegacyTest, TestGetVibratorWithInvalidIdFails) { + ASSERT_TRUE(mWrapper->getVibrator(-1).isFailed()); +} + +TEST_F(VibratorManagerHalWrapperLegacyTest, TestSyncedOperationsUnsupported) { + std::vector<int32_t> vibratorIds; + vibratorIds.push_back(0); + + ASSERT_TRUE(mWrapper->prepareSynced(vibratorIds).isUnsupported()); + ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported()); + ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported()); +} diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h new file mode 100644 index 0000000000..8d0b22e14c --- /dev/null +++ b/services/vibratorservice/test/test_utils.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 VIBRATORSERVICE_UNITTEST_UTIL_H_ +#define VIBRATORSERVICE_UNITTEST_UTIL_H_ + +#include <android/hardware/vibrator/IVibrator.h> + +#include <vibratorservice/VibratorHalWrapper.h> + +namespace android { + +namespace vibrator { + +using ::android::hardware::vibrator::CompositeEffect; +using ::android::hardware::vibrator::CompositePrimitive; + +// ------------------------------------------------------------------------------------------------- + +class MockCallbackScheduler : public vibrator::CallbackScheduler { +public: + MOCK_METHOD(void, schedule, (std::function<void()> callback, std::chrono::milliseconds delay), + (override)); +}; + +ACTION(TriggerSchedulerCallback) { + arg0(); +} + +// ------------------------------------------------------------------------------------------------- + +class TestFactory { +public: + static CompositeEffect createCompositeEffect(CompositePrimitive primitive, + std::chrono::milliseconds delay, float scale) { + CompositeEffect effect; + effect.primitive = primitive; + effect.delayMs = delay.count(); + effect.scale = scale; + return effect; + } + + static std::function<void()> createCountingCallback(int32_t* counter) { + return [counter]() { *counter += 1; }; + } + +private: + TestFactory() = delete; + ~TestFactory() = delete; +}; + +// ------------------------------------------------------------------------------------------------- + +} // namespace vibrator + +} // namespace android + +#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file diff --git a/vulkan/include/hardware/hwvulkan.h b/vulkan/include/hardware/hwvulkan.h index 9e9a14d489..98bc8e3c46 100644 --- a/vulkan/include/hardware/hwvulkan.h +++ b/vulkan/include/hardware/hwvulkan.h @@ -54,8 +54,9 @@ typedef union { /* A hwvulkan_device_t corresponds to an ICD on other systems. Currently there * can only be one on a system (HWVULKAN_DEVICE_0). It is opened once per * process when the Vulkan API is first used; the hw_device_t::close() function - * is never called. Any non-trivial resource allocation should be done when - * the VkInstance is created rather than when the hwvulkan_device_t is opened. + * is called upon driver unloading. Any non-trivial resource allocation should + * be done when the VkInstance is created rather than when the hwvulkan_device_t + * is opened. */ typedef struct hwvulkan_device_t { struct hw_device_t common; diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h index 9ffe83ba2e..ba98696aef 100644 --- a/vulkan/include/vulkan/vk_android_native_buffer.h +++ b/vulkan/include/vulkan/vk_android_native_buffer.h @@ -27,17 +27,19 @@ extern "C" { #define VK_ANDROID_native_buffer 1 #define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER 11 -/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6 +/* + * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 6 * * This version of the extension transitions from gralloc0 to gralloc1 usage * flags (int -> 2x uint64_t). The WSI implementation will temporarily continue * to fill out deprecated fields in VkNativeBufferANDROID, and will call the * deprecated vkGetSwapchainGrallocUsageANDROID if the new * vkGetSwapchainGrallocUsage2ANDROID is not supported. This transitionary - * backwards-compatibility support is temporary, and will likely be removed in + * backwards-compatibility support is temporary, and will likely be removed * (along with all gralloc0 support) in a future release. */ -/* NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8 +/* + * NOTE ON VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8 * * This version of the extension doesn't introduce new types or structs, but is * to accommodate the new struct VkBindImageMemorySwapchainInfoKHR added in @@ -47,97 +49,155 @@ extern "C" { * in VkBindImageMemorySwapchainInfoKHR will be additionally chained to the * pNext chain of VkBindImageMemoryInfo and passed down to the driver. */ -#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8 -#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer" - -#define VK_ANDROID_NATIVE_BUFFER_ENUM(type,id) ((type)(1000000000 + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id))) -#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0) -#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1) -#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2) - +#define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8 +#define VK_ANDROID_NATIVE_BUFFER_EXTENSION_NAME "VK_ANDROID_native_buffer" + +#define VK_ANDROID_NATIVE_BUFFER_ENUM(type, id) \ + ((type)(1000000000 + \ + (1000 * (VK_ANDROID_NATIVE_BUFFER_EXTENSION_NUMBER - 1)) + (id))) +#define VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID \ + VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 0) +#define VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID \ + VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 1) +#define VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID \ + VK_ANDROID_NATIVE_BUFFER_ENUM(VkStructureType, 2) + +/* clang-format off */ typedef enum VkSwapchainImageUsageFlagBitsANDROID { VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001, VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkSwapchainImageUsageFlagBitsANDROID; typedef VkFlags VkSwapchainImageUsageFlagsANDROID; +/* + * struct VkNativeBufferUsage2ANDROID + * + * consumer: gralloc1 consumer usage flag + * producer: gralloc1 producer usage flag + */ typedef struct { - uint64_t consumer; - uint64_t producer; + uint64_t consumer; + uint64_t producer; } VkNativeBufferUsage2ANDROID; +/* + * struct VkNativeBufferANDROID + * + * sType: VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID + * pNext: NULL or a pointer to a structure extending this structure + * handle: buffer handle returned from gralloc alloc() + * stride: stride returned from gralloc alloc() + * format: gralloc format requested when the buffer was allocated + * usage: gralloc usage requested when the buffer was allocated + * usage2: gralloc usage requested when the buffer was allocated + */ typedef struct { - VkStructureType sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID - const void* pNext; - - // Buffer handle and stride returned from gralloc alloc() - buffer_handle_t handle; - int stride; - - // Gralloc format and usage requested when the buffer was allocated. - int format; - int usage; // DEPRECATED in SPEC_VERSION 6 - // -- Added in SPEC_VERSION 6 -- - VkNativeBufferUsage2ANDROID usage2; + VkStructureType sType; + const void* pNext; + buffer_handle_t handle; + int stride; + int format; + int usage; /* DEPRECATED in SPEC_VERSION 6 */ + VkNativeBufferUsage2ANDROID usage2; /* ADDED in SPEC_VERSION 6 */ } VkNativeBufferANDROID; +/* + * struct VkSwapchainImageCreateInfoANDROID + * + * sType: VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID + * pNext: NULL or a pointer to a structure extending this structure + * usage: is a bitmask of VkSwapchainImageUsageFlagsANDROID + */ typedef struct { - VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID - const void* pNext; - - VkSwapchainImageUsageFlagsANDROID usage; + VkStructureType sType; + const void* pNext; + VkSwapchainImageUsageFlagsANDROID usage; } VkSwapchainImageCreateInfoANDROID; +/* + * struct VkPhysicalDevicePresentationPropertiesANDROID + * + * sType: VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID + * pNext: NULL or a pointer to a structure extending this structure + * sharedImage: specifies if the image can be shared with the display system + */ typedef struct { - VkStructureType sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID - const void* pNext; - - VkBool32 sharedImage; + VkStructureType sType; + const void* pNext; + VkBool32 sharedImage; } VkPhysicalDevicePresentationPropertiesANDROID; -// -- DEPRECATED in SPEC_VERSION 6 -- -typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage); -// -- ADDED in SPEC_VERSION 6 -- -typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); -typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence); -typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd); +/* DEPRECATED in SPEC_VERSION 6 */ +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsageANDROID)( + VkDevice device, + VkFormat format, + VkImageUsageFlags imageUsage, + int* grallocUsage); + +/* ADDED in SPEC_VERSION 6 */ +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainGrallocUsage2ANDROID)( + VkDevice device, + VkFormat format, + VkImageUsageFlags imageUsage, + VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, + uint64_t* grallocConsumerUsage, + uint64_t* grallocProducerUsage); + +typedef VkResult (VKAPI_PTR *PFN_vkAcquireImageANDROID)( + VkDevice device, + VkImage image, + int nativeFenceFd, + VkSemaphore semaphore, + VkFence fence); + +typedef VkResult (VKAPI_PTR *PFN_vkQueueSignalReleaseImageANDROID)( + VkQueue queue, + uint32_t waitSemaphoreCount, + const VkSemaphore* pWaitSemaphores, + VkImage image, + int* pNativeFenceFd); #ifndef VK_NO_PROTOTYPES -// -- DEPRECATED in SPEC_VERSION 6 -- +/* DEPRECATED in SPEC_VERSION 6 */ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsageANDROID( - VkDevice device, - VkFormat format, - VkImageUsageFlags imageUsage, - int* grallocUsage + VkDevice device, + VkFormat format, + VkImageUsageFlags imageUsage, + int* grallocUsage ); -// -- ADDED in SPEC_VERSION 6 -- + +/* ADDED in SPEC_VERSION 6 */ VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainGrallocUsage2ANDROID( - VkDevice device, - VkFormat format, - VkImageUsageFlags imageUsage, + VkDevice device, + VkFormat format, + VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, - uint64_t* grallocConsumerUsage, - uint64_t* grallocProducerUsage + uint64_t* grallocConsumerUsage, + uint64_t* grallocProducerUsage ); + VKAPI_ATTR VkResult VKAPI_CALL vkAcquireImageANDROID( - VkDevice device, - VkImage image, - int nativeFenceFd, - VkSemaphore semaphore, - VkFence fence + VkDevice device, + VkImage image, + int nativeFenceFd, + VkSemaphore semaphore, + VkFence fence ); + VKAPI_ATTR VkResult VKAPI_CALL vkQueueSignalReleaseImageANDROID( - VkQueue queue, - uint32_t waitSemaphoreCount, - const VkSemaphore* pWaitSemaphores, - VkImage image, - int* pNativeFenceFd + VkQueue queue, + uint32_t waitSemaphoreCount, + const VkSemaphore* pWaitSemaphores, + VkImage image, + int* pNativeFenceFd ); + #endif +/* clang-format on */ #ifdef __cplusplus } #endif -#endif // __VK_ANDROID_NATIVE_BUFFER_H__ +#endif /* __VK_ANDROID_NATIVE_BUFFER_H__ */ diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 921b095726..1d29bab355 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -90,7 +90,6 @@ cc_library_shared { "libhardware", "libsync", "libbase", - "libdl_android", "libhidlbase", "liblog", "libui", @@ -101,6 +100,7 @@ cc_library_shared { "libnativebridge_lazy", "libnativeloader_lazy", "libnativewindow", + "libvndksupport", "android.hardware.graphics.common@1.0", "libSurfaceFlingerProp", ], diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp index 5b9affd03a..2d4690a2af 100644 --- a/vulkan/libvulkan/api.cpp +++ b/vulkan/libvulkan/api.cpp @@ -1174,23 +1174,18 @@ const LayerChain::ActiveLayer* LayerChain::GetActiveLayers( // ---------------------------------------------------------------------------- bool EnsureInitialized() { - static std::once_flag once_flag; - static bool initialized; - - std::call_once(once_flag, []() { - if (driver::OpenHAL()) { - initialized = true; - } - }); - - { - static pid_t pid = getpid() + 1; - static std::mutex layer_lock; - std::lock_guard<std::mutex> lock(layer_lock); - if (pid != getpid()) { - pid = getpid(); - DiscoverLayers(); - } + static bool initialized = false; + static pid_t init_attempted_for_pid = 0; + static std::mutex init_lock; + + std::lock_guard<std::mutex> lock(init_lock); + if (init_attempted_for_pid == getpid()) + return initialized; + + init_attempted_for_pid = getpid(); + if (driver::OpenHAL()) { + DiscoverLayers(); + initialized = true; } return initialized; @@ -1256,7 +1251,7 @@ VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, ATRACE_CALL(); if (!EnsureInitialized()) - return VK_ERROR_INITIALIZATION_FAILED; + return VK_ERROR_OUT_OF_HOST_MEMORY; uint32_t count = GetLayerCount(); @@ -1280,7 +1275,7 @@ VkResult EnumerateInstanceExtensionProperties( ATRACE_CALL(); if (!EnsureInitialized()) - return VK_ERROR_INITIALIZATION_FAILED; + return VK_ERROR_OUT_OF_HOST_MEMORY; if (pLayerName) { const Layer* layer = FindLayer(pLayerName); @@ -1456,6 +1451,11 @@ VkResult EnumerateDeviceExtensionProperties( VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) { ATRACE_CALL(); + // Load the driver here if not done yet. This api will be used in Zygote + // for Vulkan driver pre-loading because of the minimum overhead. + if (!EnsureInitialized()) + return VK_ERROR_OUT_OF_HOST_MEMORY; + *pApiVersion = VK_API_VERSION_1_1; return VK_SUCCESS; } diff --git a/vulkan/libvulkan/api.h b/vulkan/libvulkan/api.h index 416cba0125..2a215d7427 100644 --- a/vulkan/libvulkan/api.h +++ b/vulkan/libvulkan/api.h @@ -24,17 +24,34 @@ namespace vulkan { namespace api { -// clang-format off -VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); -VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator); -VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); -VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator); -VKAPI_ATTR VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties); -VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); -VKAPI_ATTR VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties); -VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); +VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkInstance* pInstance); +VKAPI_ATTR void DestroyInstance(VkInstance instance, + const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice); +VKAPI_ATTR void DestroyDevice(VkDevice device, + const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR VkResult +EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, + VkLayerProperties* pProperties); +VKAPI_ATTR VkResult +EnumerateInstanceExtensionProperties(const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); +VKAPI_ATTR VkResult +EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkLayerProperties* pProperties); +VKAPI_ATTR VkResult +EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion); -// clang-format on inline InstanceData& GetData(VkInstance instance) { return driver::GetData(instance).opaque_api_data; diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index c63fdf5969..6f09a8c45b 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -28,20 +28,17 @@ #include <android/dlext.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> -#include <cutils/properties.h> #include <graphicsenv/GraphicsEnv.h> #include <log/log.h> -#include <nativeloader/dlext_namespaces.h> #include <sys/prctl.h> #include <utils/Timers.h> #include <utils/Trace.h> +#include <vndksupport/linker.h> #include <algorithm> #include <array> #include <climits> #include <new> -#include <string_view> -#include <sstream> #include <vector> #include "stubhal.h" @@ -84,6 +81,8 @@ class Hal { Hal(const Hal&) = delete; Hal& operator=(const Hal&) = delete; + bool ShouldUnloadBuiltinDriver(); + void UnloadBuiltinDriver(); bool InitDebugReportIndex(); static Hal hal_; @@ -95,15 +94,15 @@ class Hal { class CreateInfoWrapper { public: CreateInfoWrapper(const VkInstanceCreateInfo& create_info, + uint32_t icd_api_version, const VkAllocationCallbacks& allocator); CreateInfoWrapper(VkPhysicalDevice physical_dev, const VkDeviceCreateInfo& create_info, + uint32_t icd_api_version, const VkAllocationCallbacks& allocator); ~CreateInfoWrapper(); VkResult Validate(); - void DowngradeApiVersion(); - void UpgradeDeviceCoreApiVersion(uint32_t api_version); const std::bitset<ProcHook::EXTENSION_COUNT>& GetHookExtensions() const; const std::bitset<ProcHook::EXTENSION_COUNT>& GetHalExtensions() const; @@ -118,10 +117,12 @@ class CreateInfoWrapper { const char** names; uint32_t name_count; + ExtensionFilter() + : exts(nullptr), ext_count(0), names(nullptr), name_count(0) {} }; + VkResult SanitizeApiVersion(); VkResult SanitizePNext(); - VkResult SanitizeLayers(); VkResult SanitizeExtensions(); @@ -133,6 +134,8 @@ class CreateInfoWrapper { const bool is_instance_; const VkAllocationCallbacks& allocator_; + const uint32_t loader_api_version_; + const uint32_t icd_api_version_; VkPhysicalDevice physical_dev_; @@ -151,19 +154,11 @@ class CreateInfoWrapper { Hal Hal::hal_; -void* LoadLibrary(const android_dlextinfo& dlextinfo, - const std::string_view subname) { - ATRACE_CALL(); - - std::stringstream ss; - ss << "vulkan." << subname << ".so"; - return android_dlopen_ext(ss.str().c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); -} - const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{ "ro.hardware.vulkan", "ro.board.platform", }}; +constexpr int LIB_DL_FLAGS = RTLD_LOCAL | RTLD_NOW; // LoadDriver returns: // * 0 when succeed, or @@ -174,20 +169,26 @@ int LoadDriver(android_namespace_t* library_namespace, const hwvulkan_module_t** module) { ATRACE_CALL(); - const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = library_namespace, - }; void* so = nullptr; - char prop[PROPERTY_VALUE_MAX]; for (auto key : HAL_SUBNAME_KEY_PROPERTIES) { - int prop_len = property_get(key, prop, nullptr); - if (prop_len > 0 && prop_len <= UINT_MAX) { - std::string_view lib_name(prop, static_cast<unsigned int>(prop_len)); - so = LoadLibrary(dlextinfo, lib_name); - if (so) - break; + std::string lib_name = android::base::GetProperty(key, ""); + if (lib_name.empty()) + continue; + + lib_name = "vulkan." + lib_name + ".so"; + if (library_namespace) { + // load updated driver + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = library_namespace, + }; + so = android_dlopen_ext(lib_name.c_str(), LIB_DL_FLAGS, &dlextinfo); + } else { + // load built-in driver + so = android_load_sphal_library(lib_name.c_str(), LIB_DL_FLAGS); } + if (so) + break; } if (!so) return -ENOENT; @@ -211,12 +212,9 @@ int LoadDriver(android_namespace_t* library_namespace, int LoadBuiltinDriver(const hwvulkan_module_t** module) { ATRACE_CALL(); - auto ns = android_get_exported_namespace("sphal"); - if (!ns) - return -ENOENT; android::GraphicsEnv::getInstance().setDriverToLoad( android::GpuStatsInfo::Driver::VULKAN); - return LoadDriver(ns, module); + return LoadDriver(nullptr, module); } int LoadUpdatedDriver(const hwvulkan_module_t** module) { @@ -241,7 +239,12 @@ bool Hal::Open() { const nsecs_t openTime = systemTime(); - ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once"); + if (hal_.ShouldUnloadBuiltinDriver()) { + hal_.UnloadBuiltinDriver(); + } + + if (hal_.dev_) + return true; // Use a stub device unless we successfully open a real HAL device. hal_.dev_ = &stubhal::kDevice; @@ -286,6 +289,38 @@ bool Hal::Open() { return true; } +bool Hal::ShouldUnloadBuiltinDriver() { + // Should not unload since the driver was not loaded + if (!hal_.dev_) + return false; + + // Should not unload if stubhal is used on the device + if (hal_.dev_ == &stubhal::kDevice) + return false; + + // Unload the driver if updated driver is chosen + if (android::GraphicsEnv::getInstance().getDriverNamespace()) + return true; + + return false; +} + +void Hal::UnloadBuiltinDriver() { + ATRACE_CALL(); + + ALOGD("Unload builtin Vulkan driver."); + + // Close the opened device + ALOG_ASSERT(!hal_.dev_->common.close(hal_.dev_->common), + "hw_device_t::close() failed."); + + // Close the opened shared library in the hw_module_t + android_unload_sphal_library(hal_.dev_->common.module->dso); + + hal_.dev_ = nullptr; + hal_.debug_report_index_ = -1; +} + bool Hal::InitDebugReportIndex() { ATRACE_CALL(); @@ -324,32 +359,27 @@ bool Hal::InitDebugReportIndex() { } CreateInfoWrapper::CreateInfoWrapper(const VkInstanceCreateInfo& create_info, + uint32_t icd_api_version, const VkAllocationCallbacks& allocator) : is_instance_(true), allocator_(allocator), + loader_api_version_(VK_API_VERSION_1_1), + icd_api_version_(icd_api_version), physical_dev_(VK_NULL_HANDLE), instance_info_(create_info), - extension_filter_() { - // instance core versions need to match the loader api version - for (uint32_t i = ProcHook::EXTENSION_CORE_1_0; - i != ProcHook::EXTENSION_COUNT; ++i) { - hook_extensions_.set(i); - hal_extensions_.set(i); - } -} + extension_filter_() {} CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev, const VkDeviceCreateInfo& create_info, + uint32_t icd_api_version, const VkAllocationCallbacks& allocator) : is_instance_(false), allocator_(allocator), + loader_api_version_(VK_API_VERSION_1_1), + icd_api_version_(icd_api_version), physical_dev_(physical_dev), dev_info_(create_info), - extension_filter_() { - // initialize with baseline core API version - hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0); - hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0); -} + extension_filter_() {} CreateInfoWrapper::~CreateInfoWrapper() { allocator_.pfnFree(allocator_.pUserData, extension_filter_.exts); @@ -357,7 +387,9 @@ CreateInfoWrapper::~CreateInfoWrapper() { } VkResult CreateInfoWrapper::Validate() { - VkResult result = SanitizePNext(); + VkResult result = SanitizeApiVersion(); + if (result == VK_SUCCESS) + result = SanitizePNext(); if (result == VK_SUCCESS) result = SanitizeLayers(); if (result == VK_SUCCESS) @@ -384,6 +416,22 @@ CreateInfoWrapper::operator const VkDeviceCreateInfo*() const { return &dev_info_; } +VkResult CreateInfoWrapper::SanitizeApiVersion() { + if (!is_instance_ || !instance_info_.pApplicationInfo) + return VK_SUCCESS; + + if (icd_api_version_ > VK_API_VERSION_1_0 || + instance_info_.pApplicationInfo->apiVersion < VK_API_VERSION_1_1) + return VK_SUCCESS; + + // override apiVersion to avoid error return from 1.0 icd + application_info_ = *instance_info_.pApplicationInfo; + application_info_.apiVersion = VK_API_VERSION_1_0; + instance_info_.pApplicationInfo = &application_info_; + + return VK_SUCCESS; +} + VkResult CreateInfoWrapper::SanitizePNext() { const struct StructHeader { VkStructureType type; @@ -431,15 +479,33 @@ VkResult CreateInfoWrapper::SanitizeExtensions() { : dev_info_.ppEnabledExtensionNames; auto& ext_count = (is_instance_) ? instance_info_.enabledExtensionCount : dev_info_.enabledExtensionCount; - if (!ext_count) - return VK_SUCCESS; VkResult result = InitExtensionFilter(); if (result != VK_SUCCESS) return result; - for (uint32_t i = 0; i < ext_count; i++) - FilterExtension(ext_names[i]); + if (is_instance_ && icd_api_version_ < loader_api_version_) { + for (uint32_t i = 0; i < ext_count; i++) { + // Upon api downgrade, skip the promoted instance extensions in the + // first pass to avoid duplicate extensions. + const std::optional<uint32_t> version = + GetInstanceExtensionPromotedVersion(ext_names[i]); + if (version && *version > icd_api_version_ && + *version <= loader_api_version_) + continue; + + FilterExtension(ext_names[i]); + } + + // Enable the required extensions to support core functionalities. + const auto promoted_extensions = GetPromotedInstanceExtensions( + icd_api_version_, loader_api_version_); + for (const auto& promoted_extension : promoted_extensions) + FilterExtension(promoted_extension); + } else { + for (uint32_t i = 0; i < ext_count; i++) + FilterExtension(ext_names[i]); + } // Enable device extensions that contain physical-device commands, so that // vkGetInstanceProcAddr will return those physical-device commands. @@ -447,6 +513,23 @@ VkResult CreateInfoWrapper::SanitizeExtensions() { hook_extensions_.set(ProcHook::KHR_swapchain); } + const uint32_t api_version = + is_instance_ ? loader_api_version_ + : std::min(icd_api_version_, loader_api_version_); + switch (api_version) { + case VK_API_VERSION_1_1: + hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1); + hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1); + [[clang::fallthrough]]; + case VK_API_VERSION_1_0: + hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0); + hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0); + break; + default: + ALOGE("Unknown API version[%u]", api_version); + break; + } + ext_names = extension_filter_.names; ext_count = extension_filter_.name_count; @@ -504,10 +587,24 @@ VkResult CreateInfoWrapper::InitExtensionFilter() { filter.ext_count = count; // allocate name array - uint32_t enabled_ext_count = (is_instance_) - ? instance_info_.enabledExtensionCount - : dev_info_.enabledExtensionCount; - count = std::min(filter.ext_count, enabled_ext_count); + if (is_instance_) { + uint32_t enabled_ext_count = instance_info_.enabledExtensionCount; + + // It requires enabling additional promoted extensions to downgrade api, + // so we reserve enough space here. + if (icd_api_version_ < loader_api_version_) { + enabled_ext_count += CountPromotedInstanceExtensions( + icd_api_version_, loader_api_version_); + } + + count = std::min(filter.ext_count, enabled_ext_count); + } else { + count = std::min(filter.ext_count, dev_info_.enabledExtensionCount); + } + + if (!count) + return VK_SUCCESS; + filter.names = reinterpret_cast<const char**>(allocator_.pfnAllocation( allocator_.pUserData, sizeof(const char*) * count, alignof(const char*), VK_SYSTEM_ALLOCATION_SCOPE_COMMAND)); @@ -535,6 +632,10 @@ void CreateInfoWrapper::FilterExtension(const char* name) { hook_extensions_.set(ext_bit); break; case ProcHook::KHR_get_physical_device_properties2: + case ProcHook::KHR_device_group_creation: + case ProcHook::KHR_external_memory_capabilities: + case ProcHook::KHR_external_semaphore_capabilities: + case ProcHook::KHR_external_fence_capabilities: case ProcHook::EXTENSION_UNKNOWN: // Extensions we don't need to do anything about at this level break; @@ -592,6 +693,10 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::KHR_android_surface: case ProcHook::KHR_get_physical_device_properties2: + case ProcHook::KHR_device_group_creation: + case ProcHook::KHR_external_memory_capabilities: + case ProcHook::KHR_external_semaphore_capabilities: + case ProcHook::KHR_external_fence_capabilities: case ProcHook::KHR_get_surface_capabilities2: case ProcHook::KHR_surface: case ProcHook::EXT_debug_report: @@ -638,40 +743,6 @@ void CreateInfoWrapper::FilterExtension(const char* name) { } } -void CreateInfoWrapper::DowngradeApiVersion() { - // If pApplicationInfo is NULL, apiVersion is assumed to be 1.0: - if (instance_info_.pApplicationInfo) { - application_info_ = *instance_info_.pApplicationInfo; - instance_info_.pApplicationInfo = &application_info_; - application_info_.apiVersion = VK_API_VERSION_1_0; - } -} - -void CreateInfoWrapper::UpgradeDeviceCoreApiVersion(uint32_t api_version) { - ALOG_ASSERT(!is_instance_, "Device only API called by instance wrapper."); - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" - api_version ^= VK_VERSION_PATCH(api_version); -#pragma clang diagnostic pop - - // cap the API version to the loader supported highest version - if (api_version > VK_API_VERSION_1_1) - api_version = VK_API_VERSION_1_1; - - switch (api_version) { - case VK_API_VERSION_1_1: - hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1); - hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1); - [[clang::fallthrough]]; - case VK_API_VERSION_1_0: - break; - default: - ALOGD("Unknown upgrade API version[%u]", api_version); - break; - } -} - VKAPI_ATTR void* DefaultAllocate(void*, size_t size, size_t alignment, @@ -903,21 +974,14 @@ VkResult EnumerateInstanceExtensionProperties( return result; } -bool QueryPresentationProperties( +void QueryPresentationProperties( VkPhysicalDevice physicalDevice, - VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties) { - const InstanceData& data = GetData(physicalDevice); - - // GPDP2 must be present and enabled on the instance. - if (!data.driver.GetPhysicalDeviceProperties2KHR && - !data.driver.GetPhysicalDeviceProperties2) - return false; - + VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties) { // Request the android-specific presentation properties via GPDP2 - VkPhysicalDeviceProperties2KHR properties = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, + VkPhysicalDeviceProperties2 properties = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, presentation_properties, - {} + {}, }; #pragma clang diagnostic push @@ -928,14 +992,7 @@ bool QueryPresentationProperties( presentation_properties->pNext = nullptr; presentation_properties->sharedImage = VK_FALSE; - if (data.driver.GetPhysicalDeviceProperties2KHR) { - data.driver.GetPhysicalDeviceProperties2KHR(physicalDevice, - &properties); - } else { - data.driver.GetPhysicalDeviceProperties2(physicalDevice, &properties); - } - - return true; + GetPhysicalDeviceProperties2(physicalDevice, &properties); } VkResult EnumerateDeviceExtensionProperties( @@ -957,8 +1014,8 @@ VkResult EnumerateDeviceExtensionProperties( } VkPhysicalDevicePresentationPropertiesANDROID presentation_properties; - if (QueryPresentationProperties(physicalDevice, &presentation_properties) && - presentation_properties.sharedImage) { + QueryPresentationProperties(physicalDevice, &presentation_properties); + if (presentation_properties.sharedImage) { loader_extensions.push_back({ VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME, VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION}); @@ -1027,49 +1084,32 @@ VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks& data_allocator = (pAllocator) ? *pAllocator : GetDefaultAllocator(); - CreateInfoWrapper wrapper(*pCreateInfo, data_allocator); - VkResult result = wrapper.Validate(); - if (result != VK_SUCCESS) - return result; - - ATRACE_BEGIN("AllocateInstanceData"); - InstanceData* data = AllocateInstanceData(data_allocator); - ATRACE_END(); - if (!data) - return VK_ERROR_OUT_OF_HOST_MEMORY; - - data->hook_extensions |= wrapper.GetHookExtensions(); - - ATRACE_BEGIN("autoDowngradeApiVersion"); -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" - uint32_t api_version = ((pCreateInfo->pApplicationInfo) - ? pCreateInfo->pApplicationInfo->apiVersion - : VK_API_VERSION_1_0); - uint32_t api_major_version = VK_VERSION_MAJOR(api_version); - uint32_t api_minor_version = VK_VERSION_MINOR(api_version); - uint32_t icd_api_version; + VkResult result = VK_SUCCESS; + uint32_t icd_api_version = VK_API_VERSION_1_0; PFN_vkEnumerateInstanceVersion pfn_enumerate_instance_version = reinterpret_cast<PFN_vkEnumerateInstanceVersion>( Hal::Device().GetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); - if (!pfn_enumerate_instance_version) { - icd_api_version = VK_API_VERSION_1_0; - } else { + if (pfn_enumerate_instance_version) { ATRACE_BEGIN("pfn_enumerate_instance_version"); result = (*pfn_enumerate_instance_version)(&icd_api_version); ATRACE_END(); - } - uint32_t icd_api_major_version = VK_VERSION_MAJOR(icd_api_version); - uint32_t icd_api_minor_version = VK_VERSION_MINOR(icd_api_version); + if (result != VK_SUCCESS) + return result; - if ((icd_api_major_version == 1) && (icd_api_minor_version == 0) && - ((api_major_version > 1) || (api_minor_version > 0))) { - api_version = VK_API_VERSION_1_0; - wrapper.DowngradeApiVersion(); + icd_api_version ^= VK_VERSION_PATCH(icd_api_version); } -#pragma clang diagnostic pop - ATRACE_END(); + + CreateInfoWrapper wrapper(*pCreateInfo, icd_api_version, data_allocator); + result = wrapper.Validate(); + if (result != VK_SUCCESS) + return result; + + InstanceData* data = AllocateInstanceData(data_allocator); + if (!data) + return VK_ERROR_OUT_OF_HOST_MEMORY; + + data->hook_extensions |= wrapper.GetHookExtensions(); // call into the driver VkInstance instance; @@ -1133,7 +1173,16 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkAllocationCallbacks& data_allocator = (pAllocator) ? *pAllocator : instance_data.allocator; - CreateInfoWrapper wrapper(physicalDevice, *pCreateInfo, data_allocator); + VkPhysicalDeviceProperties properties; + ATRACE_BEGIN("driver.GetPhysicalDeviceProperties"); + instance_data.driver.GetPhysicalDeviceProperties(physicalDevice, + &properties); + ATRACE_END(); + + CreateInfoWrapper wrapper( + physicalDevice, *pCreateInfo, + properties.apiVersion ^ VK_VERSION_PATCH(properties.apiVersion), + data_allocator); VkResult result = wrapper.Validate(); if (result != VK_SUCCESS) return result; @@ -1145,13 +1194,6 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, if (!data) return VK_ERROR_OUT_OF_HOST_MEMORY; - VkPhysicalDeviceProperties properties; - ATRACE_BEGIN("driver.GetPhysicalDeviceProperties"); - instance_data.driver.GetPhysicalDeviceProperties(physicalDevice, - &properties); - ATRACE_END(); - - wrapper.UpgradeDeviceCoreApiVersion(properties.apiVersion); data->hook_extensions |= wrapper.GetHookExtensions(); // call into the driver @@ -1248,7 +1290,8 @@ VkResult EnumeratePhysicalDeviceGroups( VkResult result = VK_SUCCESS; const auto& data = GetData(instance); - if (!data.driver.EnumeratePhysicalDeviceGroups) { + if (!data.driver.EnumeratePhysicalDeviceGroups && + !data.driver.EnumeratePhysicalDeviceGroupsKHR) { uint32_t device_count = 0; result = EnumeratePhysicalDevices(instance, &device_count, nullptr); if (result < 0) @@ -1280,9 +1323,15 @@ VkResult EnumeratePhysicalDeviceGroups( pPhysicalDeviceGroupProperties[i].subsetAllocation = 0; } } else { - result = data.driver.EnumeratePhysicalDeviceGroups( - instance, pPhysicalDeviceGroupCount, - pPhysicalDeviceGroupProperties); + if (data.driver.EnumeratePhysicalDeviceGroups) { + result = data.driver.EnumeratePhysicalDeviceGroups( + instance, pPhysicalDeviceGroupCount, + pPhysicalDeviceGroupProperties); + } else { + result = data.driver.EnumeratePhysicalDeviceGroupsKHR( + instance, pPhysicalDeviceGroupCount, + pPhysicalDeviceGroupProperties); + } if ((result == VK_SUCCESS || result == VK_INCOMPLETE) && *pPhysicalDeviceGroupCount && pPhysicalDeviceGroupProperties) { for (uint32_t i = 0; i < *pPhysicalDeviceGroupCount; i++) { @@ -1323,10 +1372,10 @@ void GetDeviceQueue2(VkDevice device, if (*pQueue != VK_NULL_HANDLE) SetData(*pQueue, data); } -VKAPI_ATTR VkResult -AllocateCommandBuffers(VkDevice device, - const VkCommandBufferAllocateInfo* pAllocateInfo, - VkCommandBuffer* pCommandBuffers) { +VkResult AllocateCommandBuffers( + VkDevice device, + const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCommandBuffers) { ATRACE_CALL(); const auto& data = GetData(device); @@ -1341,10 +1390,10 @@ AllocateCommandBuffers(VkDevice device, return result; } -VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, - uint32_t submitCount, - const VkSubmitInfo* pSubmits, - VkFence fence) { +VkResult QueueSubmit(VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence) { ATRACE_CALL(); const auto& data = GetData(queue); @@ -1352,5 +1401,198 @@ VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence); } +void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceFeatures2) { + driver.GetPhysicalDeviceFeatures2(physicalDevice, pFeatures); + return; + } + + driver.GetPhysicalDeviceFeatures2KHR(physicalDevice, pFeatures); +} + +void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties2* pProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceProperties2) { + driver.GetPhysicalDeviceProperties2(physicalDevice, pProperties); + return; + } + + driver.GetPhysicalDeviceProperties2KHR(physicalDevice, pProperties); +} + +void GetPhysicalDeviceFormatProperties2( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties2* pFormatProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceFormatProperties2) { + driver.GetPhysicalDeviceFormatProperties2(physicalDevice, format, + pFormatProperties); + return; + } + + driver.GetPhysicalDeviceFormatProperties2KHR(physicalDevice, format, + pFormatProperties); +} + +VkResult GetPhysicalDeviceImageFormatProperties2( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, + VkImageFormatProperties2* pImageFormatProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceImageFormatProperties2) { + return driver.GetPhysicalDeviceImageFormatProperties2( + physicalDevice, pImageFormatInfo, pImageFormatProperties); + } + + return driver.GetPhysicalDeviceImageFormatProperties2KHR( + physicalDevice, pImageFormatInfo, pImageFormatProperties); +} + +void GetPhysicalDeviceQueueFamilyProperties2( + VkPhysicalDevice physicalDevice, + uint32_t* pQueueFamilyPropertyCount, + VkQueueFamilyProperties2* pQueueFamilyProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceQueueFamilyProperties2) { + driver.GetPhysicalDeviceQueueFamilyProperties2( + physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties); + return; + } + + driver.GetPhysicalDeviceQueueFamilyProperties2KHR( + physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties); +} + +void GetPhysicalDeviceMemoryProperties2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties2* pMemoryProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceMemoryProperties2) { + driver.GetPhysicalDeviceMemoryProperties2(physicalDevice, + pMemoryProperties); + return; + } + + driver.GetPhysicalDeviceMemoryProperties2KHR(physicalDevice, + pMemoryProperties); +} + +void GetPhysicalDeviceSparseImageFormatProperties2( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, + uint32_t* pPropertyCount, + VkSparseImageFormatProperties2* pProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceSparseImageFormatProperties2) { + driver.GetPhysicalDeviceSparseImageFormatProperties2( + physicalDevice, pFormatInfo, pPropertyCount, pProperties); + return; + } + + driver.GetPhysicalDeviceSparseImageFormatProperties2KHR( + physicalDevice, pFormatInfo, pPropertyCount, pProperties); +} + +void GetPhysicalDeviceExternalBufferProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, + VkExternalBufferProperties* pExternalBufferProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceExternalBufferProperties) { + driver.GetPhysicalDeviceExternalBufferProperties( + physicalDevice, pExternalBufferInfo, pExternalBufferProperties); + return; + } + + if (driver.GetPhysicalDeviceExternalBufferPropertiesKHR) { + driver.GetPhysicalDeviceExternalBufferPropertiesKHR( + physicalDevice, pExternalBufferInfo, pExternalBufferProperties); + return; + } + + memset(&pExternalBufferProperties->externalMemoryProperties, 0, + sizeof(VkExternalMemoryProperties)); +} + +void GetPhysicalDeviceExternalSemaphoreProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, + VkExternalSemaphoreProperties* pExternalSemaphoreProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceExternalSemaphoreProperties) { + driver.GetPhysicalDeviceExternalSemaphoreProperties( + physicalDevice, pExternalSemaphoreInfo, + pExternalSemaphoreProperties); + return; + } + + if (driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR) { + driver.GetPhysicalDeviceExternalSemaphorePropertiesKHR( + physicalDevice, pExternalSemaphoreInfo, + pExternalSemaphoreProperties); + return; + } + + pExternalSemaphoreProperties->exportFromImportedHandleTypes = 0; + pExternalSemaphoreProperties->compatibleHandleTypes = 0; + pExternalSemaphoreProperties->externalSemaphoreFeatures = 0; +} + +void GetPhysicalDeviceExternalFenceProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, + VkExternalFenceProperties* pExternalFenceProperties) { + ATRACE_CALL(); + + const auto& driver = GetData(physicalDevice).driver; + + if (driver.GetPhysicalDeviceExternalFenceProperties) { + driver.GetPhysicalDeviceExternalFenceProperties( + physicalDevice, pExternalFenceInfo, pExternalFenceProperties); + return; + } + + if (driver.GetPhysicalDeviceExternalFencePropertiesKHR) { + driver.GetPhysicalDeviceExternalFencePropertiesKHR( + physicalDevice, pExternalFenceInfo, pExternalFenceProperties); + return; + } + + pExternalFenceProperties->exportFromImportedHandleTypes = 0; + pExternalFenceProperties->compatibleHandleTypes = 0; + pExternalFenceProperties->externalFenceFeatures = 0; +} + } // namespace driver } // namespace vulkan diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h index 23c717cf5d..14c516b16b 100644 --- a/vulkan/libvulkan/driver.h +++ b/vulkan/libvulkan/driver.h @@ -103,30 +103,95 @@ struct DeviceData { bool OpenHAL(); const VkAllocationCallbacks& GetDefaultAllocator(); -bool QueryPresentationProperties( +void QueryPresentationProperties( VkPhysicalDevice physicalDevice, - VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties); - -// clang-format off -VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName); -VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName); -VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); - -VKAPI_ATTR VkResult EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); - -VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); -VKAPI_ATTR void DestroyInstance(VkInstance instance, const VkAllocationCallbacks* pAllocator); -VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); -VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator); - -VKAPI_ATTR VkResult EnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices); -VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); - -VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue); -VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); -VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers); -VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence); -// clang-format on + VkPhysicalDevicePresentationPropertiesANDROID* presentation_properties); + +VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, + const char* pName); +VKAPI_ATTR PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, + const char* pName); +VKAPI_ATTR VkResult +EnumerateInstanceExtensionProperties(const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); +VKAPI_ATTR VkResult +EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); +VKAPI_ATTR VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkInstance* pInstance); +VKAPI_ATTR void DestroyInstance(VkInstance instance, + const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice); +VKAPI_ATTR void DestroyDevice(VkDevice device, + const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR VkResult +EnumeratePhysicalDevices(VkInstance instance, + uint32_t* pPhysicalDeviceCount, + VkPhysicalDevice* pPhysicalDevices); +VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups( + VkInstance instance, + uint32_t* pPhysicalDeviceGroupCount, + VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); +VKAPI_ATTR void GetDeviceQueue(VkDevice device, + uint32_t queueFamilyIndex, + uint32_t queueIndex, + VkQueue* pQueue); +VKAPI_ATTR void GetDeviceQueue2(VkDevice device, + const VkDeviceQueueInfo2* pQueueInfo, + VkQueue* pQueue); +VKAPI_ATTR VkResult +AllocateCommandBuffers(VkDevice device, + const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCommandBuffers); +VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence); +VKAPI_ATTR void GetPhysicalDeviceFeatures2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures); +VKAPI_ATTR void GetPhysicalDeviceProperties2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties2* pProperties); +VKAPI_ATTR void GetPhysicalDeviceFormatProperties2( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties2* pFormatProperties); +VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, + VkImageFormatProperties2* pImageFormatProperties); +VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2( + VkPhysicalDevice physicalDevice, + uint32_t* pQueueFamilyPropertyCount, + VkQueueFamilyProperties2* pQueueFamilyProperties); +VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties2* pMemoryProperties); +VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, + uint32_t* pPropertyCount, + VkSparseImageFormatProperties2* pProperties); +VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, + VkExternalBufferProperties* pExternalBufferProperties); +VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, + VkExternalSemaphoreProperties* pExternalSemaphoreProperties); +VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, + VkExternalFenceProperties* pExternalFenceProperties); template <typename DispatchableType> void StaticAssertDispatchable(DispatchableType) { diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index 52205e9e79..5f37a50a03 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -363,6 +363,55 @@ const ProcHook g_proc_hooks[] = { reinterpret_cast<PFN_vkVoidFunction>(checkedGetPastPresentationTimingGOOGLE), }, { + "vkGetPhysicalDeviceExternalBufferProperties", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalBufferProperties), + nullptr, + }, + { + "vkGetPhysicalDeviceExternalFenceProperties", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalFenceProperties), + nullptr, + }, + { + "vkGetPhysicalDeviceExternalSemaphoreProperties", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceExternalSemaphoreProperties), + nullptr, + }, + { + "vkGetPhysicalDeviceFeatures2", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFeatures2), + nullptr, + }, + { + "vkGetPhysicalDeviceFormatProperties2", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceFormatProperties2), + nullptr, + }, + { + "vkGetPhysicalDeviceImageFormatProperties2", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceImageFormatProperties2), + nullptr, + }, + { + "vkGetPhysicalDeviceMemoryProperties2", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceMemoryProperties2), + nullptr, + }, + { "vkGetPhysicalDevicePresentRectanglesKHR", ProcHook::INSTANCE, ProcHook::KHR_swapchain, @@ -370,6 +419,27 @@ const ProcHook g_proc_hooks[] = { nullptr, }, { + "vkGetPhysicalDeviceProperties2", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceProperties2), + nullptr, + }, + { + "vkGetPhysicalDeviceQueueFamilyProperties2", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceQueueFamilyProperties2), + nullptr, + }, + { + "vkGetPhysicalDeviceSparseImageFormatProperties2", + ProcHook::INSTANCE, + ProcHook::EXTENSION_CORE_1_1, + reinterpret_cast<PFN_vkVoidFunction>(GetPhysicalDeviceSparseImageFormatProperties2), + nullptr, + }, + { "vkGetPhysicalDeviceSurfaceCapabilities2KHR", ProcHook::INSTANCE, ProcHook::KHR_get_surface_capabilities2, @@ -480,10 +550,9 @@ const ProcHook g_proc_hooks[] = { } // namespace const ProcHook* GetProcHook(const char* name) { - const auto& begin = g_proc_hooks; - const auto& end = - g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]); - const auto hook = std::lower_bound( + auto begin = std::cbegin(g_proc_hooks); + auto end = std::cend(g_proc_hooks); + auto hook = std::lower_bound( begin, end, name, [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; }); return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr; @@ -505,6 +574,10 @@ ProcHook::Extension GetProcHookExtension(const char* name) { if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer; if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2; if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2; + if (strcmp(name, "VK_KHR_device_group_creation") == 0) return ProcHook::KHR_device_group_creation; + if (strcmp(name, "VK_KHR_external_memory_capabilities") == 0) return ProcHook::KHR_external_memory_capabilities; + if (strcmp(name, "VK_KHR_external_semaphore_capabilities") == 0) return ProcHook::KHR_external_semaphore_capabilities; + if (strcmp(name, "VK_KHR_external_fence_capabilities") == 0) return ProcHook::KHR_external_fence_capabilities; // clang-format on return ProcHook::EXTENSION_UNKNOWN; } @@ -543,9 +616,28 @@ bool InitDriverTable(VkInstance instance, INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT); INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT); INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT); + INIT_PROC(false, instance, GetPhysicalDeviceFeatures2); + INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFeatures2KHR); INIT_PROC(false, instance, GetPhysicalDeviceProperties2); INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR); + INIT_PROC(false, instance, GetPhysicalDeviceFormatProperties2); + INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceFormatProperties2KHR); + INIT_PROC(false, instance, GetPhysicalDeviceImageFormatProperties2); + INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceImageFormatProperties2KHR); + INIT_PROC(false, instance, GetPhysicalDeviceQueueFamilyProperties2); + INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceQueueFamilyProperties2KHR); + INIT_PROC(false, instance, GetPhysicalDeviceMemoryProperties2); + INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceMemoryProperties2KHR); + INIT_PROC(false, instance, GetPhysicalDeviceSparseImageFormatProperties2); + INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceSparseImageFormatProperties2KHR); + INIT_PROC(false, instance, GetPhysicalDeviceExternalBufferProperties); + INIT_PROC_EXT(KHR_external_memory_capabilities, true, instance, GetPhysicalDeviceExternalBufferPropertiesKHR); + INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties); + INIT_PROC_EXT(KHR_external_semaphore_capabilities, true, instance, GetPhysicalDeviceExternalSemaphorePropertiesKHR); + INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties); + INIT_PROC_EXT(KHR_external_fence_capabilities, true, instance, GetPhysicalDeviceExternalFencePropertiesKHR); INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups); + INIT_PROC_EXT(KHR_device_group_creation, true, instance, EnumeratePhysicalDeviceGroupsKHR); // clang-format on return success; @@ -577,5 +669,53 @@ bool InitDriverTable(VkDevice dev, return success; } +const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = { + // clang-format off + std::make_pair("VK_KHR_device_group_creation", VK_API_VERSION_1_1), + std::make_pair("VK_KHR_external_fence_capabilities", VK_API_VERSION_1_1), + std::make_pair("VK_KHR_external_memory_capabilities", VK_API_VERSION_1_1), + std::make_pair("VK_KHR_external_semaphore_capabilities", VK_API_VERSION_1_1), + std::make_pair("VK_KHR_get_physical_device_properties2", VK_API_VERSION_1_1), + // clang-format on +}; + +std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) { + auto begin = std::cbegin(g_promoted_instance_extensions); + auto end = std::cend(g_promoted_instance_extensions); + auto iter = + std::lower_bound(begin, end, name, + [](const std::pair<const char*, uint32_t>& e, + const char* n) { return strcmp(e.first, n) < 0; }); + return (iter < end && strcmp(iter->first, name) == 0) + ? std::optional<uint32_t>(iter->second) + : std::nullopt; +} + +uint32_t CountPromotedInstanceExtensions(uint32_t begin_version, + uint32_t end_version) { + auto begin = std::cbegin(g_promoted_instance_extensions); + auto end = std::cend(g_promoted_instance_extensions); + uint32_t count = 0; + + for (auto iter = begin; iter != end; iter++) + if (iter->second > begin_version && iter->second <= end_version) + count++; + + return count; +} + +std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version, + uint32_t end_version) { + auto begin = std::cbegin(g_promoted_instance_extensions); + auto end = std::cend(g_promoted_instance_extensions); + std::vector<const char*> extensions; + + for (auto iter = begin; iter != end; iter++) + if (iter->second > begin_version && iter->second <= end_version) + extensions.emplace_back(iter->first); + + return extensions; +} + } // namespace driver } // namespace vulkan diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 62538c1518..047e774004 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -23,6 +23,8 @@ #include <vulkan/vulkan.h> #include <bitset> +#include <optional> +#include <vector> namespace vulkan { namespace driver { @@ -48,6 +50,10 @@ struct ProcHook { ANDROID_external_memory_android_hardware_buffer, KHR_bind_memory2, KHR_get_physical_device_properties2, + KHR_device_group_creation, + KHR_external_memory_capabilities, + KHR_external_semaphore_capabilities, + KHR_external_fence_capabilities, EXTENSION_CORE_1_0, EXTENSION_CORE_1_1, @@ -75,9 +81,28 @@ struct InstanceDriverTable { PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; PFN_vkDebugReportMessageEXT DebugReportMessageEXT; + PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2; + PFN_vkGetPhysicalDeviceFeatures2KHR GetPhysicalDeviceFeatures2KHR; PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2; PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR; + PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2; + PFN_vkGetPhysicalDeviceFormatProperties2KHR GetPhysicalDeviceFormatProperties2KHR; + PFN_vkGetPhysicalDeviceImageFormatProperties2 GetPhysicalDeviceImageFormatProperties2; + PFN_vkGetPhysicalDeviceImageFormatProperties2KHR GetPhysicalDeviceImageFormatProperties2KHR; + PFN_vkGetPhysicalDeviceQueueFamilyProperties2 GetPhysicalDeviceQueueFamilyProperties2; + PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR GetPhysicalDeviceQueueFamilyProperties2KHR; + PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2; + PFN_vkGetPhysicalDeviceMemoryProperties2KHR GetPhysicalDeviceMemoryProperties2KHR; + PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 GetPhysicalDeviceSparseImageFormatProperties2; + PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR GetPhysicalDeviceSparseImageFormatProperties2KHR; + PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties; + PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR GetPhysicalDeviceExternalBufferPropertiesKHR; + PFN_vkGetPhysicalDeviceExternalSemaphoreProperties GetPhysicalDeviceExternalSemaphoreProperties; + PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR GetPhysicalDeviceExternalSemaphorePropertiesKHR; + PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties; + PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR GetPhysicalDeviceExternalFencePropertiesKHR; PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups; + PFN_vkEnumeratePhysicalDeviceGroupsKHR EnumeratePhysicalDeviceGroupsKHR; // clang-format on }; @@ -110,6 +135,12 @@ bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc, const std::bitset<ProcHook::EXTENSION_COUNT>& extensions); +std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name); +uint32_t CountPromotedInstanceExtensions(uint32_t begin_version, + uint32_t end_version); +std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version, + uint32_t end_version); + } // namespace driver } // namespace vulkan diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 6b51817caa..48090af513 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -907,11 +907,10 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); VkPhysicalDevicePresentationPropertiesANDROID present_properties; - if (QueryPresentationProperties(pdev, &present_properties)) { - if (present_properties.sharedImage) { - present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR); - present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR); - } + QueryPresentationProperties(pdev, &present_properties); + if (present_properties.sharedImage) { + present_modes.push_back(VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR); + present_modes.push_back(VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR); } uint32_t num_modes = uint32_t(present_modes.size()); @@ -996,7 +995,6 @@ VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice, strerror(-err), err); } - // TODO(b/143294545): Return something better than "whole window" pRects[0].offset.x = 0; pRects[0].offset.y = 0; pRects[0].extent = VkExtent2D{static_cast<uint32_t>(width), diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py index a64a7026db..6a73023193 100644 --- a/vulkan/scripts/driver_generator.py +++ b/vulkan/scripts/driver_generator.py @@ -40,6 +40,10 @@ _KNOWN_EXTENSIONS = _INTERCEPTED_EXTENSIONS + [ 'VK_ANDROID_external_memory_android_hardware_buffer', 'VK_KHR_bind_memory2', 'VK_KHR_get_physical_device_properties2', + 'VK_KHR_device_group_creation', + 'VK_KHR_external_memory_capabilities', + 'VK_KHR_external_semaphore_capabilities', + 'VK_KHR_external_fence_capabilities', ] # Functions needed at vulkan::driver level. @@ -71,12 +75,41 @@ _NEEDED_COMMANDS = [ 'vkDestroyImage', 'vkGetPhysicalDeviceProperties', - 'vkGetPhysicalDeviceProperties2', - 'vkGetPhysicalDeviceProperties2KHR', # VK_KHR_swapchain v69 requirement 'vkBindImageMemory2', 'vkBindImageMemory2KHR', + + # For promoted VK_KHR_device_group_creation + 'vkEnumeratePhysicalDeviceGroupsKHR', + + # For promoted VK_KHR_get_physical_device_properties2 + 'vkGetPhysicalDeviceFeatures2', + 'vkGetPhysicalDeviceFeatures2KHR', + 'vkGetPhysicalDeviceProperties2', + 'vkGetPhysicalDeviceProperties2KHR', + 'vkGetPhysicalDeviceFormatProperties2', + 'vkGetPhysicalDeviceFormatProperties2KHR', + 'vkGetPhysicalDeviceImageFormatProperties2', + 'vkGetPhysicalDeviceImageFormatProperties2KHR', + 'vkGetPhysicalDeviceQueueFamilyProperties2', + 'vkGetPhysicalDeviceQueueFamilyProperties2KHR', + 'vkGetPhysicalDeviceMemoryProperties2', + 'vkGetPhysicalDeviceMemoryProperties2KHR', + 'vkGetPhysicalDeviceSparseImageFormatProperties2', + 'vkGetPhysicalDeviceSparseImageFormatProperties2KHR', + + # For promoted VK_KHR_external_memory_capabilities + 'vkGetPhysicalDeviceExternalBufferProperties', + 'vkGetPhysicalDeviceExternalBufferPropertiesKHR', + + # For promoted VK_KHR_external_semaphore_capabilities + 'vkGetPhysicalDeviceExternalSemaphoreProperties', + 'vkGetPhysicalDeviceExternalSemaphorePropertiesKHR', + + # For promoted VK_KHR_external_fence_capabilities + 'vkGetPhysicalDeviceExternalFenceProperties', + 'vkGetPhysicalDeviceExternalFencePropertiesKHR', ] # Functions intercepted at vulkan::driver level. @@ -106,6 +139,24 @@ _INTERCEPTED_COMMANDS = [ # VK_KHR_swapchain v69 requirement 'vkBindImageMemory2', 'vkBindImageMemory2KHR', + + # For promoted VK_KHR_get_physical_device_properties2 + 'vkGetPhysicalDeviceFeatures2', + 'vkGetPhysicalDeviceProperties2', + 'vkGetPhysicalDeviceFormatProperties2', + 'vkGetPhysicalDeviceImageFormatProperties2', + 'vkGetPhysicalDeviceQueueFamilyProperties2', + 'vkGetPhysicalDeviceMemoryProperties2', + 'vkGetPhysicalDeviceSparseImageFormatProperties2', + + # For promoted VK_KHR_external_memory_capabilities + 'vkGetPhysicalDeviceExternalBufferProperties', + + # For promoted VK_KHR_external_semaphore_capabilities + 'vkGetPhysicalDeviceExternalSemaphoreProperties', + + # For promoted VK_KHR_external_fence_capabilities + 'vkGetPhysicalDeviceExternalFenceProperties', ] @@ -162,6 +213,8 @@ def gen_h(): #include <vulkan/vulkan.h> #include <bitset> +#include <optional> +#include <vector> namespace vulkan { namespace driver { @@ -229,6 +282,12 @@ bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc, const std::bitset<ProcHook::EXTENSION_COUNT>& extensions); +std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name); +uint32_t CountPromotedInstanceExtensions(uint32_t begin_version, + uint32_t end_version); +std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version, + uint32_t end_version); + } // namespace driver } // namespace vulkan @@ -464,10 +523,9 @@ const ProcHook g_proc_hooks[] = { } // namespace const ProcHook* GetProcHook(const char* name) { - const auto& begin = g_proc_hooks; - const auto& end = - g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]); - const auto hook = std::lower_bound( + auto begin = std::cbegin(g_proc_hooks); + auto end = std::cend(g_proc_hooks); + auto hook = std::lower_bound( begin, end, name, [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; }); return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr; @@ -539,6 +597,54 @@ bool InitDriverTable(VkDevice dev, return success; } +const std::pair<const char*, uint32_t> g_promoted_instance_extensions[] = { + // clang-format off\n""") + + for key, value in sorted(gencom.promoted_inst_ext_dict.items()): + f.write(gencom.indent(1) + 'std::make_pair("' + key + '", ' + value + '),\n') + + f.write("""\ + // clang-format on +}; + +std::optional<uint32_t> GetInstanceExtensionPromotedVersion(const char* name) { + auto begin = std::cbegin(g_promoted_instance_extensions); + auto end = std::cend(g_promoted_instance_extensions); + auto iter = + std::lower_bound(begin, end, name, + [](const std::pair<const char*, uint32_t>& e, + const char* n) { return strcmp(e.first, n) < 0; }); + return (iter < end && strcmp(iter->first, name) == 0) + ? std::optional<uint32_t>(iter->second) + : std::nullopt; +} + +uint32_t CountPromotedInstanceExtensions(uint32_t begin_version, + uint32_t end_version) { + auto begin = std::cbegin(g_promoted_instance_extensions); + auto end = std::cend(g_promoted_instance_extensions); + uint32_t count = 0; + + for (auto iter = begin; iter != end; iter++) + if (iter->second > begin_version && iter->second <= end_version) + count++; + + return count; +} + +std::vector<const char*> GetPromotedInstanceExtensions(uint32_t begin_version, + uint32_t end_version) { + auto begin = std::cbegin(g_promoted_instance_extensions); + auto end = std::cend(g_promoted_instance_extensions); + std::vector<const char*> extensions; + + for (auto iter = begin; iter != end; iter++) + if (iter->second > begin_version && iter->second <= end_version) + extensions.emplace_back(iter->first); + + return extensions; +} + } // namespace driver } // namespace vulkan\n""") diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py index e9a96f3959..72fd4fbc9c 100644 --- a/vulkan/scripts/generator_common.py +++ b/vulkan/scripts/generator_common.py @@ -98,6 +98,9 @@ version_code_list = [] # Dict for mapping a function to the core Vulkan API version. version_dict = {} +# Dict for mapping a promoted instance extension to the core Vulkan API version. +promoted_inst_ext_dict = {} + def indent(num): """Returns the requested indents. @@ -184,6 +187,15 @@ def version_code(version): return version[11:] +def version_2_api_version(version): + """Returns the api version from a version string. + + Args: + version: Vulkan version string. + """ + return 'VK_API' + version[2:] + + def is_function_supported(cmd): """Returns true if a function is core or from a supportable extension. @@ -328,6 +340,7 @@ def parse_vulkan_registry(): return_type_dict version_code_list version_dict + promoted_inst_ext_dict """ registry = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', 'external', 'vulkan-headers', 'registry', 'vk.xml') @@ -380,6 +393,10 @@ def parse_vulkan_registry(): apiversion = 'VK_VERSION_1_0' if extension.tag == 'extension': extname = extension.get('name') + if (extension.get('type') == 'instance' and + extension.get('promotedto') is not None): + promoted_inst_ext_dict[extname] = \ + version_2_api_version(extension.get('promotedto')) for req in extension: if req.get('feature') is not None: apiversion = req.get('feature') diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h index a283b83d7c..52e7bee288 100644 --- a/vulkan/vkjson/vkjson.h +++ b/vulkan/vkjson/vkjson.h @@ -158,10 +158,7 @@ bool VkJsonInstanceFromJson(const std::string& json, VkJsonInstance* instance, std::string* errors); -VkJsonDevice VkJsonGetDevice(VkInstance instance, - VkPhysicalDevice device, - uint32_t instanceExtensionCount, - const char* const* instanceExtensions); +VkJsonDevice VkJsonGetDevice(VkPhysicalDevice device); std::string VkJsonDeviceToJson(const VkJsonDevice& device); bool VkJsonDeviceFromJson(const std::string& json, VkJsonDevice* device, @@ -177,7 +174,7 @@ bool VkJsonImageFormatPropertiesFromJson(const std::string& json, typedef VkJsonDevice VkJsonAllProperties; inline VkJsonAllProperties VkJsonGetAllProperties( VkPhysicalDevice physicalDevice) { - return VkJsonGetDevice(VK_NULL_HANDLE, physicalDevice, 0, nullptr); + return VkJsonGetDevice(physicalDevice); } inline std::string VkJsonAllPropertiesToJson( const VkJsonAllProperties& properties) { diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc index 73586d42f8..eb0fcc3443 100644 --- a/vulkan/vkjson/vkjson_instance.cc +++ b/vulkan/vkjson/vkjson_instance.cc @@ -28,8 +28,6 @@ #include <utility> namespace { -const char* kSupportedInstanceExtensions[] = { - "VK_KHR_get_physical_device_properties2"}; bool EnumerateExtensions(const char* layer_name, std::vector<VkExtensionProperties>* extensions) { @@ -47,15 +45,6 @@ bool EnumerateExtensions(const char* layer_name, } bool HasExtension(const char* extension_name, - uint32_t count, - const char* const* extensions) { - return std::find_if(extensions, extensions + count, - [extension_name](const char* extension) { - return strcmp(extension, extension_name) == 0; - }) != extensions + count; -} - -bool HasExtension(const char* extension_name, const std::vector<VkExtensionProperties>& extensions) { return std::find_if(extensions.cbegin(), extensions.cend(), [extension_name](const VkExtensionProperties& extension) { @@ -65,27 +54,9 @@ bool HasExtension(const char* extension_name, } } // anonymous namespace -VkJsonDevice VkJsonGetDevice(VkInstance instance, - VkPhysicalDevice physical_device, - uint32_t instance_extension_count, - const char* const* instance_extensions) { +VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { VkJsonDevice device; - PFN_vkGetPhysicalDeviceProperties2KHR vkpGetPhysicalDeviceProperties2KHR = - nullptr; - PFN_vkGetPhysicalDeviceFeatures2KHR vkpGetPhysicalDeviceFeatures2KHR = - nullptr; - if (instance != VK_NULL_HANDLE && - HasExtension("VK_KHR_get_physical_device_properties2", - instance_extension_count, instance_extensions)) { - vkpGetPhysicalDeviceProperties2KHR = - reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>( - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2KHR")); - vkpGetPhysicalDeviceFeatures2KHR = - reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>( - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2KHR")); - } - uint32_t extension_count = 0; vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &extension_count, nullptr); @@ -103,55 +74,48 @@ VkJsonDevice VkJsonGetDevice(VkInstance instance, device.layers.data()); } - if (HasExtension("VK_KHR_get_physical_device_properties2", - instance_extension_count, instance_extensions)) { - VkPhysicalDeviceProperties2KHR properties = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, - nullptr, - {} // properties - }; - if (HasExtension("VK_KHR_driver_properties", device.extensions)) { - device.ext_driver_properties.reported = true; - device.ext_driver_properties.driver_properties_khr.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; - device.ext_driver_properties.driver_properties_khr.pNext = - properties.pNext; - properties.pNext = - &device.ext_driver_properties.driver_properties_khr; - } - vkpGetPhysicalDeviceProperties2KHR(physical_device, &properties); - device.properties = properties.properties; + VkPhysicalDeviceProperties2 properties = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + nullptr, + {}, + }; + if (HasExtension("VK_KHR_driver_properties", device.extensions)) { + device.ext_driver_properties.reported = true; + device.ext_driver_properties.driver_properties_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; + device.ext_driver_properties.driver_properties_khr.pNext = properties.pNext; + properties.pNext = &device.ext_driver_properties.driver_properties_khr; + } + vkGetPhysicalDeviceProperties2(physical_device, &properties); + device.properties = properties.properties; - VkPhysicalDeviceFeatures2KHR features = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR, - nullptr, - {} // features - }; - if (HasExtension("VK_KHR_variable_pointers", device.extensions)) { - device.ext_variable_pointer_features.reported = true; - device.ext_variable_pointer_features.variable_pointer_features_khr.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR; - device.ext_variable_pointer_features.variable_pointer_features_khr.pNext = - features.pNext; - features.pNext = - &device.ext_variable_pointer_features.variable_pointer_features_khr; - } - if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) { - device.ext_shader_float16_int8_features.reported = true; - device.ext_shader_float16_int8_features.shader_float16_int8_features_khr - .sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR; - device.ext_shader_float16_int8_features.shader_float16_int8_features_khr - .pNext = features.pNext; - features.pNext = &device.ext_shader_float16_int8_features - .shader_float16_int8_features_khr; - } - vkpGetPhysicalDeviceFeatures2KHR(physical_device, &features); - device.features = features.features; - } else { - vkGetPhysicalDeviceProperties(physical_device, &device.properties); - vkGetPhysicalDeviceFeatures(physical_device, &device.features); + VkPhysicalDeviceFeatures2 features = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + nullptr, + {}, + }; + if (HasExtension("VK_KHR_variable_pointers", device.extensions)) { + device.ext_variable_pointer_features.reported = true; + device.ext_variable_pointer_features.variable_pointer_features_khr.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR; + device.ext_variable_pointer_features.variable_pointer_features_khr.pNext = + features.pNext; + features.pNext = + &device.ext_variable_pointer_features.variable_pointer_features_khr; + } + if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) { + device.ext_shader_float16_int8_features.reported = true; + device.ext_shader_float16_int8_features.shader_float16_int8_features_khr + .sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR; + device.ext_shader_float16_int8_features.shader_float16_int8_features_khr + .pNext = features.pNext; + features.pNext = &device.ext_shader_float16_int8_features + .shader_float16_int8_features_khr; } + vkGetPhysicalDeviceFeatures2(physical_device, &features); + device.features = features.features; + vkGetPhysicalDeviceMemoryProperties(physical_device, &device.memory); uint32_t queue_family_count = 0; @@ -193,135 +157,117 @@ VkJsonDevice VkJsonGetDevice(VkInstance instance, } } - PFN_vkGetPhysicalDeviceProperties2 vkpGetPhysicalDeviceProperties2 = - reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2>( - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceProperties2")); - if (vkpGetPhysicalDeviceProperties2) { - VkPhysicalDeviceProperties2 properties2 = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, nullptr, {}}; - - device.subgroup_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - device.subgroup_properties.pNext = properties2.pNext; - properties2.pNext = &device.subgroup_properties; - - device.point_clipping_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES; - device.point_clipping_properties.pNext = properties2.pNext; - properties2.pNext = &device.point_clipping_properties; - - device.multiview_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; - device.multiview_properties.pNext = properties2.pNext; - properties2.pNext = &device.multiview_properties; - - device.id_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; - device.id_properties.pNext = properties2.pNext; - properties2.pNext = &device.id_properties; - - device.maintenance3_properties.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES; - device.maintenance3_properties.pNext = properties2.pNext; - properties2.pNext = &device.maintenance3_properties; - - (*vkpGetPhysicalDeviceProperties2)(physical_device, &properties2); - } + VkPhysicalDeviceProperties2 properties2 = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + nullptr, + {}, + }; - PFN_vkGetPhysicalDeviceFeatures2 vkpGetPhysicalDeviceFeatures2 = - reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2>( - vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures2")); - if (vkpGetPhysicalDeviceFeatures2) { - VkPhysicalDeviceFeatures2 features2 = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, nullptr, {}}; - - device.bit16_storage_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; - device.bit16_storage_features.pNext = features2.pNext; - features2.pNext = &device.bit16_storage_features; - - device.multiview_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; - device.multiview_features.pNext = features2.pNext; - features2.pNext = &device.multiview_features; - - device.variable_pointer_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES; - device.variable_pointer_features.pNext = features2.pNext; - features2.pNext = &device.variable_pointer_features; - - device.protected_memory_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; - device.protected_memory_features.pNext = features2.pNext; - features2.pNext = &device.protected_memory_features; - - device.sampler_ycbcr_conversion_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; - device.sampler_ycbcr_conversion_features.pNext = features2.pNext; - features2.pNext = &device.sampler_ycbcr_conversion_features; - - device.shader_draw_parameter_features.sType = - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES; - device.shader_draw_parameter_features.pNext = features2.pNext; - features2.pNext = &device.shader_draw_parameter_features; - - (*vkpGetPhysicalDeviceFeatures2)(physical_device, &features2); - } + device.subgroup_properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + device.subgroup_properties.pNext = properties2.pNext; + properties2.pNext = &device.subgroup_properties; + + device.point_clipping_properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES; + device.point_clipping_properties.pNext = properties2.pNext; + properties2.pNext = &device.point_clipping_properties; + + device.multiview_properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; + device.multiview_properties.pNext = properties2.pNext; + properties2.pNext = &device.multiview_properties; + + device.id_properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES; + device.id_properties.pNext = properties2.pNext; + properties2.pNext = &device.id_properties; + + device.maintenance3_properties.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES; + device.maintenance3_properties.pNext = properties2.pNext; + properties2.pNext = &device.maintenance3_properties; + + vkGetPhysicalDeviceProperties2(physical_device, &properties2); + + VkPhysicalDeviceFeatures2 features2 = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + nullptr, + {}, + }; - PFN_vkGetPhysicalDeviceExternalFenceProperties - vkpGetPhysicalDeviceExternalFenceProperties = - reinterpret_cast<PFN_vkGetPhysicalDeviceExternalFenceProperties>( - vkGetInstanceProcAddr( - instance, "vkGetPhysicalDeviceExternalFenceProperties")); - if (vkpGetPhysicalDeviceExternalFenceProperties) { - VkPhysicalDeviceExternalFenceInfo external_fence_info = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr, - VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT}; - VkExternalFenceProperties external_fence_properties = {}; - - for (VkExternalFenceHandleTypeFlagBits handle_type = - VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; - handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; - handle_type = static_cast<VkExternalFenceHandleTypeFlagBits>( - handle_type << 1)) { - external_fence_info.handleType = handle_type; - (*vkpGetPhysicalDeviceExternalFenceProperties)( - physical_device, &external_fence_info, &external_fence_properties); - if (external_fence_properties.exportFromImportedHandleTypes || - external_fence_properties.compatibleHandleTypes || - external_fence_properties.externalFenceFeatures) { - device.external_fence_properties.insert( - std::make_pair(handle_type, external_fence_properties)); - } + device.bit16_storage_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES; + device.bit16_storage_features.pNext = features2.pNext; + features2.pNext = &device.bit16_storage_features; + + device.multiview_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; + device.multiview_features.pNext = features2.pNext; + features2.pNext = &device.multiview_features; + + device.variable_pointer_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES; + device.variable_pointer_features.pNext = features2.pNext; + features2.pNext = &device.variable_pointer_features; + + device.protected_memory_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES; + device.protected_memory_features.pNext = features2.pNext; + features2.pNext = &device.protected_memory_features; + + device.sampler_ycbcr_conversion_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + device.sampler_ycbcr_conversion_features.pNext = features2.pNext; + features2.pNext = &device.sampler_ycbcr_conversion_features; + + device.shader_draw_parameter_features.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES; + device.shader_draw_parameter_features.pNext = features2.pNext; + features2.pNext = &device.shader_draw_parameter_features; + + vkGetPhysicalDeviceFeatures2(physical_device, &features2); + + VkPhysicalDeviceExternalFenceInfo external_fence_info = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, nullptr, + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT}; + VkExternalFenceProperties external_fence_properties = {}; + + for (VkExternalFenceHandleTypeFlagBits handle_type = + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT; + handle_type <= VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT; + handle_type = + static_cast<VkExternalFenceHandleTypeFlagBits>(handle_type << 1)) { + external_fence_info.handleType = handle_type; + vkGetPhysicalDeviceExternalFenceProperties( + physical_device, &external_fence_info, &external_fence_properties); + if (external_fence_properties.exportFromImportedHandleTypes || + external_fence_properties.compatibleHandleTypes || + external_fence_properties.externalFenceFeatures) { + device.external_fence_properties.insert( + std::make_pair(handle_type, external_fence_properties)); } } - PFN_vkGetPhysicalDeviceExternalSemaphoreProperties - vkpGetPhysicalDeviceExternalSemaphoreProperties = reinterpret_cast< - PFN_vkGetPhysicalDeviceExternalSemaphoreProperties>( - vkGetInstanceProcAddr( - instance, "vkGetPhysicalDeviceExternalSemaphoreProperties")); - if (vkpGetPhysicalDeviceExternalSemaphoreProperties) { - VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = { - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr, - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT}; - VkExternalSemaphoreProperties external_semaphore_properties = {}; - - for (VkExternalSemaphoreHandleTypeFlagBits handle_type = - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; - handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>( - handle_type << 1)) { - external_semaphore_info.handleType = handle_type; - (*vkpGetPhysicalDeviceExternalSemaphoreProperties)( - physical_device, &external_semaphore_info, - &external_semaphore_properties); - if (external_semaphore_properties.exportFromImportedHandleTypes || - external_semaphore_properties.compatibleHandleTypes || - external_semaphore_properties.externalSemaphoreFeatures) { - device.external_semaphore_properties.insert( - std::make_pair(handle_type, external_semaphore_properties)); - } + VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = { + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, nullptr, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT}; + VkExternalSemaphoreProperties external_semaphore_properties = {}; + + for (VkExternalSemaphoreHandleTypeFlagBits handle_type = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT; + handle_type <= VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; + handle_type = static_cast<VkExternalSemaphoreHandleTypeFlagBits>( + handle_type << 1)) { + external_semaphore_info.handleType = handle_type; + vkGetPhysicalDeviceExternalSemaphoreProperties( + physical_device, &external_semaphore_info, + &external_semaphore_properties); + if (external_semaphore_properties.exportFromImportedHandleTypes || + external_semaphore_properties.compatibleHandleTypes || + external_semaphore_properties.externalSemaphoreFeatures) { + device.external_semaphore_properties.insert( + std::make_pair(handle_type, external_semaphore_properties)); } } } @@ -355,19 +301,15 @@ VkJsonInstance VkJsonGetInstance() { if (!EnumerateExtensions(nullptr, &instance.extensions)) return VkJsonInstance(); - std::vector<const char*> instance_extensions; - for (const auto extension : kSupportedInstanceExtensions) { - if (HasExtension(extension, instance.extensions)) - instance_extensions.push_back(extension); - } - - const VkApplicationInfo app_info = {VK_STRUCTURE_TYPE_APPLICATION_INFO, - nullptr, - "vkjson_info", - 1, - "", - 0, - VK_API_VERSION_1_1}; + const VkApplicationInfo app_info = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, + nullptr, + "vkjson_info", + 1, + "", + 0, + VK_API_VERSION_1_1, + }; VkInstanceCreateInfo instance_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, nullptr, @@ -375,8 +317,9 @@ VkJsonInstance VkJsonGetInstance() { &app_info, 0, nullptr, - static_cast<uint32_t>(instance_extensions.size()), - instance_extensions.data()}; + 0, + nullptr, + }; VkInstance vkinstance; result = vkCreateInstance(&instance_info, nullptr, &vkinstance); if (result != VK_SUCCESS) @@ -401,52 +344,38 @@ VkJsonInstance VkJsonGetInstance() { instance.devices.reserve(sz); for (uint32_t i = 0; i < sz; ++i) { device_map.insert(std::make_pair(devices[i], i)); - instance.devices.emplace_back(VkJsonGetDevice(vkinstance, devices[i], - instance_extensions.size(), - instance_extensions.data())); + instance.devices.emplace_back(VkJsonGetDevice(devices[i])); } - PFN_vkEnumerateInstanceVersion vkpEnumerateInstanceVersion = - reinterpret_cast<PFN_vkEnumerateInstanceVersion>( - vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); - if (!vkpEnumerateInstanceVersion) { - instance.api_version = VK_API_VERSION_1_0; - } else { - result = (*vkpEnumerateInstanceVersion)(&instance.api_version); - if (result != VK_SUCCESS) { - vkDestroyInstance(vkinstance, nullptr); - return VkJsonInstance(); - } + result = vkEnumerateInstanceVersion(&instance.api_version); + if (result != VK_SUCCESS) { + vkDestroyInstance(vkinstance, nullptr); + return VkJsonInstance(); } - PFN_vkEnumeratePhysicalDeviceGroups vkpEnumeratePhysicalDeviceGroups = - reinterpret_cast<PFN_vkEnumeratePhysicalDeviceGroups>( - vkGetInstanceProcAddr(vkinstance, "vkEnumeratePhysicalDeviceGroups")); - if (vkpEnumeratePhysicalDeviceGroups) { - count = 0; - result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count, nullptr); - if (result != VK_SUCCESS) { - vkDestroyInstance(vkinstance, nullptr); - return VkJsonInstance(); - } + count = 0; + result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, nullptr); + if (result != VK_SUCCESS) { + vkDestroyInstance(vkinstance, nullptr); + return VkJsonInstance(); + } - VkJsonDeviceGroup device_group; - std::vector<VkPhysicalDeviceGroupProperties> group_properties; - group_properties.resize(count); - result = (*vkpEnumeratePhysicalDeviceGroups)(vkinstance, &count, - group_properties.data()); - if (result != VK_SUCCESS) { - vkDestroyInstance(vkinstance, nullptr); - return VkJsonInstance(); - } - for (auto properties : group_properties) { - device_group.properties = properties; - for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) { - device_group.device_inds.push_back( - device_map[properties.physicalDevices[i]]); - } - instance.device_groups.push_back(device_group); + VkJsonDeviceGroup device_group; + std::vector<VkPhysicalDeviceGroupProperties> group_properties; + group_properties.resize(count); + result = vkEnumeratePhysicalDeviceGroups(vkinstance, &count, + group_properties.data()); + if (result != VK_SUCCESS) { + vkDestroyInstance(vkinstance, nullptr); + return VkJsonInstance(); + } + for (auto properties : group_properties) { + device_group.properties = properties; + for (uint32_t i = 0; i < properties.physicalDeviceCount; ++i) { + device_group.device_inds.push_back( + device_map[properties.physicalDevices[i]]); } + instance.device_groups.push_back(device_group); } vkDestroyInstance(vkinstance, nullptr); |