diff options
150 files changed, 3954 insertions, 928 deletions
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index fdac5db1d1..316f04cb27 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -316,97 +316,6 @@ on late-init && property:ro.boot.fastboot.boottrace= write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0 -# Only create the tracing instance if persist.mm_events.enabled -# Attempting to remove the tracing instance after it has been created -# will likely fail with EBUSY as it would be in use by traced_probes. -on mm_events_property_available && property:persist.mm_events.enabled=true -# Create MM Events Tracing Instance for Kmem Activity Trigger - mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system - mkdir /sys/kernel/tracing/instances/mm_events 0755 system system - -# Read and set per CPU buffer size - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb - chmod 0666 /sys/kernel/tracing/instances/mm_events/buffer_size_kb - -# Set the default buffer size to the minimum - write /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb 1 - write /sys/kernel/tracing/instances/mm_events/buffer_size_kb 1 - -# Read and enable tracing - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/tracing_on - chmod 0666 /sys/kernel/tracing/instances/mm_events/tracing_on - -# Tracing disabled by default - write /sys/kernel/debug/tracing/instances/mm_events/tracing_on 0 - write /sys/kernel/tracing/instances/mm_events/tracing_on 0 - -# Read and truncate kernel trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/trace - -# Enable trace events - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/vmscan/mm_vmscan_direct_reclaim_begin/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/vmscan/mm_vmscan_direct_reclaim_begin/enable - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/vmscan/mm_vmscan_kswapd_wake/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/vmscan/mm_vmscan_kswapd_wake/enable - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/compaction/mm_compaction_begin/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/compaction/mm_compaction_begin/enable - -# Read and clear per-CPU raw kernel trace -# Cannot use wildcards in .rc files. Update this if there is a phone with -# more CPUs. - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu0/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu0/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu1/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu1/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu2/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu2/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu3/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu3/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu4/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu4/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu5/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu5/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu6/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu6/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu7/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu7/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu8/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu8/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu9/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu9/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu10/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu10/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu11/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu11/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu12/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu12/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu13/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu13/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu14/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu14/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu15/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu15/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu16/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu16/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu17/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu17/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu18/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu18/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu19/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu19/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu20/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu20/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu21/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu21/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu22/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu22/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace - -on property:ro.persistent_properties.ready=true - trigger mm_events_property_available - # Handle hyp tracing instance on late-init && property:ro.boot.hypervisor.vm.supported=1 diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp index 6e6d27d463..a2e171fa3b 100644 --- a/cmds/flatland/GLHelper.cpp +++ b/cmds/flatland/GLHelper.cpp @@ -241,7 +241,7 @@ bool GLHelper::createWindowSurface(uint32_t w, uint32_t h, status_t err; if (mSurfaceComposerClient == nullptr) { - mSurfaceComposerClient = new SurfaceComposerClient; + mSurfaceComposerClient = sp<SurfaceComposerClient>::make(); } err = mSurfaceComposerClient->initCheck(); if (err != NO_ERROR) { diff --git a/include/android/configuration.h b/include/android/configuration.h index 46c7dfeceb..a291db0ecd 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -26,6 +26,7 @@ #ifndef ANDROID_CONFIGURATION_H #define ANDROID_CONFIGURATION_H +#include <stdint.h> #include <sys/cdefs.h> #include <android/asset_manager.h> diff --git a/include/android/thermal.h b/include/android/thermal.h index e5d46b5b8a..93ae961423 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -304,6 +304,26 @@ int AThermal_getThermalHeadroomThresholds(AThermalManager* _Nonnull manager, * Prototype of the function that is called when thermal headroom or thresholds changes. * It's passed the updated thermal headroom and thresholds as parameters, as well as the * pointer provided by the client that registered a callback. + * <p> + * This may not be used to fully replace the {@link AThermal_getThermalHeadroom} API as it will + * only notify on one of the conditions below that will significantly change one or both + * values of current headroom and headroom thresholds since previous callback: + * 1. thermal throttling events: when the skin temperature has cross any of the thresholds + * and there isn't a previous callback in a short time ago with similar values. + * 2. skin temperature threshold change events: note that if the absolute °C threshold + * values change in a way that does not significantly change the current headroom nor + * headroom thresholds, it will not trigger any callback. The client should not + * need to take action in such case since the difference from temperature vs threshold + * hasn't changed. + * <p> + * By API version 36, it provides a forecast in the same call for developer's convenience + * based on a {@code forecastSeconds} defined by the device, which can be static or dynamic + * varied by OEM. Be aware that it will not notify on forecast temperature change but the + * events mentioned above. So periodically polling against {@link AThermal_getThermalHeadroom} + * API should still be used to actively monitor temperature forecast in advance. + * <p> + * This serves as a more advanced option compared to thermal status listener, where the + * latter will only notify on thermal throttling events with status update. * * @param data The data pointer to be passed when callback is called. * @param headroom The current non-negative normalized headroom value, also see diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 0cd87201fb..279a4ae35c 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -454,4 +454,6 @@ private: InputVerifier mInputVerifier; }; +std::ostream& operator<<(std::ostream& out, const InputMessage& msg); + } // namespace android diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h index f4f4d5e05c..e22481fceb 100644 --- a/include/powermanager/PowerHalController.h +++ b/include/powermanager/PowerHalController.h @@ -73,6 +73,9 @@ public: int tgid, int uid) override; virtual HalResult<void> closeSessionChannel(int tgid, int uid) override; virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override; + virtual HalResult<void> sendCompositionData( + const std::vector<hal::CompositionData>& data) override; + virtual HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override; private: std::mutex mConnectedHalMutex; diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h index 42901821bc..17a4cd4eff 100644 --- a/include/powermanager/PowerHalWrapper.h +++ b/include/powermanager/PowerHalWrapper.h @@ -18,6 +18,8 @@ #include <aidl/android/hardware/power/Boost.h> #include <aidl/android/hardware/power/ChannelConfig.h> +#include <aidl/android/hardware/power/CompositionData.h> +#include <aidl/android/hardware/power/CompositionUpdate.h> #include <aidl/android/hardware/power/IPower.h> #include <aidl/android/hardware/power/IPowerHintSession.h> #include <aidl/android/hardware/power/Mode.h> @@ -37,6 +39,8 @@ namespace android { namespace power { +namespace hal = aidl::android::hardware::power; + // State of Power HAL support for individual apis. enum class HalSupport { UNKNOWN = 0, @@ -49,21 +53,20 @@ class HalWrapper { public: virtual ~HalWrapper() = default; - virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, - int32_t durationMs) = 0; - virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0; + virtual HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) = 0; + virtual HalResult<void> setMode(hal::Mode mode, bool enabled) = 0; virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) = 0; virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, - aidl::android::hardware::power::SessionTag tag, - aidl::android::hardware::power::SessionConfig* config) = 0; + hal::SessionTag tag, hal::SessionConfig* config) = 0; virtual HalResult<int64_t> getHintSessionPreferredRate() = 0; - virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, - int uid) = 0; + virtual HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) = 0; virtual HalResult<void> closeSessionChannel(int tgid, int uid) = 0; virtual HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() = 0; + virtual HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) = 0; + virtual HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) = 0; }; // Empty Power HAL wrapper that ignores all api calls. @@ -72,21 +75,20 @@ public: EmptyHalWrapper() = default; ~EmptyHalWrapper() override = default; - HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, - int32_t durationMs) override; - HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, - aidl::android::hardware::power::SessionTag tag, - aidl::android::hardware::power::SessionConfig* config) override; + hal::SessionTag tag, hal::SessionConfig* config) override; HalResult<int64_t> getHintSessionPreferredRate() override; - HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, - int uid) override; + HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) override; HalResult<void> closeSessionChannel(int tgid, int uid) override; HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override; + HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) override; + HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override; protected: virtual const char* getUnsupportedMessage(); @@ -99,9 +101,8 @@ public: : mHandleV1_0(std::move(handleV1_0)) {} ~HidlHalWrapperV1_0() override = default; - HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, - int32_t durationMs) override; - HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; protected: const sp<hardware::power::V1_0::IPower> mHandleV1_0; @@ -127,9 +128,8 @@ protected: // Wrapper for the HIDL Power HAL v1.2. class HidlHalWrapperV1_2 : public HidlHalWrapperV1_1 { public: - HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, - int32_t durationMs) override; - HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; explicit HidlHalWrapperV1_2(sp<hardware::power::V1_2::IPower> handleV1_2) : HidlHalWrapperV1_1(std::move(handleV1_2)) {} ~HidlHalWrapperV1_2() override = default; @@ -141,7 +141,7 @@ protected: // Wrapper for the HIDL Power HAL v1.3. class HidlHalWrapperV1_3 : public HidlHalWrapperV1_2 { public: - HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; explicit HidlHalWrapperV1_3(sp<hardware::power::V1_3::IPower> handleV1_3) : HidlHalWrapperV1_2(std::move(handleV1_3)) {} ~HidlHalWrapperV1_3() override = default; @@ -153,26 +153,24 @@ protected: // Wrapper for the AIDL Power HAL. class AidlHalWrapper : public EmptyHalWrapper { public: - explicit AidlHalWrapper(std::shared_ptr<aidl::android::hardware::power::IPower> handle) - : mHandle(std::move(handle)) {} + explicit AidlHalWrapper(std::shared_ptr<hal::IPower> handle) : mHandle(std::move(handle)) {} ~AidlHalWrapper() override = default; - HalResult<void> setBoost(aidl::android::hardware::power::Boost boost, - int32_t durationMs) override; - HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override; + HalResult<void> setBoost(hal::Boost boost, int32_t durationMs) override; + HalResult<void> setMode(hal::Mode mode, bool enabled) override; HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) override; HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig( int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos, - aidl::android::hardware::power::SessionTag tag, - aidl::android::hardware::power::SessionConfig* config) override; + hal::SessionTag tag, hal::SessionConfig* config) override; HalResult<int64_t> getHintSessionPreferredRate() override; - HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid, - int uid) override; + HalResult<hal::ChannelConfig> getSessionChannel(int tgid, int uid) override; HalResult<void> closeSessionChannel(int tgid, int uid) override; HalResult<aidl::android::hardware::power::SupportInfo> getSupportInfo() override; + HalResult<void> sendCompositionData(const std::vector<hal::CompositionData>& data) override; + HalResult<void> sendCompositionUpdate(const hal::CompositionUpdate& update) override; protected: const char* getUnsupportedMessage() override; @@ -181,16 +179,10 @@ private: // Control access to the boost and mode supported arrays. std::mutex mBoostMutex; std::mutex mModeMutex; - std::shared_ptr<aidl::android::hardware::power::IPower> mHandle; - std::array<HalSupport, - static_cast<int32_t>( - *(ndk::enum_range<aidl::android::hardware::power::Boost>().end() - 1)) + - 1> + std::shared_ptr<hal::IPower> mHandle; + std::array<HalSupport, static_cast<int32_t>(*(ndk::enum_range<hal::Boost>().end() - 1)) + 1> mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN}; - std::array<HalSupport, - static_cast<int32_t>( - *(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) + - 1> + std::array<HalSupport, static_cast<int32_t>(*(ndk::enum_range<hal::Mode>().end() - 1)) + 1> mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN}; }; diff --git a/include/powermanager/PowerHintSessionWrapper.h b/include/powermanager/PowerHintSessionWrapper.h index ba6fe77c80..0134e02f12 100644 --- a/include/powermanager/PowerHintSessionWrapper.h +++ b/include/powermanager/PowerHintSessionWrapper.h @@ -45,9 +45,11 @@ public: virtual HalResult<void> setMode(::aidl::android::hardware::power::SessionMode in_type, bool in_enabled); virtual HalResult<aidl::android::hardware::power::SessionConfig> getSessionConfig(); + std::optional<int> getSessionId(); private: std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mSession; + std::optional<int> mSessionId; int32_t mInterfaceVersion; }; diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index bc7ae37ff0..9883eb2672 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -445,6 +445,9 @@ status_t BBinder::linkToDeath( const sp<DeathRecipient>& /*recipient*/, void* /*cookie*/, uint32_t /*flags*/) { + // BBinder::linkToDeath is invalid because this process owns this binder. + // The DeathRecipient is called on BpBinders when the process owning the + // binder node dies. return INVALID_OPERATION; } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 777c22a63e..2c37624304 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -542,7 +542,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { return BAD_VALUE; } - if ((mDataSize+len) > mDataCapacity) { + if ((mDataPos + len) > mDataCapacity) { // grow data err = growData(len); if (err != NO_ERROR) { diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp index 6259d9d2d2..a71da3f384 100644 --- a/libs/binder/tests/binderParcelUnitTest.cpp +++ b/libs/binder/tests/binderParcelUnitTest.cpp @@ -197,6 +197,17 @@ TEST(Parcel, AppendPlainDataPartial) { ASSERT_EQ(2, p2.readInt32()); } +TEST(Parcel, AppendWithBadDataPos) { + Parcel p1; + p1.writeInt32(1); + p1.writeInt32(1); + Parcel p2; + p2.setDataCapacity(8); + p2.setDataPosition(10000); + + EXPECT_EQ(android::BAD_VALUE, p2.appendFrom(&p1, 0, 8)); +} + TEST(Parcel, HasBinders) { sp<IBinder> b1 = sp<BBinder>::make(); diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 170b2adfbb..7c9c452c01 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -382,7 +382,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( sockaddr_un addr_un{}; addr_un.sun_family = AF_UNIX; strcpy(addr_un.sun_path, serverConfig.addr.c_str()); - addr = *reinterpret_cast<sockaddr_storage*>(&addr_un); + std::memcpy(&addr, &addr_un, sizeof(sockaddr_un)); addrLen = sizeof(sockaddr_un); status = session->setupPreconnectedClient({}, [=]() { @@ -394,7 +394,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( sockaddr_un addr_un{}; addr_un.sun_family = AF_UNIX; strcpy(addr_un.sun_path, serverConfig.addr.c_str()); - addr = *reinterpret_cast<sockaddr_storage*>(&addr_un); + std::memcpy(&addr, &addr_un, sizeof(sockaddr_un)); addrLen = sizeof(sockaddr_un); status = session->setupUnixDomainClient(serverConfig.addr.c_str()); @@ -409,7 +409,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( .svm_port = static_cast<unsigned int>(serverInfo.port), .svm_cid = VMADDR_CID_LOCAL, }; - addr = *reinterpret_cast<sockaddr_storage*>(&addr_vm); + std::memcpy(&addr, &addr_vm, sizeof(sockaddr_vm)); addrLen = sizeof(sockaddr_vm); status = session->setupVsockClient(VMADDR_CID_LOCAL, serverInfo.port); @@ -420,7 +420,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( addr_in.sin_family = AF_INET; addr_in.sin_port = htons(serverInfo.port); inet_aton(ip_addr.c_str(), &addr_in.sin_addr); - addr = *reinterpret_cast<sockaddr_storage*>(&addr_in); + std::memcpy(&addr, &addr_in, sizeof(sockaddr_in)); addrLen = sizeof(sockaddr_in); status = session->setupInetClient(ip_addr.c_str(), serverInfo.port); diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h index 127676bf9a..1ac00ca13f 100644 --- a/libs/binder/trusty/include/binder/RpcServerTrusty.h +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -97,6 +97,15 @@ private: // By default we use the latest stable version. LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION)); + // The default behavior in trusty is to allow handles to be passed with tipc IPC. + // We add mode NONE so that servers do not reject connections from clients who do + // not change their default transport mode. + static const std::vector<RpcSession::FileDescriptorTransportMode> + TRUSTY_SERVER_SUPPORTED_FD_MODES = {RpcSession::FileDescriptorTransportMode::TRUSTY, + RpcSession::FileDescriptorTransportMode::NONE}; + + rpcServer->setSupportedFileDescriptorTransportModes(TRUSTY_SERVER_SUPPORTED_FD_MODES); + return rpcServer; } diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp index 12e347e4f3..451383a90a 100644 --- a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp +++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp @@ -27,13 +27,6 @@ using android::RpcTransportCtxFactoryTipcTrusty; using android::sp; using android::wp; -// The default behavior in trusty is to allow handles to be passed with tipc IPC. -// We add mode NONE so that servers do not reject connections from clients who do -// not change their default transport mode. -static const std::vector<RpcSession::FileDescriptorTransportMode> TRUSTY_SERVER_SUPPORTED_FD_MODES = - {RpcSession::FileDescriptorTransportMode::TRUSTY, - RpcSession::FileDescriptorTransportMode::NONE}; - struct ARpcServerTrusty { sp<RpcServer> mRpcServer; @@ -60,8 +53,6 @@ ARpcServerTrusty* ARpcServerTrusty_newPerSession(AIBinder* (*cb)(const void*, si return nullptr; } - rpcServer->setSupportedFileDescriptorTransportModes(TRUSTY_SERVER_SUPPORTED_FD_MODES); - rpcServer->setPerSessionRootObject( [cb, cbArgSp](wp<RpcSession> /*session*/, const void* addrPtr, size_t len) { auto* aib = (*cb)(addrPtr, len, cbArgSp.get()); diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 43ee33eb86..2d18d80678 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -50,7 +50,6 @@ static const char* native_processes_to_dump[] = { // Native processes to dump on debuggable builds. static const char* debuggable_native_processes_to_dump[] = { - "/system/bin/keystore2", "/system/bin/vold", NULL, }; diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index b5fa321fc2..158c54886d 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -295,7 +295,6 @@ filegroup { cc_defaults { name: "libgui-defaults", defaults: ["libgui_bufferqueue-defaults"], - srcs: [":libgui-sources"], static_libs: [ "libgui_aidl_static", "libgui_window_info_static", @@ -319,6 +318,10 @@ cc_library_shared { "libgui-defaults", ], + srcs: [ + ":libgui-sources", + ], + export_static_lib_headers: [ "libgui_aidl_static", "libgui_window_info_static", diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 63dcfbcb9b..83c6b57289 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -2230,17 +2230,47 @@ int Surface::detachNextBuffer(sp<GraphicBuffer>* outBuffer, return NO_ERROR; } +int Surface::isBufferOwned(const sp<GraphicBuffer>& buffer, bool* outIsOwned) const { + ATRACE_CALL(); + + if (buffer == nullptr) { + ALOGE("%s: Bad input, buffer was null", __FUNCTION__); + return BAD_VALUE; + } + if (outIsOwned == nullptr) { + ALOGE("%s: Bad input, output was null", __FUNCTION__); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + + int slot = this->getSlotFromBufferLocked(buffer->getNativeBuffer()); + if (slot == BAD_VALUE) { + ALOGV("%s: Buffer %" PRIu64 " is not owned", __FUNCTION__, buffer->getId()); + *outIsOwned = false; + return NO_ERROR; + } else if (slot < 0) { + ALOGV("%s: Buffer %" PRIu64 " look up failed (%d)", __FUNCTION__, buffer->getId(), slot); + *outIsOwned = false; + return slot; + } + + *outIsOwned = true; + return NO_ERROR; +} + int Surface::attachBuffer(ANativeWindowBuffer* buffer) { ATRACE_CALL(); - ALOGV("Surface::attachBuffer"); + sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer)); + + ALOGV("Surface::attachBuffer bufferId=%" PRIu64, graphicBuffer->getId()); Mutex::Autolock lock(mMutex); if (mReportRemovedBuffers) { mRemovedBuffers.clear(); } - sp<GraphicBuffer> graphicBuffer(static_cast<GraphicBuffer*>(buffer)); uint32_t priorGeneration = graphicBuffer->mGenerationNumber; graphicBuffer->mGenerationNumber = mGenerationNumber; int32_t attachedSlot = -1; diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 1eb9b87c3c..50877f8c56 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -194,7 +194,7 @@ const std::string& SurfaceControl::getName() const { return mName; } -std::shared_ptr<Choreographer> SurfaceControl::getChoreographer() { +sp<Choreographer> SurfaceControl::getChoreographer() { if (mChoreographer) { return mChoreographer; } @@ -203,7 +203,7 @@ std::shared_ptr<Choreographer> SurfaceControl::getChoreographer() { ALOGE("%s: No looper prepared for thread", __func__); return nullptr; } - mChoreographer = std::make_shared<Choreographer>(looper, getHandle()); + mChoreographer = sp<Choreographer>::make(looper, getHandle()); status_t result = mChoreographer->initialize(); if (result != OK) { ALOGE("Failed to initialize choreographer"); diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index 50abadb922..6a1e9f6f03 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -36,8 +36,6 @@ class BufferQueueProducer : public BnGraphicBufferProducer { public: friend class BufferQueue; // Needed to access binderDied - explicit BufferQueueProducer(const sp<BufferQueueCore>& core, - bool consumerIsSurfaceFlinger = false); ~BufferQueueProducer() override; // requestBuffer returns the GraphicBuffer for slot N. @@ -219,6 +217,9 @@ public: #endif protected: + explicit BufferQueueProducer(const sp<BufferQueueCore>& core, + bool consumerIsSurfaceFlinger = false); + friend class sp<BufferQueueProducer>; // see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the // total maximum buffer count for the buffer queue (dequeued AND acquired) status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount); diff --git a/libs/gui/include/gui/Choreographer.h b/libs/gui/include/gui/Choreographer.h index 5862967d4a..b436af1eb3 100644 --- a/libs/gui/include/gui/Choreographer.h +++ b/libs/gui/include/gui/Choreographer.h @@ -79,8 +79,6 @@ public: }; static Context gChoreographers; - explicit Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle = nullptr) - EXCLUDES(gChoreographers.lock); void postFrameCallbackDelayed(AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, AChoreographer_vsyncCallback vsyncCallback, void* data, @@ -113,6 +111,10 @@ public: private: Choreographer(const Choreographer&) = delete; + explicit Choreographer(const sp<Looper>& looper, const sp<IBinder>& layerHandle = nullptr) + EXCLUDES(gChoreographers.lock); + friend class sp<Choreographer>; + friend AChoreographer* AChoreographer_create(); void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, VsyncEventData vsyncEventData) override; diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index b06ad077f4..cdf216c945 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -23,12 +23,6 @@ using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; class DisplayEventDispatcher : public LooperCallback { public: - explicit DisplayEventDispatcher(const sp<Looper>& looper, - gui::ISurfaceComposer::VsyncSource vsyncSource = - gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, - EventRegistrationFlags eventRegistration = {}, - const sp<IBinder>& layerHandle = nullptr); - status_t initialize(); void dispose(); status_t scheduleVsync(); @@ -38,6 +32,14 @@ public: status_t getLatestVsyncEventData(ParcelableVsyncEventData* outVsyncEventData) const; protected: + explicit DisplayEventDispatcher(const sp<Looper>& looper, + gui::ISurfaceComposer::VsyncSource vsyncSource = + gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp, + EventRegistrationFlags eventRegistration = {}, + const sp<IBinder>& layerHandle = nullptr); + + friend class sp<DisplayEventDispatcher>; + virtual ~DisplayEventDispatcher() = default; private: diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 755674d9e6..3cfbed11bf 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -442,6 +442,9 @@ public: status_t detachBuffer(const sp<GraphicBuffer>& buffer); #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS) + // Sets outIsOwned to true if the given buffer is currently known to be owned by this Surface. + status_t isBufferOwned(const sp<GraphicBuffer>& buffer, bool* outIsOwned) const; + // Batch version of dequeueBuffer, cancelBuffer and queueBuffer // Note that these batched operations are not supported when shared buffer mode is being used. struct BatchBuffer { diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index 344b957ba7..91a422d155 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -26,6 +26,7 @@ #include <android/gui/ISurfaceComposerClient.h> +#include <gui/Choreographer.h> #include <ui/FrameStats.h> #include <ui/PixelFormat.h> #include <ui/Region.h> @@ -36,7 +37,6 @@ namespace android { // --------------------------------------------------------------------------- -class Choreographer; class IGraphicBufferProducer; class Surface; class SurfaceComposerClient; @@ -82,7 +82,7 @@ public: const std::string& getName() const; // TODO(b/267195698): Consider renaming. - std::shared_ptr<Choreographer> getChoreographer(); + sp<Choreographer> getChoreographer(); sp<IGraphicBufferProducer> getIGraphicBufferProducer(); @@ -134,7 +134,7 @@ private: PixelFormat mFormat = PIXEL_FORMAT_NONE; uint32_t mCreateFlags = 0; uint64_t mFallbackFrameNumber = 100; - std::shared_ptr<Choreographer> mChoreographer; + sp<Choreographer> mChoreographer; }; }; // namespace android diff --git a/libs/gui/include/gui/WindowInfosListenerReporter.h b/libs/gui/include/gui/WindowInfosListenerReporter.h index 684e21ad96..f9a3acee41 100644 --- a/libs/gui/include/gui/WindowInfosListenerReporter.h +++ b/libs/gui/include/gui/WindowInfosListenerReporter.h @@ -40,6 +40,9 @@ public: void reconnect(const sp<gui::ISurfaceComposer>&); private: + WindowInfosListenerReporter() = default; + friend class sp<WindowInfosListenerReporter>; + std::mutex mListenersMutex; std::unordered_set<sp<gui::WindowInfosListener>, gui::SpHash<gui::WindowInfosListener>> mWindowInfosListeners GUARDED_BY(mListenersMutex); diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index e20345dd1a..bd53031e79 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -51,11 +51,6 @@ cc_test { "-Werror", "-Wextra", "-Wthread-safety", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_CONSUMER_BASE_OWNS_BQ=true", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_PLATFORM_API_IMPROVEMENTS=true", - "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_WB_UNLIMITED_SLOTS=true", ], srcs: [ @@ -100,6 +95,7 @@ cc_test { "android.hardware.configstore-utils", "libSurfaceFlingerProp", "libGLESv1_CM", + "libgui", "libgui_test_server_aidl-cpp", "libinput", "libnativedisplay", diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index b861c6d4b7..4e4c8a2b63 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -201,7 +201,7 @@ public: protected: void SetUp() { mComposer = ComposerService::getComposerService(); - mClient = new SurfaceComposerClient(); + mClient = sp<SurfaceComposerClient>::make(); const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); ASSERT_FALSE(ids.empty()); // display 0 is picked as this test is not much display depedent diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index cfbb2e7386..e22f57e43e 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -1452,10 +1452,6 @@ TEST_F(BufferQueueTest, TestProducerConnectDisconnect) { ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); } -TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) { - ASSERT_EQ(flags::bq_setframerate(), COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)); -} - struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer { BufferItemConsumerSetFrameRateListener() : BufferItemConsumer(GRALLOC_USAGE_SW_READ_OFTEN, 1) {} @@ -1561,9 +1557,14 @@ TEST_F(BufferQueueTest, TestAdditionalOptions) { {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3}, }}; - ASSERT_EQ(NO_INIT, - native_window_set_buffers_additional_options(surface.get(), extras.data(), - extras.size())); + auto status = native_window_set_buffers_additional_options(surface.get(), extras.data(), + extras.size()); + if (flags::bq_extendedallocate()) { + ASSERT_EQ(NO_INIT, status); + } else { + ASSERT_EQ(INVALID_OPERATION, status); + GTEST_SKIP() << "Flag bq_extendedallocate not enabled"; + } if (!IsCuttlefish()) { GTEST_SKIP() << "Not cuttlefish"; diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp index bffb3f0430..62d73ca752 100644 --- a/libs/gui/tests/DisplayedContentSampling_test.cpp +++ b/libs/gui/tests/DisplayedContentSampling_test.cpp @@ -30,7 +30,7 @@ static constexpr uint32_t INVALID_MASK = 0x10; class DisplayedContentSamplingTest : public ::testing::Test { protected: void SetUp() { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(OK, mComposerClient->initCheck()); const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); ASSERT_FALSE(ids.empty()); diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index adf8fce472..5a5067b2b9 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -398,7 +398,7 @@ public: InputSurfacesTest() { ProcessState::self()->startThreadPool(); } void SetUp() { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); ASSERT_FALSE(ids.empty()); diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp index 40af8e845a..407c18ed0d 100644 --- a/libs/gui/tests/GLTest.cpp +++ b/libs/gui/tests/GLTest.cpp @@ -56,7 +56,7 @@ void GLTest::SetUp() { } if (mDisplaySecs > 0) { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); mSurfaceControl = mComposerClient->createSurface( diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp index c35efe28a3..d80d223f49 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -180,7 +180,7 @@ protected: } void SetUp() override { - mSurfaceComposerClient = new SurfaceComposerClient; + mSurfaceComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck()); mBackgroundLayer = diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp index 8fea689c91..a2fe8fd2ec 100644 --- a/libs/gui/tests/SamplingDemo.cpp +++ b/libs/gui/tests/SamplingDemo.cpp @@ -36,7 +36,7 @@ namespace android { class Button : public gui::BnRegionSamplingListener { public: Button(const char* name, const Rect& samplingArea) { - sp<SurfaceComposerClient> client = new SurfaceComposerClient; + sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make(); mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceEffect); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index c4dcba856a..4fee11c2cb 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -134,7 +134,7 @@ protected: } virtual void SetUp() { - mComposerClient = new SurfaceComposerClient; + mComposerClient = sp<SurfaceComposerClient>::make(); ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); // TODO(brianderson): The following sometimes fails and is a source of @@ -2690,4 +2690,57 @@ TEST_F(SurfaceTest, UnlimitedSlots_BatchOperations) { EXPECT_EQ(128u, outputs.size()); } #endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_UNLIMITED_SLOTS) + +TEST_F(SurfaceTest, isBufferOwned) { + const int TEST_USAGE_FLAGS = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; + auto [bufferItemConsumer, surface] = BufferItemConsumer::create(TEST_USAGE_FLAGS); + + sp<SurfaceListener> listener = sp<StubSurfaceListener>::make(); + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, listener)); + + sp<GraphicBuffer> surfaceAttachableBuffer = + sp<GraphicBuffer>::make(10, 10, PIXEL_FORMAT_RGBA_8888, 1, TEST_USAGE_FLAGS); + + // + // Attaching a buffer makes it owned. + // + + bool isOwned; + EXPECT_EQ(OK, surface->isBufferOwned(surfaceAttachableBuffer, &isOwned)); + EXPECT_FALSE(isOwned); + + EXPECT_EQ(OK, surface->attachBuffer(surfaceAttachableBuffer.get())); + EXPECT_EQ(OK, surface->isBufferOwned(surfaceAttachableBuffer, &isOwned)); + EXPECT_TRUE(isOwned); + + // + // A dequeued buffer is always owned. + // + + sp<GraphicBuffer> buffer; + sp<Fence> fence; + EXPECT_EQ(OK, surface->dequeueBuffer(&buffer, &fence)); + EXPECT_EQ(OK, surface->isBufferOwned(buffer, &isOwned)); + EXPECT_TRUE(isOwned); + + // + // A detached buffer is no longer owned. + // + + EXPECT_EQ(OK, surface->detachBuffer(buffer)); + EXPECT_EQ(OK, surface->isBufferOwned(buffer, &isOwned)); + EXPECT_FALSE(isOwned); + + // + // It's not currently possible to verify whether or not a consumer has attached a buffer until + // it shows up on the Surface. + // + + sp<GraphicBuffer> consumerAttachableBuffer = + sp<GraphicBuffer>::make(10, 10, PIXEL_FORMAT_RGBA_8888, 1, TEST_USAGE_FLAGS); + + ASSERT_EQ(OK, bufferItemConsumer->attachBuffer(consumerAttachableBuffer)); + EXPECT_EQ(OK, surface->isBufferOwned(consumerAttachableBuffer, &isOwned)); + EXPECT_FALSE(isOwned); +} } // namespace android diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index 6087461f6c..9578639e2b 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -171,11 +171,6 @@ InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTim return msg; } -std::ostream& operator<<(std::ostream& out, const InputMessage& msg) { - out << ftl::enum_string(msg.header.type); - return out; -} - } // namespace // --- InputConsumerNoResampling --- diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d388d48e8d..cb6f7f0707 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -436,16 +436,29 @@ android::base::Result<InputMessage> InputChannel::receiveMessage() { if (error == EAGAIN || error == EWOULDBLOCK) { return android::base::Error(WOULD_BLOCK); } - if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) { - return android::base::Error(DEAD_OBJECT); + if (error == EPIPE) { + return android::base::ResultError("Got EPIPE", DEAD_OBJECT); + } + if (error == ENOTCONN) { + return android::base::ResultError("Got ENOTCONN", DEAD_OBJECT); + } + if (error == ECONNREFUSED) { + return android::base::ResultError("Got ECONNREFUSED", DEAD_OBJECT); + } + if (error == ECONNRESET) { + // This means that the client has closed the channel while there was + // still some data in the buffer. In most cases, subsequent reads + // would result in more data. However, that is not guaranteed, so we + // should not return WOULD_BLOCK here to try again. + return android::base::ResultError("Got ECONNRESET", DEAD_OBJECT); } return android::base::Error(-error); } if (nRead == 0) { // check for EOF - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ receive message failed because peer was closed", name.c_str()); - return android::base::Error(DEAD_OBJECT); + LOG_IF(INFO, DEBUG_CHANNEL_MESSAGES) + << "channel '" << name << "' ~ receive message failed because peer was closed"; + return android::base::ResultError("::recv returned 0", DEAD_OBJECT); } if (!msg.isValid(nRead)) { @@ -766,4 +779,9 @@ android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveC return android::base::Error(UNKNOWN_ERROR); } +std::ostream& operator<<(std::ostream& out, const InputMessage& msg) { + out << ftl::enum_string(msg.header.type); + return out; +} + } // namespace android diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 0e3d399e5a..a2de0093d1 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -113,6 +113,16 @@ flag { } flag { + name: "allow_transfer_of_entire_gesture" + namespace: "input" + description: "When calling 'transferTouchGesture', the entire gesture (including new POINTER_DOWN events from the same device) will be automatically transferred to the destination window" + bug: "397979572" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_keyboard_classifier" namespace: "input" description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic" diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 25356cfcf0..9b582d99dc 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -20,6 +20,7 @@ #include <time.h> #include <errno.h> +#include <android-base/logging.h> #include <binder/Binder.h> #include <binder/Parcel.h> #include <gtest/gtest.h> @@ -43,6 +44,39 @@ bool operator==(const InputChannel& left, const InputChannel& right) { return left.getName() == right.getName() && left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino; } + +/** + * Read a message from the provided channel. Read will continue until there's data, so only call + * this if there's data in the channel, or it's closed. If there's no data, this will loop forever. + */ +android::base::Result<InputMessage> readMessage(InputChannel& channel) { + while (true) { + // Keep reading until we get something other than 'WOULD_BLOCK' + android::base::Result<InputMessage> result = channel.receiveMessage(); + if (!result.ok() && result.error().code() == WOULD_BLOCK) { + // The data is not available yet. + continue; // try again + } + return result; + } +} + +InputMessage createFinishedMessage(uint32_t seq) { + InputMessage finish{}; + finish.header.type = InputMessage::Type::FINISHED; + finish.header.seq = seq; + finish.body.finished.handled = true; + return finish; +} + +InputMessage createKeyMessage(uint32_t seq) { + InputMessage key{}; + key.header.type = InputMessage::Type::KEY; + key.header.seq = seq; + key.body.key.action = AKEY_EVENT_ACTION_DOWN; + return key; +} + } // namespace class InputChannelTest : public testing::Test { @@ -227,6 +261,120 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { } } +/** + * In this test, server writes 3 key events to the client. The client, upon receiving the first key, + * sends a "finished" signal back to server, and then closes the fd. + * + * Next, we check what the server receives. + * + * In most cases, the server will receive the finish event, and then an 'fd closed' event. + * + * However, sometimes, the 'finish' event will not be delivered to the server. This is communicated + * to the server via 'ECONNRESET', which the InputChannel converts into DEAD_OBJECT. + * + * The server needs to be aware of this behaviour and correctly clean up any state associated with + * the client, even if the client did not end up finishing some of the messages. + * + * This test is written to expose a behaviour on the linux side - occasionally, the + * last events written to the fd by the consumer are not delivered to the server. + * + * When tested on 2025 hardware, ECONNRESET was received approximately 1 out of 40 tries. + * In vast majority (~ 29999 / 30000) of cases, after receiving ECONNRESET, the server could still + * read the client data after receiving ECONNRESET. + */ +TEST_F(InputChannelTest, ReceiveAfterCloseMultiThreaded) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; + status_t result = + InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + // Sender / publisher: publish 3 keys + InputMessage key1 = createKeyMessage(/*seq=*/1); + serverChannel->sendMessage(&key1); + // The client should close the fd after it reads this one, but we will send 2 more here. + InputMessage key2 = createKeyMessage(/*seq=*/2); + serverChannel->sendMessage(&key2); + InputMessage key3 = createKeyMessage(/*seq=*/3); + serverChannel->sendMessage(&key3); + + std::thread consumer = std::thread([clientChannel = std::move(clientChannel)]() mutable { + // Read the first key + android::base::Result<InputMessage> firstKey = readMessage(*clientChannel); + if (!firstKey.ok()) { + FAIL() << "Did not receive the first key"; + } + + // Send finish + const InputMessage finish = createFinishedMessage(firstKey->header.seq); + clientChannel->sendMessage(&finish); + // Now close the fd + clientChannel.reset(); + }); + + // Now try to read the finish message, even though client closed the fd + android::base::Result<InputMessage> response = readMessage(*serverChannel); + consumer.join(); + if (response.ok()) { + ASSERT_EQ(response->header.type, InputMessage::Type::FINISHED); + } else { + // It's possible that after the client closes the fd, server will receive ECONNRESET. + // In those situations, this error code will be translated into DEAD_OBJECT by the + // InputChannel. + ASSERT_EQ(response.error().code(), DEAD_OBJECT); + // In most cases, subsequent attempts to read the client channel at this + // point would succeed. However, for simplicity, we exit here (since + // it's not guaranteed). + return; + } + + // There should not be any more events from the client, since the client closed fd after the + // first key. + android::base::Result<InputMessage> noEvent = serverChannel->receiveMessage(); + ASSERT_FALSE(noEvent.ok()) << "Got event " << *noEvent; +} + +/** + * Similar test as above, but single-threaded. + */ +TEST_F(InputChannelTest, ReceiveAfterCloseSingleThreaded) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; + status_t result = + InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + // Sender / publisher: publish 3 keys + InputMessage key1 = createKeyMessage(/*seq=*/1); + serverChannel->sendMessage(&key1); + // The client should close the fd after it reads this one, but we will send 2 more here. + InputMessage key2 = createKeyMessage(/*seq=*/2); + serverChannel->sendMessage(&key2); + InputMessage key3 = createKeyMessage(/*seq=*/3); + serverChannel->sendMessage(&key3); + + // Read the first key + android::base::Result<InputMessage> firstKey = readMessage(*clientChannel); + if (!firstKey.ok()) { + FAIL() << "Did not receive the first key"; + } + + // Send finish + const InputMessage finish = createFinishedMessage(firstKey->header.seq); + clientChannel->sendMessage(&finish); + // Now close the fd + clientChannel.reset(); + + // Now try to read the finish message, even though client closed the fd + android::base::Result<InputMessage> response = readMessage(*serverChannel); + ASSERT_FALSE(response.ok()); + ASSERT_EQ(response.error().code(), DEAD_OBJECT); + + // We can still read the finish event (but in practice, the expectation is that the server will + // not be doing this after getting DEAD_OBJECT). + android::base::Result<InputMessage> finishEvent = serverChannel->receiveMessage(); + ASSERT_TRUE(finishEvent.ok()); + ASSERT_EQ(finishEvent->header.type, InputMessage::Type::FINISHED); +} + TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) { std::unique_ptr<InputChannel> serverChannel, clientChannel; diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index 1e3449c004..201d5e9ff5 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -39,12 +39,29 @@ struct DetailedTimingDescriptor { ui::Size physicalSizeInMm; }; +// These values must match the ones in ScreenPartStatus.aidl file in the composer HAL +enum class ScreenPartStatus : uint8_t { + /** + * Device cannot differentiate an original screen from a replaced screen. + */ + UNSUPPORTED = 0, + /** + * Device has the original screen it was manufactured with. + */ + ORIGINAL = 1, + /** + * Device has a replaced screen. + */ + REPLACED = 2, +}; + struct DisplayIdentificationInfo { PhysicalDisplayId id; std::string name; uint8_t port; std::optional<DeviceProductInfo> deviceProductInfo; std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor; + ScreenPartStatus screenPartStatus; }; struct ExtensionBlock { diff --git a/opengl/tests/lib/WindowSurface.cpp b/opengl/tests/lib/WindowSurface.cpp index e94b565f11..beac9001bc 100644 --- a/opengl/tests/lib/WindowSurface.cpp +++ b/opengl/tests/lib/WindowSurface.cpp @@ -29,7 +29,7 @@ using namespace android; WindowSurface::WindowSurface() { status_t err; - sp<SurfaceComposerClient> surfaceComposerClient = new SurfaceComposerClient; + sp<SurfaceComposerClient> surfaceComposerClient = sp<SurfaceComposerClient>::make(); err = surfaceComposerClient->initCheck(); if (err != NO_ERROR) { fprintf(stderr, "SurfaceComposerClient::initCheck error: %#x\n", err); diff --git a/services/automotive/display/AutomotiveDisplayProxyService.cpp b/services/automotive/display/AutomotiveDisplayProxyService.cpp index afa623352b..56c3b7d31f 100644 --- a/services/automotive/display/AutomotiveDisplayProxyService.cpp +++ b/services/automotive/display/AutomotiveDisplayProxyService.cpp @@ -65,7 +65,7 @@ AutomotiveDisplayProxyService::getIGraphicBufferProducer(uint64_t id) { std::swap(displayWidth, displayHeight); } - sp<android::SurfaceComposerClient> surfaceClient = new SurfaceComposerClient(); + sp<android::SurfaceComposerClient> surfaceClient = sp<SurfaceComposerClient>::make(); err = surfaceClient->initCheck(); if (err != NO_ERROR) { ALOGE("SurfaceComposerClient::initCheck error: %#x", err); diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index becfb05f67..9cd76c7d11 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -125,10 +125,17 @@ struct KeyEntry : EventEntry { bool syntheticRepeat; // set to true for synthetic key repeats enum class InterceptKeyResult { + // The interception result is unknown. UNKNOWN, + // The event should be skipped and not sent to the application. SKIP, + // The event should be sent to the application. CONTINUE, + // The event should eventually be sent to the application, after a delay. TRY_AGAIN_LATER, + // The event should not be initially sent to the application, but instead go through + // post-processing to generate a fallback key event and then sent to the application. + FALLBACK, }; // These are special fields that may need to be modified while the event is being dispatched. mutable InterceptKeyResult interceptKeyResult; // set based on the interception result diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 95e1c06615..2908c61182 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -952,7 +952,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, new LatencyAggregatorWithHistograms())) : std::move(std::unique_ptr<InputEventTimelineProcessor>( new LatencyAggregator()))), - mLatencyTracker(*mInputEventTimelineProcessor) { + mLatencyTracker(*mInputEventTimelineProcessor, mInputDevices) { mReporter = createInputReporter(); mWindowInfoListener = sp<DispatcherWindowListener>::make(*this); @@ -1961,11 +1961,74 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con } } + if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::FALLBACK) { + findAndDispatchFallbackEvent(currentTime, entry, inputTargets); + // Drop the key. + return true; + } + // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; } +void InputDispatcher::findAndDispatchFallbackEvent(nsecs_t currentTime, + std::shared_ptr<const KeyEntry> entry, + std::vector<InputTarget>& inputTargets) { + // Find the fallback associated with the incoming key event and dispatch it. + KeyEvent event = createKeyEvent(*entry); + const int32_t originalKeyCode = entry->keyCode; + + // Fetch the fallback event. + KeyCharacterMap::FallbackAction fallback; + for (const InputDeviceInfo& deviceInfo : mInputDevices) { + if (deviceInfo.getId() == entry->deviceId) { + const KeyCharacterMap* map = deviceInfo.getKeyCharacterMap(); + + LOG_ALWAYS_FATAL_IF(map == nullptr, "No KeyCharacterMap for device %d", + entry->deviceId); + map->getFallbackAction(entry->keyCode, entry->metaState, &fallback); + break; + } + } + + if (fallback.keyCode == AKEYCODE_UNKNOWN) { + // No fallback detected. + return; + } + + std::unique_ptr<KeyEntry> fallbackKeyEntry = + std::make_unique<KeyEntry>(mIdGenerator.nextId(), entry->injectionState, + event.getEventTime(), event.getDeviceId(), event.getSource(), + event.getDisplayId(), entry->policyFlags, entry->action, + event.getFlags() | AKEY_EVENT_FLAG_FALLBACK, + fallback.keyCode, event.getScanCode(), /*metaState=*/0, + event.getRepeatCount(), event.getDownTime()); + + if (mTracer) { + fallbackKeyEntry->traceTracker = + mTracer->traceDerivedEvent(*fallbackKeyEntry, *entry->traceTracker); + } + + for (const InputTarget& inputTarget : inputTargets) { + std::shared_ptr<Connection> connection = inputTarget.connection; + if (!connection->responsive || (connection->status != Connection::Status::NORMAL)) { + return; + } + + connection->inputState.setFallbackKey(originalKeyCode, fallback.keyCode); + if (entry->action == AKEY_EVENT_ACTION_UP) { + connection->inputState.removeFallbackKey(originalKeyCode); + } + + if (mTracer) { + mTracer->dispatchToTargetHint(*fallbackKeyEntry->traceTracker, inputTarget); + } + enqueueDispatchEntryAndStartDispatchCycleLocked(currentTime, connection, + std::move(fallbackKeyEntry), inputTarget); + } +} + void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) { LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS) << prefix << "eventTime=" << entry.eventTime << ", deviceId=" << entry.deviceId @@ -2447,6 +2510,24 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( return injectionError(InputEventInjectionResult::TARGET_MISMATCH); } + if (newTouchedWindowHandle != nullptr && + maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // Check if this should be redirected to another window, in case this window previously + // called 'transferTouch' for this gesture. + const auto it = + std::find_if(tempTouchState.windows.begin(), tempTouchState.windows.end(), + [&](const TouchedWindow& touchedWindow) { + return touchedWindow.forwardingWindowToken == + newTouchedWindowHandle->getToken() && + touchedWindow.hasTouchingPointers(entry.deviceId); + }); + if (it != tempTouchState.windows.end()) { + LOG(INFO) << "Forwarding pointer from " << newTouchedWindowHandle->getName() + << " to " << it->windowHandle->getName(); + newTouchedWindowHandle = it->windowHandle; + } + } + std::vector<sp<WindowInfoHandle>> newTouchedWindows = findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, mWindowInfos); if (newTouchedWindowHandle != nullptr) { @@ -2487,7 +2568,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( isDownOrPointerDown ? std::make_optional( entry.eventTime) - : std::nullopt); + : std::nullopt, + /*forwardingWindowToken=*/nullptr); if (!addResult.ok()) { LOG(ERROR) << "Error while processing " << entry << " for " << windowHandle->getName(); @@ -2514,7 +2596,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( tempTouchState.addOrUpdateWindow(wallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, entry.deviceId, {pointer}, - entry.eventTime); + entry.eventTime, + /*forwardingWindowToken=*/nullptr); } } } @@ -2613,7 +2696,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, InputTarget::DispatchMode::SLIPPERY_ENTER, targetFlags, entry.deviceId, {pointer}, - entry.eventTime); + entry.eventTime, + /*forwardingWindowToken=*/nullptr); // Check if the wallpaper window should deliver the corresponding event. slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, @@ -4346,7 +4430,7 @@ void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedA std::scoped_lock _l(mLock); // Reset key repeating in case a keyboard device was added or removed or something. resetKeyRepeatLocked(); - mLatencyTracker.setInputDevices(args.inputDeviceInfos); + mInputDevices = args.inputDeviceInfos; } void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { @@ -5770,7 +5854,7 @@ void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { } bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop) { + bool isDragDrop, bool transferEntireGesture) { if (fromToken == toToken) { LOG_IF(INFO, DEBUG_FOCUS) << "Trivial transfer to same window."; return true; @@ -5784,7 +5868,7 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s "transferring touch from this window to another window", traceContext.getTracker()); - auto result = mTouchStates.transferTouchGesture(fromToken, toToken); + auto result = mTouchStates.transferTouchGesture(fromToken, toToken, transferEntireGesture); if (!result.has_value()) { return false; } @@ -5828,7 +5912,8 @@ std::optional<std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<Pointe std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>, std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>> InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IBinder>& fromToken, - const sp<android::IBinder>& toToken) { + const sp<android::IBinder>& toToken, + bool transferEntireGesture) { // Find the target touch state and touched window by fromToken. auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(fromToken); if (!touchStateWindowAndDisplay.has_value()) { @@ -5871,8 +5956,12 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB } // Transferring touch focus using this API should not effect the focused window. newTargetFlags |= InputTarget::Flags::NO_FOCUS_CHANGE; + sp<IBinder> forwardingWindowToken; + if (transferEntireGesture && com::android::input::flags::allow_transfer_of_entire_gesture()) { + forwardingWindowToken = fromToken; + } state.addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags, - deviceId, pointers, downTimeInTarget); + deviceId, pointers, downTimeInTarget, forwardingWindowToken); // Synthesize cancel for old window and down for new window. std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken); @@ -5954,7 +6043,8 @@ bool InputDispatcher::transferTouchOnDisplay(const sp<IBinder>& destChannelToken fromToken = from->getToken(); } // release lock - return transferTouchGesture(fromToken, destChannelToken); + return transferTouchGesture(fromToken, destChannelToken, /*isDragDrop=*/false, + /*transferEntireGesture=*/false); } void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { @@ -7132,7 +7222,8 @@ void InputDispatcher::DispatcherTouchState::slipWallpaperTouch( state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER, InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED, - deviceId, pointers, entry.eventTime); + deviceId, pointers, entry.eventTime, + /*forwardingWindowToken=*/nullptr); } } @@ -7173,7 +7264,8 @@ InputDispatcher::DispatcherTouchState::transferWallpaperTouch( wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, - deviceId, pointers, downTimeInTarget); + deviceId, pointers, downTimeInTarget, + /*forwardingWindowToken=*/nullptr); std::shared_ptr<Connection> wallpaperConnection = mConnectionManager.getConnection(newWallpaper->getToken()); if (wallpaperConnection != nullptr) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 38f782573a..2e8f2ce04e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -127,7 +127,7 @@ public: void setMaximumObscuringOpacityForTouch(float opacity) override; bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop = false) override; + bool isDragDrop, bool transferEntireGesture) override; bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken, ui::LogicalDisplayId displayId) override; @@ -440,7 +440,8 @@ private: std::optional< std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<PointerProperties>, std::list<CancellationArgs>, std::list<PointerDownArgs>>> - transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); + transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool transferEntireGesture); base::Result<std::list<CancellationArgs>, status_t> pilferPointers( const sp<IBinder>& token, const Connection& requestingConnection); @@ -918,10 +919,14 @@ private: std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable( const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) REQUIRES(mLock); + void findAndDispatchFallbackEvent(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, + std::vector<InputTarget>& inputTargets) REQUIRES(mLock); // Statistics gathering. nsecs_t mLastStatisticPushTime = 0; std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock); + // Must outlive `mLatencyTracker`. + std::vector<InputDeviceInfo> mInputDevices; LatencyTracker mLatencyTracker GUARDED_BY(mLock); void traceInboundQueueLengthLocked() REQUIRES(mLock); void traceOutboundQueueLength(const Connection& connection); diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index 0921e37d03..7c23694fbd 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -67,8 +67,9 @@ static void eraseByValue(std::multimap<K, V>& map, const V& value) { } // namespace -LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor) - : mTimelineProcessor(&processor) {} +LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor, + std::vector<InputDeviceInfo>& inputDevices) + : mTimelineProcessor(&processor), mInputDevices(inputDevices) {} void LatencyTracker::trackListener(const NotifyArgs& args) { if (const NotifyKeyArgs* keyArgs = std::get_if<NotifyKeyArgs>(&args)) { @@ -248,8 +249,4 @@ std::string LatencyTracker::dump(const char* prefix) const { StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size()); } -void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) { - mInputDevices = inputDevices; -} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 79ea14c4fb..7e2cc79249 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -20,9 +20,11 @@ #include <map> #include <unordered_map> +#include <vector> #include <binder/IBinder.h> #include <input/Input.h> +#include <input/InputDevice.h> #include "InputEventTimeline.h" #include "NotifyArgs.h" @@ -41,8 +43,10 @@ public: /** * Create a LatencyTracker. * param reportingFunction: the function that will be called in order to report full latency. + * param inputDevices: input devices relevant for tracking. */ - LatencyTracker(InputEventTimelineProcessor& processor); + LatencyTracker(InputEventTimelineProcessor& processor, + std::vector<InputDeviceInfo>& inputDevices); /** * Start keeping track of an event identified by the args. This must be called first. * If duplicate events are encountered (events that have the same eventId), none of them will be @@ -60,7 +64,6 @@ public: std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); std::string dump(const char* prefix) const; - void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices); private: /** @@ -81,7 +84,7 @@ private: std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes; InputEventTimelineProcessor* mTimelineProcessor; - std::vector<InputDeviceInfo> mInputDevices; + std::vector<InputDeviceInfo>& mInputDevices; void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction, diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 2bf63beb05..f1fca0c317 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -74,7 +74,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow( const sp<WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers, - std::optional<nsecs_t> firstDownTimeInTarget) { + std::optional<nsecs_t> firstDownTimeInTarget, sp<IBinder> forwardingWindowToken) { if (touchingPointers.empty()) { LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName(); return android::base::Error(); @@ -88,6 +88,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow( if (touchedWindow.windowHandle == windowHandle) { touchedWindow.dispatchMode = dispatchMode; touchedWindow.targetFlags |= targetFlags; + touchedWindow.forwardingWindowToken = forwardingWindowToken; // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have // downTime set initially. Need to update existing window when a pointer is down for the // window. @@ -103,6 +104,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow( touchedWindow.windowHandle = windowHandle; touchedWindow.dispatchMode = dispatchMode; touchedWindow.targetFlags = targetFlags; + touchedWindow.forwardingWindowToken = forwardingWindowToken; touchedWindow.addTouchingPointers(deviceId, touchingPointers); if (firstDownTimeInTarget) { touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget); diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 451d91704c..20155f4396 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -47,7 +47,7 @@ struct TouchState { const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers, - std::optional<nsecs_t> firstDownTimeInTarget); + std::optional<nsecs_t> firstDownTimeInTarget, sp<IBinder> forwardingWindowToken); void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId, const PointerProperties& pointer, float x, float y); diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp index fa5be1a719..053a2e2441 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.cpp +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -331,9 +331,9 @@ std::string TouchedWindow::dump() const { std::string out; std::string deviceStates = dumpMap(mDeviceStates, constToString, TouchedWindow::deviceStateToString); - out += StringPrintf("name='%s', targetFlags=%s, mDeviceStates=%s\n", + out += StringPrintf("name='%s', targetFlags=%s, forwardingWindowToken=%p, mDeviceStates=%s\n", windowHandle->getName().c_str(), targetFlags.string().c_str(), - deviceStates.c_str()); + forwardingWindowToken.get(), deviceStates.c_str()); return out; } diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index c38681eef0..d27c597803 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -34,6 +34,11 @@ struct TouchedWindow { InputTarget::DispatchMode dispatchMode = InputTarget::DispatchMode::AS_IS; ftl::Flags<InputTarget::Flags> targetFlags; + // If another window has transferred touches to this window, and wants to continue sending the + // rest of the gesture to this window, store the token of the originating (transferred-from) + // window here. + sp<IBinder> forwardingWindowToken; + // Hovering bool hasHoveringPointers() const; bool hasHoveringPointers(DeviceId deviceId) const; @@ -76,7 +81,7 @@ struct TouchedWindow { }; std::vector<DeviceId> eraseHoveringPointersIf( - std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition); + std::function<bool(const PointerProperties&, float x, float y)> condition); private: struct DeviceState { diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index ab039c34ef..b22ddca4e6 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -148,7 +148,7 @@ public: * Returns true on success. False if the window did not actually have an active touch gesture. */ virtual bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop) = 0; + bool isDragDrop, bool transferEntireGesture) = 0; /** * Transfer a touch gesture to the provided channel, no matter where the current touch is. diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 608bec4a0c..c8432005a4 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -443,6 +443,9 @@ public: /* Get the Bluetooth address of an input device, if known. */ virtual std::optional<std::string> getBluetoothAddress(int32_t deviceId) const = 0; + /* Gets the sysfs root path for this device. Returns an empty path if there is none. */ + virtual std::filesystem::path getSysfsRootPath(int32_t deviceId) const = 0; + /* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */ virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0; diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 2fcb5d831f..559bc0aa7a 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -246,7 +246,7 @@ static nsecs_t processEventTimestamp(const struct input_event& event) { /** * Returns the sysfs root path of the input device. */ -static std::optional<std::filesystem::path> getSysfsRootPath(const char* devicePath) { +static std::optional<std::filesystem::path> getSysfsRootForEvdevDevicePath(const char* devicePath) { std::error_code errorCode; // Stat the device path to get the major and minor number of the character file @@ -1619,7 +1619,7 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { std::shared_ptr<const EventHub::AssociatedDevice> EventHub::obtainAssociatedDeviceLocked( const std::filesystem::path& devicePath, const std::shared_ptr<PropertyMap>& config) const { const std::optional<std::filesystem::path> sysfsRootPathOpt = - getSysfsRootPath(devicePath.c_str()); + getSysfsRootForEvdevDevicePath(devicePath.c_str()); if (!sysfsRootPathOpt) { return nullptr; } @@ -1897,58 +1897,89 @@ std::vector<RawEvent> EventHub::getEvents(int timeoutMillis) { break; // return to the caller before we actually rescan } - // Report any devices that had last been added/removed. - 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()); - const int32_t deviceId = (device->id == mBuiltInKeyboardId) - ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID - : device->id; - events.push_back({ - .when = now, - .deviceId = deviceId, - .type = DEVICE_REMOVED, - }); - it = mClosingDevices.erase(it); - if (events.size() == EVENT_BUFFER_SIZE) { - break; + handleSysfsNodeChangeNotificationsLocked(); + + // Use a do-while loop to ensure that we drain the closing and opening devices loop + // at least once, even if there are no devices to re-open. + do { + if (!mDeviceIdsToReopen.empty()) { + // If there are devices that need to be re-opened, ensure that we re-open them + // one at a time to send the DEVICE_REMOVED and DEVICE_ADDED notifications for + // each before moving on to the next. This is to avoid notifying all device + // removals and additions in one batch, which could cause additional unnecessary + // device added/removed notifications for merged InputDevices from InputReader. + const int32_t deviceId = mDeviceIdsToReopen.back(); + mDeviceIdsToReopen.erase(mDeviceIdsToReopen.end() - 1); + if (auto it = mDevices.find(deviceId); it != mDevices.end()) { + ALOGI("Reopening input device: id=%d, name=%s", it->second->id, + it->second->identifier.name.c_str()); + const auto path = it->second->path; + closeDeviceLocked(*it->second); + openDeviceLocked(path); + } } - } - if (mNeedToScanDevices) { - mNeedToScanDevices = false; - scanDevicesLocked(); - } - - 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()); - const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; - events.push_back({ - .when = now, - .deviceId = deviceId, - .type = DEVICE_ADDED, - }); - - // 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 (tryAddVideoDeviceLocked(*device, videoDevice)) { - // videoDevice was transferred to 'device' - it = mUnattachedVideoDevices.erase(it); + // Report any devices that had last been added/removed. + 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()); + const int32_t deviceId = (device->id == mBuiltInKeyboardId) + ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID + : device->id; + events.push_back({ + .when = now, + .deviceId = deviceId, + .type = DEVICE_REMOVED, + }); + it = mClosingDevices.erase(it); + if (events.size() == EVENT_BUFFER_SIZE) { break; } } - auto [dev_it, inserted] = mDevices.insert_or_assign(device->id, std::move(device)); - if (!inserted) { - ALOGW("Device id %d exists, replaced.", device->id); + if (mNeedToScanDevices) { + mNeedToScanDevices = false; + scanDevicesLocked(); } - if (events.size() == EVENT_BUFFER_SIZE) { - break; + + 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()); + const int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + events.push_back({ + .when = now, + .deviceId = deviceId, + .type = DEVICE_ADDED, + }); + + // 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 (tryAddVideoDeviceLocked(*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); + } + if (events.size() == EVENT_BUFFER_SIZE) { + break; + } } + + // Perform this loop of re-opening devices so that we re-open one device at a time. + } while (!mDeviceIdsToReopen.empty()); + + if (events.size() == EVENT_BUFFER_SIZE) { + break; } // Grab the next input event. @@ -2664,17 +2695,44 @@ status_t EventHub::disableDevice(int32_t deviceId) { return device->disable(); } +std::filesystem::path EventHub::getSysfsRootPath(int32_t deviceId) const { + std::scoped_lock _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device == nullptr) { + ALOGE("Invalid device id=%" PRId32 " provided to %s", deviceId, __func__); + return {}; + } + + return device->associatedDevice ? device->associatedDevice->sysfsRootPath + : std::filesystem::path{}; +} + // TODO(b/274755573): Shift to uevent handling on native side and remove this method // Currently using Java UEventObserver to trigger this which uses UEvent infrastructure that uses a // NETLINK socket to observe UEvents. We can create similar infrastructure on Eventhub side to // directly observe UEvents instead of triggering from Java side. void EventHub::sysfsNodeChanged(const std::string& sysfsNodePath) { - std::scoped_lock _l(mLock); + mChangedSysfsNodeNotifications.emplace(sysfsNodePath); +} + +void EventHub::handleSysfsNodeChangeNotificationsLocked() { + // Use a set to de-dup any repeated notifications. + std::set<std::string> changedNodes; + while (true) { + auto node = mChangedSysfsNodeNotifications.popWithTimeout(std::chrono::nanoseconds(0)); + if (!node.has_value()) break; + changedNodes.emplace(*node); + } + if (changedNodes.empty()) { + return; + } // Testing whether a sysfs node changed involves several syscalls, so use a cache to avoid // testing the same node multiple times. + // TODO(b/281822656): Notify InputReader separately when an AssociatedDevice changes, + // instead of needing to re-open all of Devices that are associated with it. std::map<std::shared_ptr<const AssociatedDevice>, bool /*changed*/> testedDevices; - auto isAssociatedDeviceChanged = [&testedDevices, &sysfsNodePath](const Device& dev) { + auto shouldReopenDevice = [&testedDevices, &changedNodes](const Device& dev) { if (!dev.associatedDevice) { return false; } @@ -2683,45 +2741,45 @@ void EventHub::sysfsNodeChanged(const std::string& sysfsNodePath) { return testedIt->second; } // Cache miss - if (sysfsNodePath.find(dev.associatedDevice->sysfsRootPath.string()) == std::string::npos) { + const bool anyNodesChanged = + std::any_of(changedNodes.begin(), changedNodes.end(), [&](const std::string& node) { + return node.find(dev.associatedDevice->sysfsRootPath.string()) != + std::string::npos; + }); + if (!anyNodesChanged) { testedDevices.emplace(dev.associatedDevice, false); return false; } auto reloadedDevice = AssociatedDevice(dev.associatedDevice->sysfsRootPath, dev.associatedDevice->baseDevConfig); const bool changed = *dev.associatedDevice != reloadedDevice; + if (changed) { + ALOGI("sysfsNodeChanged: Identified change in sysfs nodes for device: %s", + dev.identifier.name.c_str()); + } testedDevices.emplace(dev.associatedDevice, changed); return changed; }; - std::set<Device*> devicesToClose; - std::set<std::string /*path*/> devicesToOpen; - - // Check in opening devices. If its associated device changed, - // the device should be removed from mOpeningDevices and needs to be opened again. - std::erase_if(mOpeningDevices, [&](const auto& dev) { - if (isAssociatedDeviceChanged(*dev)) { - devicesToOpen.emplace(dev->path); - return true; + // Check in opening devices. These can be re-opened directly because we have not yet notified + // the Reader about these devices. + for (const auto& dev : mOpeningDevices) { + if (shouldReopenDevice(*dev)) { + ALOGI("Reopening input device from mOpeningDevices: id=%d, name=%s", dev->id, + dev->identifier.name.c_str()); + const auto path = dev->path; + closeDeviceLocked(*dev); // The Device object is deleted by this function. + openDeviceLocked(path); } - return false; - }); + } - // Check in already added device. If its associated device changed, - // the device needs to be re-opened. + // Check in already added devices. Add them to the re-opening list so they can be + // re-opened serially. for (const auto& [id, dev] : mDevices) { - if (isAssociatedDeviceChanged(*dev)) { - devicesToOpen.emplace(dev->path); - devicesToClose.emplace(dev.get()); + if (shouldReopenDevice(*dev)) { + mDeviceIdsToReopen.emplace_back(dev->id); } } - - for (auto* device : devicesToClose) { - closeDeviceLocked(*device); - } - for (const auto& path : devicesToOpen) { - openDeviceLocked(path); - } } void EventHub::createVirtualKeyboardLocked() { @@ -2817,9 +2875,23 @@ void EventHub::closeDeviceLocked(Device& device) { releaseControllerNumberLocked(device.controllerNumber); device.controllerNumber = 0; device.close(); - mClosingDevices.push_back(std::move(mDevices[device.id])); - mDevices.erase(device.id); + // Try to remove this device from mDevices. + if (auto it = mDevices.find(device.id); it != mDevices.end()) { + mClosingDevices.push_back(std::move(mDevices[device.id])); + mDevices.erase(device.id); + return; + } + + // Try to remove this device from mOpeningDevices. + if (auto it = std::find_if(mOpeningDevices.begin(), mOpeningDevices.end(), + [&device](auto& d) { return d->id == device.id; }); + it != mOpeningDevices.end()) { + mOpeningDevices.erase(it); + return; + } + + LOG_ALWAYS_FATAL("%s: Device with id %d was not found!", __func__, device.id); } base::Result<void> EventHub::readNotifyLocked() { diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index 5e42d57f06..594dcba144 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -136,6 +136,8 @@ void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) { } else { dump += "<none>\n"; } + dump += StringPrintf(INDENT2 "SysfsRootPath: %s\n", + mSysfsRootPath.empty() ? "<none>" : mSysfsRootPath.c_str()); dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); dump += StringPrintf(INDENT2 "Sources: %s\n", inputEventSourceToString(deviceInfo.getSources()).c_str()); @@ -195,6 +197,10 @@ void InputDevice::addEmptyEventHubDevice(int32_t eventHubId) { DevicePair& devicePair = mDevices[eventHubId]; devicePair.second = createMappers(*devicePair.first, readerConfig); + if (mSysfsRootPath.empty()) { + mSysfsRootPath = devicePair.first->getSysfsRootPath(); + } + // Must change generation to flag this device as changed bumpGeneration(); return out; diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 58df692b3e..74ef972848 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -917,8 +917,19 @@ bool InputReader::canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId di return *associatedDisplayId == displayId; } +std::filesystem::path InputReader::getSysfsRootPath(int32_t deviceId) const { + std::scoped_lock _l(mLock); + + const InputDevice* device = findInputDeviceLocked(deviceId); + if (!device) { + return {}; + } + return device->getSysfsRootPath(); +} + void InputReader::sysfsNodeChanged(const std::string& sysfsNodePath) { mEventHub->sysfsNodeChanged(sysfsNodePath); + mEventHub->wake(); } DeviceId InputReader::getLastUsedInputDeviceId() { diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index adbfdebfb0..9f3a57c265 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -31,6 +31,7 @@ #include <batteryservice/BatteryService.h> #include <ftl/flags.h> +#include <input/BlockingQueue.h> #include <input/Input.h> #include <input/InputDevice.h> #include <input/KeyCharacterMap.h> @@ -398,6 +399,9 @@ public: /* Disable an input device. Closes file descriptor to that device. */ virtual status_t disableDevice(int32_t deviceId) = 0; + /* Gets the sysfs root path for this device. Returns an empty path if there is none. */ + virtual std::filesystem::path getSysfsRootPath(int32_t deviceId) const = 0; + /* Sysfs node changed. Reopen the Eventhub device if any new Peripheral like Light, Battery, * etc. is detected. */ virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0; @@ -613,6 +617,8 @@ public: status_t disableDevice(int32_t deviceId) override final; + std::filesystem::path getSysfsRootPath(int32_t deviceId) const override final; + void sysfsNodeChanged(const std::string& sysfsNodePath) override final; bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override final; @@ -775,6 +781,8 @@ private: void addDeviceInputInotify(); void addDeviceInotify(); + void handleSysfsNodeChangeNotificationsLocked() REQUIRES(mLock); + // Protect all internal state. mutable std::mutex mLock; @@ -807,6 +815,7 @@ private: bool mNeedToReopenDevices; bool mNeedToScanDevices; std::vector<std::string> mExcludedDevices; + std::vector<int32_t> mDeviceIdsToReopen; int mEpollFd; int mINotifyFd; @@ -824,6 +833,10 @@ private: size_t mPendingEventCount; size_t mPendingEventIndex; bool mPendingINotify; + + // The sysfs node change notifications that have been sent to EventHub. + // Enqueuing notifications does not require the lock to be held. + BlockingQueue<std::string> mChangedSysfsNodeNotifications; }; } // namespace android diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 4744dd0e4e..a1a8891940 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -81,6 +81,8 @@ public: inline virtual KeyboardType getKeyboardType() const { return mKeyboardType; } + inline std::filesystem::path getSysfsRootPath() const { return mSysfsRootPath; } + bool isEnabled(); void dump(std::string& dump, const std::string& eventHubDevStr); @@ -209,6 +211,7 @@ private: bool mHasMic; bool mDropUntilNextSync; std::optional<bool> mShouldSmoothScroll; + std::filesystem::path mSysfsRootPath; typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); @@ -471,6 +474,9 @@ public: inline void setKeyboardType(KeyboardType keyboardType) { return mDevice.setKeyboardType(keyboardType); } + inline std::filesystem::path getSysfsRootPath() const { + return mEventHub->getSysfsRootPath(mId); + } inline bool setKernelWakeEnabled(bool enabled) { return mEventHub->setKernelWakeEnabled(mId, enabled); } diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 6a259373df..9212d37966 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -118,6 +118,8 @@ public: std::optional<std::string> getBluetoothAddress(int32_t deviceId) const override; + std::filesystem::path getSysfsRootPath(int32_t deviceId) const override; + void sysfsNodeChanged(const std::string& sysfsNodePath) override; DeviceId getLastUsedInputDeviceId() override; diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index c982dab019..63eb357bdb 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -144,28 +144,39 @@ public: // records it if so. void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) { std::scoped_lock lock(mLock); + Counters& counters = mCounters[id]; switch (gesture.type) { case kGestureTypeFling: if (gesture.details.fling.fling_state == GESTURES_FLING_START) { // Indicates the end of a two-finger scroll gesture. - mCounters[id].twoFingerSwipeGestures++; + counters.twoFingerSwipeGestures++; } break; case kGestureTypeSwipeLift: - mCounters[id].threeFingerSwipeGestures++; + // The Gestures library occasionally outputs two lift gestures in a row, which can + // cause inaccurate metrics reporting. To work around this, deduplicate successive + // lift gestures. + // TODO(b/404529050): fix the Gestures library, and remove this check. + if (counters.lastGestureType != kGestureTypeSwipeLift) { + counters.threeFingerSwipeGestures++; + } break; case kGestureTypeFourFingerSwipeLift: - mCounters[id].fourFingerSwipeGestures++; + // TODO(b/404529050): fix the Gestures library, and remove this check. + if (counters.lastGestureType != kGestureTypeFourFingerSwipeLift) { + counters.fourFingerSwipeGestures++; + } break; case kGestureTypePinch: if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) { - mCounters[id].pinchGestures++; + counters.pinchGestures++; } break; default: // We're not interested in any other gestures. break; } + counters.lastGestureType = gesture.type; } private: @@ -214,6 +225,10 @@ private: int32_t threeFingerSwipeGestures = 0; int32_t fourFingerSwipeGestures = 0; int32_t pinchGestures = 0; + + // Records the last type of gesture received for this device, for deduplication purposes. + // TODO(b/404529050): fix the Gestures library and remove this field. + GestureType lastGestureType = kGestureTypeContactInitiated; }; // Metrics are aggregated by device model and version, so if two devices of the same model and diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp index e72c440480..8987b99525 100644 --- a/services/inputflinger/tests/FakeEventHub.cpp +++ b/services/inputflinger/tests/FakeEventHub.cpp @@ -627,6 +627,14 @@ void FakeEventHub::setSysfsRootPath(int32_t deviceId, std::string sysfsRootPath) device->sysfsRootPath = sysfsRootPath; } +std::filesystem::path FakeEventHub::getSysfsRootPath(int32_t deviceId) const { + Device* device = getDevice(deviceId); + if (device == nullptr) { + return {}; + } + return device->sysfsRootPath; +} + void FakeEventHub::sysfsNodeChanged(const std::string& sysfsNodePath) { int32_t foundDeviceId = -1; Device* foundDevice = nullptr; diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h index 143b93b245..1cd33c1c98 100644 --- a/services/inputflinger/tests/FakeEventHub.h +++ b/services/inputflinger/tests/FakeEventHub.h @@ -222,6 +222,7 @@ private: std::optional<int32_t> getLightBrightness(int32_t deviceId, int32_t lightId) const override; std::optional<std::unordered_map<LightColor, int32_t>> getLightIntensities( int32_t deviceId, int32_t lightId) const override; + std::filesystem::path getSysfsRootPath(int32_t deviceId) const override; void sysfsNodeChanged(const std::string& sysfsNodePath) override; void dump(std::string&) const override {} void monitor() const override {} diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 835b677e1e..298ba4209a 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -37,6 +37,7 @@ #include <input/BlockingQueue.h> #include <input/Input.h> #include <input/InputConsumer.h> +#include <input/KeyCharacterMap.h> #include <input/PrintTools.h> #include <linux/input.h> #include <sys/epoll.h> @@ -139,6 +140,30 @@ static KeyEvent getTestKeyEvent() { return event; } +InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId, DeviceId deviceId) { + InputDeviceIdentifier identifier; + identifier.vendor = vendorId; + identifier.product = productId; + auto info = InputDeviceInfo(); + info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device", + /*isExternal=*/false, /*hasMic=*/false, ui::LogicalDisplayId::INVALID); + return info; +} + +std::unique_ptr<KeyCharacterMap> loadKeyCharacterMap(const char* name) { + InputDeviceIdentifier identifier; + identifier.name = name; + std::string path = getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(), + InputDeviceConfigurationFileType:: + KEY_CHARACTER_MAP); + + if (path.empty()) { + return nullptr; + } + + return *KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE); +} + } // namespace // --- InputDispatcherTest --- @@ -1151,7 +1176,9 @@ TEST_F(InputDispatcherTest, MultiDeviceTouchTransferWithWallpaperWindows) { // Transfer touch from the middle window to the right window. ASSERT_TRUE(mDispatcher->transferTouchGesture(middleForegroundWindow->getToken(), - rightForegroundWindow->getToken())); + rightForegroundWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false)); middleForegroundWindow->consumeMotionEvent( AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB))); @@ -1183,6 +1210,148 @@ TEST_F(InputDispatcherTest, MultiDeviceTouchTransferWithWallpaperWindows) { } /** + * If a window has requested touch to be transferred, only the current pointers should be + * transferred. + * + * Subsequent pointers should still go through the normal hit testing. + * + * In this test, we are invoking 'transferTouchGesture' with the parameter 'transferEntireGesture' + * set to true, but that value doesn't make any difference, since the flag is disabled. This test + * will be removed once that flag is fully rolled out. + */ +TEST_F(InputDispatcherTest, TouchTransferDoesNotSendEntireGesture_legacy) { + SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, false); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> topWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Top window", + ui::LogicalDisplayId::DEFAULT); + + sp<FakeWindowHandle> bottomWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window", + ui::LogicalDisplayId::DEFAULT); + + mDispatcher->onWindowInfosChanged( + {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Transfer touch from the top window to the bottom window. + // The actual value of parameter 'transferEntireGesture' doesn't matter, since the flag is off. + ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/true)); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // When the second pointer goes down, it will hit the top window, and should be delivered there + // as a new pointer. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + const std::map<int32_t, PointF> expectedPointers{{0, PointF{50, 50}}}; + bottomWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers))); + bottomWindow->assertNoEvents(); +} + +/** + * If a window has requested touch to be transferred, the current pointers should be transferred. + * + * If the window did not request the "entire gesture" to be transferred, subsequent pointers should + * still go through the normal hit testing. + */ +TEST_F(InputDispatcherTest, TouchTransferDoesNotSendEntireGesture) { + SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, true); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> topWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Top window", + ui::LogicalDisplayId::DEFAULT); + + sp<FakeWindowHandle> bottomWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window", + ui::LogicalDisplayId::DEFAULT); + + mDispatcher->onWindowInfosChanged( + {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Transfer touch from the top window to the bottom window. + ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false)); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // When the second pointer goes down, it will hit the top window, and should be delivered there + // because "entire gesture" was not requested to be transferred when the original call to + // 'transferTouchGesture' was made. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + const std::map<int32_t, PointF> expectedPointers{{0, PointF{50, 50}}}; + bottomWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers))); + bottomWindow->assertNoEvents(); +} + +/** + * If a window has requested touch to be transferred, all subsequent pointers from the same gesture + * should be transferred if 'transferEntireGesture' was set to 'true'. + * + * In this test, there are 2 windows - one above and one below. + * First pointer goes to the top window. Then top window calls 'transferTouch' upon receiving + * ACTION_DOWN and transfers touch to the bottom window. + * Subsequent pointers from the same gesture should still be forwarded to the bottom window, + * as long as they first land into the top window. + */ +TEST_F(InputDispatcherTest, TouchTransferSendsEntireGesture) { + SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, true); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> topWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Top window", + ui::LogicalDisplayId::DEFAULT); + + sp<FakeWindowHandle> bottomWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window", + ui::LogicalDisplayId::DEFAULT); + + mDispatcher->onWindowInfosChanged( + {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Transfer touch from the top window to the bottom window. + ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/true)); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // When the second pointer goes down, it will hit the top window, but since the top window has + // requested the whole gesture to be transferred, it should be redirected to the bottom window. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60)) + .build()); + + bottomWindow->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN)); +} + +/** * A single window that receives touch (on top), and a wallpaper window underneath it. * The top window gets a multitouch gesture. * Ensure that wallpaper gets the same gesture. @@ -6583,7 +6752,8 @@ TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeDownWithCorrectCoordinate // The pointer is transferred to the second window, and the second window receives it in the // correct coordinate space. - mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken()); + mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken(), + /*isDragDrop=*/false, /*transferEntireGesture=*/false); firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400))); secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(-100, -400))); } @@ -7119,7 +7289,8 @@ INSTANTIATE_TEST_SUITE_P( [&](const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder> from, sp<IBinder> to) { return dispatcher->transferTouchGesture(from, to, - /*isDragAndDrop=*/false); + /*isDragAndDrop=*/false, + /*transferEntireGesture=*/false); })); TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { @@ -7159,7 +7330,8 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { secondWindow->consumeMotionDown(); // Transfer touch to the second window - mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken()); + mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken(), + /*isDragDrop=*/false, /*transferEntireGesture=*/false); // The first window gets cancel and the new gets pointer down (it already saw down) firstWindow->consumeMotionCancel(); secondWindow->consumeMotionPointerDown(1, ui::LogicalDisplayId::DEFAULT, @@ -7293,7 +7465,9 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { // Transfer touch ASSERT_TRUE(mDispatcher->transferTouchGesture(firstWindowInPrimary->getToken(), - secondWindowInPrimary->getToken())); + secondWindowInPrimary->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false)); // The first window gets cancel. firstWindowInPrimary->consumeMotionCancel(); secondWindowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, @@ -7480,6 +7654,50 @@ TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserAct mFakePolicy->assertUserActivityPoked(); } +TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyFallbackKey) { +#if !defined(__ANDROID__) + GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device."; +#endif + InputDeviceInfo testDevice = generateTestDeviceInfo(/*vendorId=*/0, + /*productId=*/0, /*deviceId=*/1); + std::unique_ptr<KeyCharacterMap> kcm = loadKeyCharacterMap("Generic"); + ASSERT_NE(nullptr, kcm); + + testDevice.setKeyCharacterMap(std::move(kcm)); + mDispatcher->notifyInputDevicesChanged(NotifyInputDevicesChangedArgs(/*id=*/1, {testDevice})); + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", + ui::LogicalDisplayId::DEFAULT); + + window->setFocusable(true); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + + window->consumeFocusEvent(true); + + mFakePolicy->setInterceptKeyBeforeDispatchingResult( + inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK); + + // In the Generic KCM fallbacks, Meta + Space => SEARCH. + mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .keyCode(AKEYCODE_SPACE) + .metaState(AMETA_META_ON) + .build()); + mDispatcher->waitForIdle(); + + // Should have poked user activity + mFakePolicy->assertUserActivityPoked(); + + // Fallback is generated and sent instead. + std::unique_ptr<KeyEvent> consumedEvent = window->consumeKey(/*handled=*/false); + ASSERT_NE(nullptr, consumedEvent); + ASSERT_THAT(*consumedEvent, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_SEARCH), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); +} + class DisableUserActivityInputDispatcherTest : public InputDispatcherTest, public ::testing::WithParamInterface<bool> {}; @@ -12541,7 +12759,8 @@ protected: // Transfer touch focus to the drag window bool transferred = mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(), - /*isDragDrop=*/true); + /*isDragDrop=*/true, + /*transferEntireGesture=*/false); if (transferred) { targetWindow->consumeMotionCancel(dragStartDisplay); mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); @@ -15113,7 +15332,9 @@ TEST_P(TransferOrDontTransferFixture, TouchDownAndMouseHover) { if (GetParam()) { // Call transferTouchGesture const bool transferred = - mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken()); + mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false); ASSERT_TRUE(transferred); mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); @@ -15195,7 +15416,9 @@ TEST_P(TransferOrDontTransferFixture, MouseAndTouchTransferSimultaneousMultiDevi if (GetParam()) { // Call transferTouchGesture const bool transferred = - mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken()); + mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false); ASSERT_TRUE(transferred); mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 43d2378f61..d1d8192395 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -611,8 +611,10 @@ protected: } void addDevice(int32_t eventHubId, const std::string& name, - ftl::Flags<InputDeviceClass> classes, const PropertyMap* configuration) { + ftl::Flags<InputDeviceClass> classes, const PropertyMap* configuration, + std::string sysfsRootPath = "") { mFakeEventHub->addDevice(eventHubId, name, classes); + mFakeEventHub->setSysfsRootPath(eventHubId, sysfsRootPath); if (configuration) { mFakeEventHub->addConfigurationMap(eventHubId, configuration); @@ -664,6 +666,18 @@ TEST_F(InputReaderTest, PolicyGetInputDevices) { ASSERT_EQ(0U, inputDevices[0].getMotionRanges().size()); } +TEST_F(InputReaderTest, GetSysfsRootPath) { + constexpr std::string SYSFS_ROOT = "xyz"; + ASSERT_NO_FATAL_FAILURE( + addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr, SYSFS_ROOT)); + + // Should also have received a notification describing the new input device. + ASSERT_EQ(1U, mFakePolicy->getInputDevices().size()); + InputDeviceInfo inputDevice = mFakePolicy->getInputDevices()[0]; + + ASSERT_EQ(SYSFS_ROOT, mReader->getSysfsRootPath(inputDevice.getId()).string()); +} + TEST_F(InputReaderTest, InputDeviceRecreatedOnSysfsNodeChanged) { ASSERT_NO_FATAL_FAILURE(addDevice(1, "keyboard", InputDeviceClass::KEYBOARD, nullptr)); mFakeEventHub->setSysfsRootPath(1, "xyz"); diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index d4e4bb00f7..8eded2e279 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -181,6 +181,7 @@ public: MOCK_METHOD(bool, isDeviceEnabled, (int32_t deviceId), (const, override)); MOCK_METHOD(status_t, enableDevice, (int32_t deviceId), (override)); MOCK_METHOD(status_t, disableDevice, (int32_t deviceId), (override)); + MOCK_METHOD(std::filesystem::path, getSysfsRootPath, (int32_t deviceId), (const, override)); MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override)); MOCK_METHOD(bool, setKernelWakeEnabled, (int32_t deviceId, bool enabled), (override)); }; diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index d8c5eac3c4..b7ca24bb61 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -19,10 +19,13 @@ #include "NotifyArgsBuilders.h" #include "android/input.h" +#include <vector> + #include <android-base/logging.h> #include <android-base/properties.h> #include <binder/Binder.h> #include <gtest/gtest.h> +#include <input/InputDevice.h> #include <input/PrintTools.h> #include <inttypes.h> #include <linux/input.h> @@ -51,11 +54,6 @@ static InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t produc return info; } -void setDefaultInputDeviceInfo(LatencyTracker& tracker) { - InputDeviceInfo deviceInfo = generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID); - tracker.setInputDevices({deviceInfo}); -} - const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200); const auto FIRST_MOUSE_POINTER = PointerBuilder(/*id=*/1, ToolType::MOUSE); @@ -120,13 +118,14 @@ protected: std::unique_ptr<LatencyTracker> mTracker; sp<IBinder> connection1; sp<IBinder> connection2; + std::vector<InputDeviceInfo> inputDevices; void SetUp() override { connection1 = sp<BBinder>::make(); connection2 = sp<BBinder>::make(); - mTracker = std::make_unique<LatencyTracker>(*this); - setDefaultInputDeviceInfo(*mTracker); + inputDevices.push_back(generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID)); + mTracker = std::make_unique<LatencyTracker>(*this, inputDevices); } void TearDown() override {} @@ -140,6 +139,10 @@ protected: */ void assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines); + void updateInputDevices(const std::vector<InputDeviceInfo>& inputDevicesUpdated) { + inputDevices = inputDevicesUpdated; + } + private: void processTimeline(const InputEventTimeline& timeline) override { mReceivedTimelines.push_back(timeline); @@ -448,7 +451,7 @@ TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline deviceInfo2.addSource(AINPUT_SOURCE_TOUCHSCREEN); deviceInfo2.addSource(AINPUT_SOURCE_STYLUS); - mTracker->setInputDevices({deviceInfo1, deviceInfo2}); + updateInputDevices({deviceInfo1, deviceInfo2}); mTracker->trackListener( MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, inputEventId) diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp index 6be922dfdb..0c8ba50776 100644 --- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp @@ -165,6 +165,10 @@ public: return reader->getBluetoothAddress(deviceId); } + std::filesystem::path getSysfsRootPath(int32_t deviceId) const { + return reader->getSysfsRootPath(deviceId); + } + void sysfsNodeChanged(const std::string& sysfsNodePath) { reader->sysfsNodeChanged(sysfsNodePath); } @@ -297,6 +301,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { std::chrono::microseconds(fdp->ConsumeIntegral<size_t>())); }, [&]() -> void { reader->getBluetoothAddress(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { reader->getSysfsRootPath(fdp->ConsumeIntegral<int32_t>()); }, })(); } diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp index 157a3338da..9c027fa00c 100644 --- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -17,6 +17,8 @@ #include <fuzzer/FuzzedDataProvider.h> #include <linux/input.h> +#include <vector> + #include "../../InputDeviceMetricsSource.h" #include "../InputEventTimeline.h" #include "NotifyArgsBuilders.h" @@ -58,7 +60,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { FuzzedDataProvider fdp(data, size); EmptyProcessor emptyProcessor; - LatencyTracker tracker(emptyProcessor); + std::vector<InputDeviceInfo> emptyDevices; + LatencyTracker tracker(emptyProcessor, emptyDevices); // Make some pre-defined tokens to ensure that some timelines are complete. std::array<sp<IBinder> /*token*/, 10> predefinedTokens; diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 9a5903981b..f619c48f3f 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -296,6 +296,7 @@ public: bool isDeviceEnabled(int32_t deviceId) const override { return mFdp->ConsumeBool(); } status_t enableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); } status_t disableDevice(int32_t deviceId) override { return mFdp->ConsumeIntegral<status_t>(); } + std::filesystem::path getSysfsRootPath(int32_t deviceId) const override { return {}; } void sysfsNodeChanged(const std::string& sysfsNodePath) override {} bool setKernelWakeEnabled(int32_t deviceId, bool enabled) override { return mFdp->ConsumeBool(); diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp index 4f65e77462..78f8f7bd96 100644 --- a/services/powermanager/Android.bp +++ b/services/powermanager/Android.bp @@ -52,6 +52,7 @@ cc_library_shared { ], whole_static_libs: [ + "android.adpf.sessionmanager_aidl-ndk", "android.os.hintmanager_aidl-ndk", ], diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp index 0ba1909a44..a817a7bf2e 100644 --- a/services/powermanager/PowerHalController.cpp +++ b/services/powermanager/PowerHalController.cpp @@ -173,6 +173,21 @@ HalResult<aidl::android::hardware::power::SupportInfo> PowerHalController::getSu return CACHE_SUPPORT(6, processHalResult(handle->getSupportInfo(), "getSupportInfo")); } +HalResult<void> PowerHalController::sendCompositionData( + const std::vector<hal::CompositionData>& data) { + std::shared_ptr<HalWrapper> handle = initHal(); + return CACHE_SUPPORT(6, + processHalResult(handle->sendCompositionData(data), + "sendCompositionData")); +} + +HalResult<void> PowerHalController::sendCompositionUpdate(const hal::CompositionUpdate& update) { + std::shared_ptr<HalWrapper> handle = initHal(); + return CACHE_SUPPORT(6, + processHalResult(handle->sendCompositionUpdate(update), + "sendCompositionUpdate")); +} + } // namespace power } // namespace android diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp index 068c23f94a..9c83bf5af3 100644 --- a/services/powermanager/PowerHalWrapper.cpp +++ b/services/powermanager/PowerHalWrapper.cpp @@ -79,6 +79,16 @@ HalResult<Aidl::SupportInfo> EmptyHalWrapper::getSupportInfo() { return HalResult<Aidl::SupportInfo>::unsupported(); } +HalResult<void> EmptyHalWrapper::sendCompositionData(const std::vector<hal::CompositionData>&) { + ALOGV("Skipped sendCompositionData because %s", getUnsupportedMessage()); + return HalResult<void>::unsupported(); +} + +HalResult<void> EmptyHalWrapper::sendCompositionUpdate(const hal::CompositionUpdate&) { + ALOGV("Skipped sendCompositionUpdate because %s", getUnsupportedMessage()); + return HalResult<void>::unsupported(); +} + const char* EmptyHalWrapper::getUnsupportedMessage() { return "Power HAL is not supported"; } @@ -292,6 +302,14 @@ HalResult<Aidl::SupportInfo> AidlHalWrapper::getSupportInfo() { return HalResult<Aidl::SupportInfo>::fromStatus(result, std::move(support)); } +HalResult<void> AidlHalWrapper::sendCompositionData(const std::vector<hal::CompositionData>& data) { + return HalResult<void>::fromStatus(mHandle->sendCompositionData(data)); +} + +HalResult<void> AidlHalWrapper::sendCompositionUpdate(const hal::CompositionUpdate& update) { + return HalResult<void>::fromStatus(mHandle->sendCompositionUpdate(update)); +} + const char* AidlHalWrapper::getUnsupportedMessage() { return "Power HAL doesn't support it"; } diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index 682d1f44dc..1c53496644 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -26,9 +26,9 @@ #include <utils/Log.h> #include <unistd.h> +#include <memory> #include <thread> - using android::binder::Status; using namespace aidl::android::hardware::power; @@ -347,3 +347,50 @@ TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionWithConfigUnsupported) { result = mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out); ASSERT_TRUE(result.isUnsupported()); } + +TEST_F(PowerHalWrapperAidlTest, TestSendingCompositionData) { + int32_t tgid = 999; + int32_t uid = 1001; + std::vector<hal::CompositionData> dataOut; + dataOut.emplace_back(hal::CompositionData{ + .timestampNanos = 0L, + .scheduledPresentTimestampsNanos = {100}, + .latchTimestampNanos = 50, + .outputIds = {0}, + }); + dataOut.emplace_back(hal::CompositionData{ + .timestampNanos = 200L, + .scheduledPresentTimestampsNanos = {300}, + .latchTimestampNanos = 250, + .outputIds = {0}, + }); + EXPECT_CALL(*mMockHal.get(), sendCompositionData(_)) + .Times(Exactly(1)) + .WillOnce([&](const std::vector<hal::CompositionData>& passedData) { + if (!std::equal(passedData.begin(), passedData.end(), dataOut.begin())) { + ADD_FAILURE() << "Passed composition data not the same"; + } + return ndk::ScopedAStatus::ok(); + }); + + ASSERT_TRUE(mWrapper->sendCompositionData(dataOut).isOk()); +} + +TEST_F(PowerHalWrapperAidlTest, TestSendingCompositionUpdate) { + int32_t tgid = 999; + int32_t uid = 1001; + hal::CompositionUpdate dataOut{ + .timestampNanos = 123, + .deadOutputIds = {1, 2, 3}, + }; + EXPECT_CALL(*mMockHal.get(), sendCompositionUpdate(_)) + .Times(Exactly(1)) + .WillOnce([&](const hal::CompositionUpdate& passedData) { + if (passedData != dataOut) { + ADD_FAILURE() << "Passed composition update data not the same"; + } + return ndk::ScopedAStatus::ok(); + }); + + ASSERT_TRUE(mWrapper->sendCompositionUpdate(dataOut).isOk()); +}
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h index 39748b8417..acd915464e 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -37,6 +37,9 @@ public: // Gets the DisplayId for the display virtual DisplayId getId() const = 0; + // True if the display has a secure layer + virtual bool hasSecureLayers() const = 0; + // True if the display is secure virtual bool isSecure() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index ec87acc372..6ec7be885a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -68,7 +68,9 @@ public: // compositionengine::Display overrides DisplayId getId() const override; + bool hasSecureLayers() const override; bool isSecure() const override; + void setSecure(bool secure) override; bool isVirtual() const override; void disconnect() override; void createDisplayColorProfile( @@ -76,7 +78,6 @@ public: void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override; void createClientCompositionCache(uint32_t cacheSize) override; void applyDisplayBrightness(bool applyImmediately) override; - void setSecure(bool secure) override; // Internal helpers used by chooseCompositionStrategy() using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h index 46cb95ef25..2d51b71b09 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -32,6 +32,7 @@ public: virtual ~Display(); MOCK_CONST_METHOD0(getId, DisplayId()); + MOCK_CONST_METHOD0(hasSecureLayers, bool()); MOCK_CONST_METHOD0(isSecure, bool()); MOCK_METHOD1(setSecure, void(bool)); MOCK_CONST_METHOD0(isVirtual, bool()); diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 5a546777f4..531cab6938 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -70,6 +70,14 @@ DisplayId Display::getId() const { return asDisplayId(mIdVariant); } +bool Display::hasSecureLayers() const { + const auto layers = getOutputLayersOrderedByZ(); + return std::any_of(layers.begin(), layers.end(), [](const auto& layer) { + const auto* state = layer->getLayerFE().getCompositionState(); + return state && state->isSecure; + }); +} + bool Display::isSecure() const { return getState().isSecure; } diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp index 87a677cd58..7c19885231 100644 --- a/services/surfaceflinger/Display/DisplayModeController.cpp +++ b/services/surfaceflinger/Display/DisplayModeController.cpp @@ -46,11 +46,21 @@ DisplayModeController::Display::Display(DisplaySnapshotRef snapshot, renderRateFpsTrace(concatId("RenderRateFps")), hasDesiredModeTrace(concatId("HasDesiredMode"), false) {} +DisplayModeController::DisplayModeController() { + using namespace std::string_literals; + mSupportsHdcp = base::GetBoolProperty("debug.sf.hdcp_support"s, false); +} + void DisplayModeController::registerDisplay(PhysicalDisplayId displayId, DisplaySnapshotRef snapshotRef, RefreshRateSelectorPtr selectorPtr) { + DisplayPtr displayPtr = std::make_unique<Display>(snapshotRef, selectorPtr); + // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced + displayPtr->setSecure(!supportsHdcp() || + snapshotRef.get().connectionType() == + ui::DisplayConnectionType::Internal); std::lock_guard lock(mDisplayLock); - mDisplays.emplace_or_replace(displayId, std::make_unique<Display>(snapshotRef, selectorPtr)); + mDisplays.emplace_or_replace(displayId, std::move(displayPtr)); } void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef, @@ -58,11 +68,14 @@ void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef, scheduler::RefreshRateSelector::Config config) { const auto& snapshot = snapshotRef.get(); const auto displayId = snapshot.displayId(); - + DisplayPtr displayPtr = + std::make_unique<Display>(snapshotRef, snapshot.displayModes(), activeModeId, config); + // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced + displayPtr->setSecure(!supportsHdcp() || + snapshotRef.get().connectionType() == + ui::DisplayConnectionType::Internal); std::lock_guard lock(mDisplayLock); - mDisplays.emplace_or_replace(displayId, - std::make_unique<Display>(snapshotRef, snapshot.displayModes(), - activeModeId, config)); + mDisplays.emplace_or_replace(displayId, std::move(displayPtr)); } void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) { @@ -304,5 +317,30 @@ auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId) return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled}; } +bool DisplayModeController::supportsHdcp() const { + return mSupportsHdcp && FlagManager::getInstance().hdcp_level_hal() && + FlagManager::getInstance().hdcp_negotiation(); +} + +void DisplayModeController::startHdcpNegotiation(PhysicalDisplayId displayId) { + using aidl::android::hardware::drm::HdcpLevel; + using aidl::android::hardware::drm::HdcpLevels; + constexpr HdcpLevels kLevels = {.connectedLevel = HdcpLevel::HDCP_V2_1, + .maxLevel = HdcpLevel::HDCP_V2_3}; + + std::lock_guard lock(mDisplayLock); + const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get(); + if (displayPtr->hdcpState == HdcpState::Desired) { + const auto status = mComposerPtr->startHdcpNegotiation(displayId, kLevels); + displayPtr->hdcpState = (status == NO_ERROR) ? HdcpState::Enabled : HdcpState::Undesired; + } +} + +void DisplayModeController::setSecure(PhysicalDisplayId displayId, bool secure) { + std::lock_guard lock(mDisplayLock); + const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get(); + displayPtr->setSecure(secure); +} + #pragma clang diagnostic pop } // namespace android::display diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h index af3e909bcf..f20434857a 100644 --- a/services/surfaceflinger/Display/DisplayModeController.h +++ b/services/surfaceflinger/Display/DisplayModeController.h @@ -46,7 +46,7 @@ class DisplayModeController { public: using ActiveModeListener = ftl::Function<void(PhysicalDisplayId, Fps vsyncRate, Fps renderFps)>; - DisplayModeController() = default; + DisplayModeController(); void setHwComposer(HWComposer* composerPtr) { mComposerPtr = composerPtr; } void setActiveModeListener(const ActiveModeListener& listener) { @@ -109,7 +109,16 @@ public: KernelIdleTimerState getKernelIdleTimerState(PhysicalDisplayId) const REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); + void setSecure(PhysicalDisplayId displayId, bool secure) REQUIRES(kMainThreadContext) + EXCLUDES(mDisplayLock); + + bool supportsHdcp() const; + + void startHdcpNegotiation(PhysicalDisplayId displayId) REQUIRES(kMainThreadContext); + private: + enum class HdcpState { Undesired, Desired, Enabled }; + struct Display { template <size_t N> std::string concatId(const char (&)[N]) const; @@ -120,6 +129,11 @@ private: : Display(snapshot, std::make_shared<scheduler::RefreshRateSelector>(std::move(modes), activeModeId, config)) {} + + void setSecure(bool secure) { + hdcpState = secure ? HdcpState::Undesired : HdcpState::Desired; + } + const DisplaySnapshotRef snapshot; const RefreshRateSelectorPtr selectorPtr; @@ -135,6 +149,8 @@ private: bool isModeSetPending GUARDED_BY(kMainThreadContext) = false; bool isKernelIdleTimerEnabled GUARDED_BY(kMainThreadContext) = false; + + HdcpState hdcpState = HdcpState::Desired; }; using DisplayPtr = std::unique_ptr<Display>; @@ -153,6 +169,8 @@ private: mutable std::mutex mDisplayLock; ui::PhysicalDisplayMap<PhysicalDisplayId, DisplayPtr> mDisplays GUARDED_BY(mDisplayLock); + + bool mSupportsHdcp = false; }; } // namespace android::display diff --git a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h index c68020ce51..71d9f2e468 100644 --- a/services/surfaceflinger/Display/VirtualDisplaySnapshot.h +++ b/services/surfaceflinger/Display/VirtualDisplaySnapshot.h @@ -35,6 +35,7 @@ public: VirtualDisplayId displayId() const { return mVirtualId; } bool isGpu() const { return mIsGpu; } + const std::string& uniqueId() const { return mUniqueId; } void dump(utils::Dumper& dumper) const { using namespace std::string_view_literals; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index de7d455fa4..bad5e2e3b5 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -50,6 +50,17 @@ namespace android { namespace hal = hardware::graphics::composer::hal; +namespace gui { +inline std::string_view to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) { + switch (optimizationPolicy) { + case ISurfaceComposer::OptimizationPolicy::optimizeForPower: + return "optimizeForPower"; + case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance: + return "optimizeForPerformance"; + } +} +} // namespace gui + DisplayDeviceCreationArgs::DisplayDeviceCreationArgs( const sp<SurfaceFlinger>& flinger, HWComposer& hwComposer, const wp<IBinder>& displayToken, std::shared_ptr<compositionengine::Display> compositionDisplay) diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 1b14145147..7d7c8adb7b 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -67,17 +67,6 @@ namespace display { class DisplaySnapshot; } // namespace display -namespace gui { -inline const char* to_string(ISurfaceComposer::OptimizationPolicy optimizationPolicy) { - switch (optimizationPolicy) { - case ISurfaceComposer::OptimizationPolicy::optimizeForPower: - return "optimizeForPower"; - case ISurfaceComposer::OptimizationPolicy::optimizeForPerformance: - return "optimizeForPerformance"; - } -} -} // namespace gui - class DisplayDevice : public RefBase { public: constexpr static float sDefaultMinLumiance = 0.0; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index d547af98ea..8ead09ca19 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -328,9 +328,7 @@ std::string AidlComposer::dumpDebugInfo() { std::string str; // Use other thread to read pipe to prevent // pipe is full, making HWC be blocked in writing. - std::thread t([&]() { - base::ReadFdToString(pipefds[0], &str); - }); + std::thread t([&]() { base::ReadFdToString(pipefds[0], &str); }); const auto status = mAidlComposer->dump(pipefds[1], /*args*/ nullptr, /*numArgs*/ 0); // Close the write-end of the pipe to make sure that when reading from the // read-end we will get eof instead of blocking forever @@ -1720,6 +1718,18 @@ Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, Pictu return error; } +Error AidlComposer::startHdcpNegotiation(Display display, + const aidl::android::hardware::drm::HdcpLevels& levels) { + const auto status = + mAidlComposerClient->startHdcpNegotiation(translate<int64_t>(display), levels); + if (!status.isOk()) { + ALOGE("startHdcpNegotiation failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + + return Error::NONE; +} + Error AidlComposer::getLuts(Display display, const std::vector<sp<GraphicBuffer>>& buffers, std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) { std::vector<aidl::android::hardware::graphics::composer3::Buffer> aidlBuffers; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index 5fcc8b00ba..b84d39a2c3 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -250,6 +250,7 @@ public: Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; Error setDisplayPictureProfileId(Display, PictureProfileId id) override; Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override; + Error startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) override; Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override; diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 018ee6e461..c55893106f 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -229,14 +229,13 @@ public: virtual std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys( Display display) = 0; virtual Error getRenderIntents(Display display, ColorMode colorMode, - std::vector<RenderIntent>* outRenderIntents) = 0; + std::vector<RenderIntent>* outRenderIntents) = 0; virtual Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) = 0; // Composer HAL 2.3 virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort, std::vector<uint8_t>* outData) = 0; - virtual Error setLayerColorTransform(Display display, Layer layer, - const float* matrix) = 0; + virtual Error setLayerColorTransform(Display display, Layer layer, const float* matrix) = 0; virtual Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat, Dataspace* outDataspace, uint8_t* outComponentMask) = 0; @@ -318,6 +317,8 @@ public: virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0; virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0; virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0; + virtual Error startHdcpNegotiation(Display display, + const aidl::android::hardware::drm::HdcpLevels& levels) = 0; virtual Error getLuts(Display display, const std::vector<sp<GraphicBuffer>>&, std::vector<V3_0::Luts>*) = 0; }; diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 8d16a6bbb3..fd0bf7386b 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -674,6 +674,11 @@ Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) { return static_cast<Error>(error); } +Error Display::startHdcpNegotiation(const aidl::android::hardware::drm::HdcpLevels& levels) { + const auto error = mComposer.startHdcpNegotiation(mId, levels); + return static_cast<Error>(error); +} + Error Display::getLuts(const std::vector<sp<GraphicBuffer>>& buffers, std::vector<aidl::android::hardware::graphics::composer3::Luts>* outLuts) { const auto error = mComposer.getLuts(mId, buffers, outLuts); diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 7c1f8e3da0..3f518211c4 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -203,6 +203,8 @@ public: [[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0; [[nodiscard]] virtual hal::Error setPictureProfileHandle( const PictureProfileHandle& handle) = 0; + [[nodiscard]] virtual hal::Error startHdcpNegotiation( + const aidl::android::hardware::drm::HdcpLevels& levels) = 0; [[nodiscard]] virtual hal::Error getLuts( const std::vector<android::sp<android::GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0; @@ -291,6 +293,8 @@ public: hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override; hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override; hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override; + hal::Error startHdcpNegotiation( + const aidl::android::hardware::drm::HdcpLevels& levels) override; hal::Error getLuts(const std::vector<android::sp<android::GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 14088a6428..758d924a53 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -560,7 +560,7 @@ status_t HWComposer::getDeviceCompositionChanges( if (!hasChangesError(error)) { RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR); } - if (state == 1) { //Present Succeeded. + if (state == 1) { // Present Succeeded. std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; error = hwcDisplay->getReleaseFences(&releaseFences); displayData.releaseFences = std::move(releaseFences); @@ -824,8 +824,8 @@ mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Datasp RETURN_IF_INVALID_DISPLAY(displayId, {}); mat4 matrix; - auto error = mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace, - &matrix); + auto error = + mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace, &matrix); RETURN_IF_HWC_ERROR(error, displayId, {}); return matrix; } @@ -1054,6 +1054,15 @@ status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId, return NO_ERROR; } +status_t HWComposer::startHdcpNegotiation(PhysicalDisplayId displayId, + const aidl::android::hardware::drm::HdcpLevels& levels) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; + auto error = hwcDisplay->startHdcpNegotiation(levels); + RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); + return NO_ERROR; +} + status_t HWComposer::getLuts( PhysicalDisplayId displayId, const std::vector<sp<GraphicBuffer>>& buffers, std::vector<aidl::android::hardware::graphics::composer3::Luts>* luts) { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 472411cf99..fcecd23bca 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -327,6 +327,8 @@ public: virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0; virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId, const PictureProfileHandle& handle) = 0; + virtual status_t startHdcpNegotiation(PhysicalDisplayId, + const aidl::android::hardware::drm::HdcpLevels&) = 0; virtual status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*) = 0; }; @@ -494,6 +496,8 @@ public: int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override; status_t setDisplayPictureProfileHandle(PhysicalDisplayId, const android::PictureProfileHandle& profile) override; + status_t startHdcpNegotiation(PhysicalDisplayId, + const aidl::android::hardware::drm::HdcpLevels&) override; status_t getLuts(PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index a010353423..5e03f30304 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -1230,15 +1230,16 @@ Error HidlComposer::getDisplayCapabilities(Display display, translate<DisplayCapability>(tmpCaps); }); } else { - mClient_2_3 - ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) { - error = static_cast<V2_4::Error>(tmpError); - if (error != V2_4::Error::NONE) { - return; - } + mClient_2_3->getDisplayCapabilities(display, + [&](const auto& tmpError, const auto& tmpCaps) { + error = static_cast<V2_4::Error>(tmpError); + if (error != V2_4::Error::NONE) { + return; + } - *outCapabilities = translate<DisplayCapability>(tmpCaps); - }); + *outCapabilities = + translate<DisplayCapability>(tmpCaps); + }); } return static_cast<Error>(error); @@ -1460,6 +1461,10 @@ Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) { return Error::UNSUPPORTED; } +Error HidlComposer::startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) { + return Error::UNSUPPORTED; +} + Error HidlComposer::getLuts(Display, const std::vector<sp<GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*) { return Error::UNSUPPORTED; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index 86ca4b1782..d3874e4889 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -363,6 +363,7 @@ public: Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; Error setDisplayPictureProfileId(Display, PictureProfileId) override; Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override; + Error startHdcpNegotiation(Display, const aidl::android::hardware::drm::HdcpLevels&) override; Error getLuts(Display, const std::vector<sp<GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*) override; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 008b0571c3..51d4078987 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -29,7 +29,6 @@ #include <cinttypes> #include <numeric> #include <unordered_set> -#include <vector> #include "../Jank/JankTracker.h" @@ -1005,11 +1004,6 @@ void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, finalizeCurrentDisplayFrame(); } -const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames() - const { - return mPresentFrames; -} - void FrameTimeline::onCommitNotComposited() { SFTRACE_CALL(); std::scoped_lock lock(mMutex); @@ -1530,7 +1524,6 @@ void FrameTimeline::flushPendingPresentFences() { mPendingPresentFences.erase(mPendingPresentFences.begin()); } - mPresentFrames.clear(); for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& pendingPresentFence = mPendingPresentFences[i]; nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; @@ -1544,12 +1537,6 @@ void FrameTimeline::flushPendingPresentFences() { auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); - // Surface frames have been jank classified and can be provided to caller - // to detect if buffer stuffing is occurring. - for (const auto& frame : displayFrame->getSurfaceFrames()) { - mPresentFrames.push_back(frame); - } - mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 9fedb57aca..fa83cd8523 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -331,11 +331,6 @@ public: virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence) = 0; - // Provides surface frames that have already been jank classified in the most recent - // flush of pending present fences. This allows buffer stuffing detection from SF. - virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames() - const = 0; - // Tells FrameTimeline that a frame was committed but not composited. This is used to flush // all the associated surface frames. virtual void onCommitNotComposited() = 0; @@ -513,8 +508,6 @@ public: void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override; - const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames() - const override; void onCommitNotComposited() override; void parseArgs(const Vector<String16>& args, std::string& result) override; void setMaxDisplayFrames(uint32_t size) override; @@ -562,9 +555,6 @@ private: // 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; - // Presented surface frames that have been jank classified and can - // indicate of potential buffer stuffing. - std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames; }; } // namespace impl diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index da536b6660..00ec863bd9 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -54,7 +54,8 @@ LayerHierarchy::LayerHierarchy(const LayerHierarchy& hierarchy, bool childrenOnl mChildren = hierarchy.mChildren; } -void LayerHierarchy::traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& traversalPath, +void LayerHierarchy::traverse(const Visitor& visitor, + const LayerHierarchy::TraversalPath& traversalPath, uint32_t depth) const { LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50, "Cycle detected in LayerHierarchy::traverse. See " @@ -70,14 +71,13 @@ void LayerHierarchy::traverse(const Visitor& visitor, LayerHierarchy::TraversalP LLOG_ALWAYS_FATAL_WITH_TRACE_IF(traversalPath.hasRelZLoop(), "Found relative z loop layerId:%d", traversalPath.invalidRelativeRootId); for (auto& [child, childVariant] : mChildren) { - ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id, - childVariant); - child->traverse(visitor, traversalPath, depth + 1); + child->traverse(visitor, traversalPath.makeChild(child->mLayer->id, childVariant), + depth + 1); } } void LayerHierarchy::traverseInZOrder(const Visitor& visitor, - LayerHierarchy::TraversalPath& traversalPath) const { + const LayerHierarchy::TraversalPath& traversalPath) const { bool traverseThisLayer = (mLayer != nullptr); for (auto it = mChildren.begin(); it < mChildren.end(); it++) { auto& [child, childVariant] = *it; @@ -91,9 +91,7 @@ void LayerHierarchy::traverseInZOrder(const Visitor& visitor, if (childVariant == LayerHierarchy::Variant::Detached) { continue; } - ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id, - childVariant); - child->traverseInZOrder(visitor, traversalPath); + child->traverseInZOrder(visitor, traversalPath.makeChild(child->mLayer->id, childVariant)); } if (traverseThisLayer) { @@ -568,42 +566,23 @@ std::string LayerHierarchy::TraversalPath::toString() const { return ss.str(); } -// Helper class to update a passed in TraversalPath when visiting a child. When the object goes out -// of scope the TraversalPath is reset to its original state. -LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath& traversalPath, - uint32_t layerId, - LayerHierarchy::Variant variant) - : mTraversalPath(traversalPath), mParentPath(traversalPath) { - // Update the traversal id with the child layer id and variant. Parent id and variant are - // stored to reset the id upon destruction. - traversalPath.id = layerId; - traversalPath.variant = variant; +LayerHierarchy::TraversalPath LayerHierarchy::TraversalPath::makeChild( + uint32_t layerId, LayerHierarchy::Variant variant) const { + TraversalPath child{*this}; + child.id = layerId; + child.variant = variant; if (LayerHierarchy::isMirror(variant)) { - traversalPath.mirrorRootIds.emplace_back(mParentPath.id); + child.mirrorRootIds.emplace_back(id); } else if (variant == LayerHierarchy::Variant::Relative) { - if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(), - layerId) != traversalPath.relativeRootIds.end()) { - traversalPath.invalidRelativeRootId = layerId; + if (std::find(relativeRootIds.begin(), relativeRootIds.end(), layerId) != + relativeRootIds.end()) { + child.invalidRelativeRootId = layerId; } - traversalPath.relativeRootIds.emplace_back(layerId); + child.relativeRootIds.emplace_back(layerId); } else if (variant == LayerHierarchy::Variant::Detached) { - traversalPath.detached = true; + child.detached = true; } -} -LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() { - // Reset the traversal id to its original parent state using the state that was saved in - // the constructor. - if (LayerHierarchy::isMirror(mTraversalPath.variant)) { - mTraversalPath.mirrorRootIds.pop_back(); - } else if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) { - mTraversalPath.relativeRootIds.pop_back(); - } - if (mTraversalPath.invalidRelativeRootId == mTraversalPath.id) { - mTraversalPath.invalidRelativeRootId = UNASSIGNED_LAYER_ID; - } - mTraversalPath.id = mParentPath.id; - mTraversalPath.variant = mParentPath.variant; - mTraversalPath.detached = mParentPath.detached; + return child; } } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index 4fdbae1831..c8c6b4df15 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -104,6 +104,8 @@ public: TraversalPath getClonedFrom() const { return {.id = id, .variant = variant}; } + TraversalPath makeChild(uint32_t layerId, LayerHierarchy::Variant variant) const; + bool operator==(const TraversalPath& other) const { return id == other.id && mirrorRootIds == other.mirrorRootIds; } @@ -122,18 +124,6 @@ public: } }; - // Helper class to add nodes to an existing traversal id and removes the - // node when it goes out of scope. - class ScopedAddToTraversalPath { - public: - ScopedAddToTraversalPath(TraversalPath& traversalPath, uint32_t layerId, - LayerHierarchy::Variant variantArg); - ~ScopedAddToTraversalPath(); - - private: - TraversalPath& mTraversalPath; - TraversalPath mParentPath; - }; LayerHierarchy(RequestedLayerState* layer); // Visitor function that provides the hierarchy node and a traversal id which uniquely @@ -191,8 +181,9 @@ private: void removeChild(LayerHierarchy*); void sortChildrenByZOrder(); void updateChild(LayerHierarchy*, LayerHierarchy::Variant); - void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const; - void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent, + void traverseInZOrder(const Visitor& visitor, + const LayerHierarchy::TraversalPath& parent) const; + void traverse(const Visitor& visitor, const LayerHierarchy::TraversalPath& parent, uint32_t depth = 0) const; void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant, bool isLastChild, bool includeMirroredHierarchy) const; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 28a6031c97..50ed72de9e 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -447,15 +447,14 @@ void LayerSnapshotBuilder::updateSnapshots(const Args& args) { if (args.root.getLayer()) { // The hierarchy can have a root layer when used for screenshots otherwise, it will have // multiple children. - LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id, - LayerHierarchy::Variant::Attached); - updateSnapshotsInHierarchy(args, args.root, root, rootSnapshot, /*depth=*/0); + LayerHierarchy::TraversalPath childPath = + root.makeChild(args.root.getLayer()->id, LayerHierarchy::Variant::Attached); + updateSnapshotsInHierarchy(args, args.root, childPath, rootSnapshot, /*depth=*/0); } else { for (auto& [childHierarchy, variant] : args.root.mChildren) { - LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, - childHierarchy->getLayer()->id, - variant); - updateSnapshotsInHierarchy(args, *childHierarchy, root, rootSnapshot, /*depth=*/0); + LayerHierarchy::TraversalPath childPath = + root.makeChild(childHierarchy->getLayer()->id, variant); + updateSnapshotsInHierarchy(args, *childHierarchy, childPath, rootSnapshot, /*depth=*/0); } } @@ -520,7 +519,7 @@ void LayerSnapshotBuilder::update(const Args& args) { const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy( const Args& args, const LayerHierarchy& hierarchy, - LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot, + const LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot, int depth) { LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50, "Cycle detected in LayerSnapshotBuilder. See " @@ -549,12 +548,10 @@ const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy( bool childHasValidFrameRate = false; for (auto& [childHierarchy, variant] : hierarchy.mChildren) { - LayerHierarchy::ScopedAddToTraversalPath addChildToPath(traversalPath, - childHierarchy->getLayer()->id, - variant); + LayerHierarchy::TraversalPath childPath = + traversalPath.makeChild(childHierarchy->getLayer()->id, variant); const LayerSnapshot& childSnapshot = - updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot, - depth + 1); + updateSnapshotsInHierarchy(args, *childHierarchy, childPath, *snapshot, depth + 1); updateFrameRateFromChildSnapshot(*snapshot, childSnapshot, *childHierarchy->getLayer(), args, &childHasValidFrameRate); } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h index 486cb33959..94b7e5fa5a 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h @@ -106,9 +106,10 @@ private: void updateSnapshots(const Args& args); - const LayerSnapshot& updateSnapshotsInHierarchy(const Args&, const LayerHierarchy& hierarchy, - LayerHierarchy::TraversalPath& traversalPath, - const LayerSnapshot& parentSnapshot, int depth); + const LayerSnapshot& updateSnapshotsInHierarchy( + const Args&, const LayerHierarchy& hierarchy, + const LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot, + int depth); void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&, const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&); static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot, diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 84b1a73e0b..280d66e12a 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -278,10 +278,9 @@ LayerProtoFromSnapshotGenerator& LayerProtoFromSnapshotGenerator::with( stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) { continue; } - frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path, - child->getLayer()->id, - variant); - LayerProtoFromSnapshotGenerator::writeHierarchyToProto(*child, path); + LayerProtoFromSnapshotGenerator::writeHierarchyToProto(*child, + path.makeChild(child->getLayer()->id, + variant)); } // fill in relative and parent info @@ -338,7 +337,8 @@ LayerProtoFromSnapshotGenerator& LayerProtoFromSnapshotGenerator::withOffscreenL } frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot( - frontend::LayerHierarchy::TraversalPath& path, const frontend::RequestedLayerState& layer) { + const frontend::LayerHierarchy::TraversalPath& path, + const frontend::RequestedLayerState& layer) { frontend::LayerSnapshot* snapshot = mSnapshotBuilder.getSnapshot(path); if (snapshot) { return snapshot; @@ -349,7 +349,7 @@ frontend::LayerSnapshot* LayerProtoFromSnapshotGenerator::getSnapshot( } void LayerProtoFromSnapshotGenerator::writeHierarchyToProto( - const frontend::LayerHierarchy& root, frontend::LayerHierarchy::TraversalPath& path) { + const frontend::LayerHierarchy& root, const frontend::LayerHierarchy::TraversalPath& path) { using Variant = frontend::LayerHierarchy::Variant; perfetto::protos::LayerProto* layerProto = mLayersProto.add_layers(); const frontend::RequestedLayerState& layer = *root.getLayer(); @@ -362,10 +362,8 @@ void LayerProtoFromSnapshotGenerator::writeHierarchyToProto( LayerProtoHelper::writeSnapshotToProto(layerProto, layer, *snapshot, mTraceFlags); for (const auto& [child, variant] : root.mChildren) { - frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path, - child->getLayer()->id, - variant); - frontend::LayerSnapshot* childSnapshot = getSnapshot(path, layer); + frontend::LayerSnapshot* childSnapshot = + getSnapshot(path.makeChild(child->getLayer()->id, variant), layer); if (variant == Variant::Attached || variant == Variant::Detached || frontend::LayerHierarchy::isMirror(variant)) { mChildToParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence; @@ -388,10 +386,7 @@ void LayerProtoFromSnapshotGenerator::writeHierarchyToProto( if (variant == Variant::Detached) { continue; } - frontend::LayerHierarchy::ScopedAddToTraversalPath addChildToPath(path, - child->getLayer()->id, - variant); - writeHierarchyToProto(*child, path); + writeHierarchyToProto(*child, path.makeChild(child->getLayer()->id, variant)); } } diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 3ca553a903..28924e45be 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -98,8 +98,8 @@ public: private: void writeHierarchyToProto(const frontend::LayerHierarchy& root, - frontend::LayerHierarchy::TraversalPath& path); - frontend::LayerSnapshot* getSnapshot(frontend::LayerHierarchy::TraversalPath& path, + const frontend::LayerHierarchy::TraversalPath& path); + frontend::LayerSnapshot* getSnapshot(const frontend::LayerHierarchy::TraversalPath& path, const frontend::RequestedLayerState& layer); const frontend::LayerSnapshotBuilder& mSnapshotBuilder; diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index c37b9653cb..5390295e9a 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -512,14 +512,6 @@ void EventThread::onModeRejected(PhysicalDisplayId displayId, DisplayModeId mode mCondition.notify_all(); } -// Merge lists of buffer stuffed Uids -void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { - std::lock_guard<std::mutex> lock(mMutex); - for (auto& [uid, count] : bufferStuffedUids) { - mBufferStuffedUids.emplace_or_replace(uid, count); - } -} - void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { DisplayEventConsumers consumers; @@ -761,10 +753,6 @@ void EventThread::generateFrameTimeline(VsyncEventData& outVsyncEventData, nsecs void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, const DisplayEventConsumers& consumers) { - // List of Uids that have been sent vsync data with queued buffer count. - // Used to keep track of which Uids can be removed from the map of - // buffer stuffed clients. - ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers; for (const auto& consumer : consumers) { DisplayEventReceiver::Event copy = event; if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC) { @@ -774,13 +762,6 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, event.vsync.vsyncData.preferredExpectedPresentationTime(), event.vsync.vsyncData.preferredDeadlineTimestamp()); } - auto it = mBufferStuffedUids.find(consumer->mOwnerUid); - if (it != mBufferStuffedUids.end()) { - copy.vsync.vsyncData.numberQueuedBuffers = it->second; - uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid); - } else { - copy.vsync.vsyncData.numberQueuedBuffers = 0; - } switch (consumer->postEvent(copy)) { case NO_ERROR: break; @@ -796,12 +777,6 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, removeDisplayEventConnectionLocked(consumer); } } - // The clients that have already received the queued buffer count - // can be removed from the buffer stuffed Uid list to avoid - // being sent duplicate messages. - for (auto uid : uidsPostedQueuedBuffers) { - mBufferStuffedUids.erase(uid); - } if (event.header.type == DisplayEventType::DISPLAY_EVENT_VSYNC && FlagManager::getInstance().vrr_config()) { mLastCommittedVsyncTime = diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index a91dde7430..612883a88b 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -56,7 +56,6 @@ using gui::VsyncEventData; // --------------------------------------------------------------------------- using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; -using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>; enum class VSyncRequest { None = -2, @@ -141,10 +140,6 @@ public: virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) = 0; - - // An elevated number of queued buffers in the server is detected. This propagates a - // flag to Choreographer indicating that buffer stuffing recovery should begin. - virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) = 0; }; struct IEventThreadCallback { @@ -199,8 +194,6 @@ public: void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) override; - void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override; - private: friend EventThreadTest; @@ -241,10 +234,6 @@ private: scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; - // All consumers that need to recover from buffer stuffing and the number - // of their queued buffers. - BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex); - IEventThreadCallback& mCallback; std::thread mThread; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 911d4894ce..16266c6b00 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -124,7 +124,10 @@ void Scheduler::setPacesetterDisplay(PhysicalDisplayId pacesetterId) { // Cancel the pending refresh rate change, if any, before updating the phase configuration. mVsyncModulator->cancelRefreshRateChange(); - mVsyncConfiguration->reset(); + { + std::scoped_lock lock{mVsyncConfigLock}; + mVsyncConfiguration->reset(); + } updatePhaseConfiguration(pacesetterId, pacesetterSelectorPtr()->getActiveMode().fps); } @@ -211,7 +214,7 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, .vsyncId = vsyncId, .expectedVsyncTime = expectedVsyncTime, .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration, - .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration, + .hwcMinWorkDuration = getCurrentVsyncConfigs().hwcMinWorkDuration, .debugPresentTimeDelay = debugPresentDelay}; ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked(); @@ -516,12 +519,26 @@ void Scheduler::updatePhaseConfiguration(PhysicalDisplayId displayId, Fps refres if (!isPacesetter) return; mRefreshRateStats->setRefreshRate(refreshRate); - mVsyncConfiguration->setRefreshRateFps(refreshRate); - setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()), - refreshRate.getPeriod()); + const auto currentConfigs = [=, this] { + std::scoped_lock lock{mVsyncConfigLock}; + mVsyncConfiguration->setRefreshRateFps(refreshRate); + return mVsyncConfiguration->getCurrentConfigs(); + }(); + setVsyncConfig(mVsyncModulator->setVsyncConfigSet(currentConfigs), refreshRate.getPeriod()); } #pragma clang diagnostic pop +void Scheduler::reloadPhaseConfiguration(Fps refreshRate, Duration minSfDuration, + Duration maxSfDuration, Duration appDuration) { + const auto currentConfigs = [=, this] { + std::scoped_lock lock{mVsyncConfigLock}; + mVsyncConfiguration = std::make_unique<impl::WorkDuration>(refreshRate, minSfDuration, + maxSfDuration, appDuration); + return mVsyncConfiguration->getCurrentConfigs(); + }(); + setVsyncConfig(mVsyncModulator->setVsyncConfigSet(currentConfigs), refreshRate.getPeriod()); +} + void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) { mRefreshRateStats->setPowerMode(powerMode); } @@ -896,8 +913,11 @@ void Scheduler::dump(utils::Dumper& dumper) const { mFrameRateOverrideMappings.dump(dumper); dumper.eol(); - mVsyncConfiguration->dump(dumper.out()); - dumper.eol(); + { + std::scoped_lock lock{mVsyncConfigLock}; + mVsyncConfiguration->dump(dumper.out()); + dumper.eol(); + } mRefreshRateStats->dump(dumper.out()); dumper.eol(); @@ -960,11 +980,6 @@ bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals, return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); } -void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { - if (!mRenderEventThread) return; - mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids)); -} - void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) { std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule; { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 81389e7362..694856e9ce 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -53,6 +53,7 @@ #include "RefreshRateSelector.h" #include "SmallAreaDetectionAllowMappings.h" #include "Utils/Dumper.h" +#include "VsyncConfiguration.h" #include "VsyncModulator.h" #include <FrontEnd/LayerHierarchy.h> @@ -95,7 +96,7 @@ public: // TODO: b/241285191 - Remove this API by promoting pacesetter in onScreen{Acquired,Released}. void setPacesetterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) - EXCLUDES(mDisplayLock); + EXCLUDES(mDisplayLock, mVsyncConfigLock); using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>; @@ -188,9 +189,19 @@ public: } } - void updatePhaseConfiguration(PhysicalDisplayId, Fps); + void updatePhaseConfiguration(PhysicalDisplayId, Fps) EXCLUDES(mVsyncConfigLock); + void reloadPhaseConfiguration(Fps, Duration minSfDuration, Duration maxSfDuration, + Duration appDuration) EXCLUDES(mVsyncConfigLock); + + VsyncConfigSet getCurrentVsyncConfigs() const EXCLUDES(mVsyncConfigLock) { + std::scoped_lock lock{mVsyncConfigLock}; + return mVsyncConfiguration->getCurrentConfigs(); + } - const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; } + VsyncConfigSet getVsyncConfigsForRefreshRate(Fps refreshRate) const EXCLUDES(mVsyncConfigLock) { + std::scoped_lock lock{mVsyncConfigLock}; + return mVsyncConfiguration->getConfigsForRefreshRate(refreshRate); + } // Sets the render rate for the scheduler to run at. void setRenderRate(PhysicalDisplayId, Fps, bool applyImmediately); @@ -266,7 +277,7 @@ public: bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const; - void dump(utils::Dumper&) const; + void dump(utils::Dumper&) const EXCLUDES(mVsyncConfigLock); void dump(Cycle, std::string&) const; void dumpVsync(std::string&) const EXCLUDES(mDisplayLock); @@ -337,10 +348,6 @@ public: mPacesetterFrameDurationFractionToSkip = frameDurationFraction; } - // Propagates a flag to the EventThread indicating that buffer stuffing - // recovery should begin. - void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); - void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; } private: @@ -352,7 +359,7 @@ private: // impl::MessageQueue overrides: void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) override - REQUIRES(kMainThreadContext, mDisplayLock); + REQUIRES(kMainThreadContext, mDisplayLock) EXCLUDES(mVsyncConfigLock); // Used to skip event dispatch before EventThread creation during boot. // TODO: b/241285191 - Reorder Scheduler initialization to avoid this. @@ -480,8 +487,9 @@ private: const FeatureFlags mFeatures; + mutable std::mutex mVsyncConfigLock; // Stores phase offsets configured per refresh rate. - const std::unique_ptr<VsyncConfiguration> mVsyncConfiguration; + std::unique_ptr<VsyncConfiguration> mVsyncConfiguration GUARDED_BY(mVsyncConfigLock); // Shifts the VSYNC phase during certain transactions and refresh rate changes. const sp<VsyncModulator> mVsyncModulator; diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp index 6ae10f3d31..8cbb17c06e 100644 --- a/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp +++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.cpp @@ -362,6 +362,17 @@ WorkDuration::WorkDuration(Fps currentRefreshRate) validateSysprops(); } +WorkDuration::WorkDuration(Fps currentRefreshRate, Duration minSfDuration, Duration maxSfDuration, + Duration appDuration) + : WorkDuration(currentRefreshRate, + /*sfDuration*/ minSfDuration.ns(), + /*appDuration*/ appDuration.ns(), + /*sfEarlyDuration*/ maxSfDuration.ns(), + /*appEarlyDuration*/ appDuration.ns(), + /*sfEarlyGpuDuration*/ maxSfDuration.ns(), + /*appEarlyGpuDuration*/ appDuration.ns(), + /*hwcMinWorkDuration*/ 0) {} + WorkDuration::WorkDuration(Fps currentRefreshRate, nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration, nsecs_t sfEarlyGpuDuration, nsecs_t appEarlyGpuDuration, diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h index b6cb373b7e..3d8ae3e5d4 100644 --- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h +++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h @@ -144,6 +144,8 @@ private: class WorkDuration : public VsyncConfiguration { public: explicit WorkDuration(Fps currentRefreshRate); + WorkDuration(Fps currentRefreshRate, Duration minSfDuration, Duration maxSfDuration, + Duration appDuration); protected: // Used for unit tests diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index aa933ee8a7..3784cf12c5 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -19,7 +19,7 @@ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wextra" -//#define LOG_NDEBUG 0 +// #define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "SurfaceFlinger.h" @@ -66,13 +66,11 @@ #include <ftl/concat.h> #include <ftl/fake_guard.h> #include <ftl/future.h> -#include <ftl/small_map.h> #include <ftl/unit.h> #include <gui/AidlUtil.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> #include <gui/IProducerListener.h> -#include <gui/JankInfo.h> #include <gui/LayerMetadata.h> #include <gui/LayerState.h> #include <gui/Surface.h> @@ -363,7 +361,7 @@ bool isExpectedPresentWithinTimeout(TimePoint expectedPresentTime, return std::abs(expectedPresentTime.ns() - (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); } -} // namespace anonymous +} // namespace // --------------------------------------------------------------------------- @@ -395,7 +393,7 @@ ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::Pixel LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig; std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { - switch(displayColorSetting) { + switch (displayColorSetting) { case DisplayColorSetting::kManaged: return std::string("Managed"); case DisplayColorSetting::kUnmanaged: @@ -403,8 +401,7 @@ std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { case DisplayColorSetting::kEnhanced: return std::string("Enhanced"); default: - return std::string("Unknown ") + - std::to_string(static_cast<int>(displayColorSetting)); + return std::string("Unknown ") + std::to_string(static_cast<int>(displayColorSetting)); } } @@ -541,9 +538,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI } mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false); - - // These are set by the HWC implementation to indicate that they will use the workarounds. - mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false); } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { @@ -596,15 +590,14 @@ sp<IBinder> SurfaceFlinger::createVirtualDisplay( class DisplayToken : public BBinder { sp<SurfaceFlinger> flinger; virtual ~DisplayToken() { - // no more references, this display must be terminated - Mutex::Autolock _l(flinger->mStateLock); - flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this)); - flinger->setTransactionFlags(eDisplayTransactionNeeded); - } - public: - explicit DisplayToken(const sp<SurfaceFlinger>& flinger) - : flinger(flinger) { + // no more references, this display must be terminated + Mutex::Autolock _l(flinger->mStateLock); + flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this)); + flinger->setTransactionFlags(eDisplayTransactionNeeded); } + + public: + explicit DisplayToken(const sp<SurfaceFlinger>& flinger) : flinger(flinger) {} }; sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this)); @@ -763,7 +756,7 @@ void SurfaceFlinger::bootFinished() { const nsecs_t now = systemTime(); const nsecs_t duration = now - mBootTime; - ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration))); mFrameTracer->initialize(); mFrameTimeline->onBootFinished(); @@ -782,8 +775,7 @@ void SurfaceFlinger::bootFinished() { property_set("service.bootanim.exit", "1"); const int LOGTAG_SF_STOP_BOOTANIM = 60110; - LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, - ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); + LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); @@ -903,8 +895,8 @@ renderengine::RenderEngine::BlurAlgorithm chooseBlurAlgorithm(bool supportsBlur) void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { SFTRACE_CALL(); - ALOGI( "SurfaceFlinger's main thread ready to run. " - "Initializing graphics H/W..."); + ALOGI("SurfaceFlinger's main thread ready to run. " + "Initializing graphics H/W..."); addTransactionReadyFilters(); Mutex::Autolock lock(mStateLock); @@ -1127,17 +1119,16 @@ void SurfaceFlinger::readPersistentProperties() { static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0)); } -status_t SurfaceFlinger::getSupportedFrameTimestamps( - std::vector<FrameEvent>* outSupported) const { +status_t SurfaceFlinger::getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const { *outSupported = { - FrameEvent::REQUESTED_PRESENT, - FrameEvent::ACQUIRE, - FrameEvent::LATCH, - FrameEvent::FIRST_REFRESH_START, - FrameEvent::LAST_REFRESH_START, - FrameEvent::GPU_COMPOSITION_DONE, - FrameEvent::DEQUEUE_READY, - FrameEvent::RELEASE, + FrameEvent::REQUESTED_PRESENT, + FrameEvent::ACQUIRE, + FrameEvent::LATCH, + FrameEvent::FIRST_REFRESH_START, + FrameEvent::LAST_REFRESH_START, + FrameEvent::GPU_COMPOSITION_DONE, + FrameEvent::DEQUEUE_READY, + FrameEvent::RELEASE, }; if (mHasReliablePresentFences) { @@ -1232,8 +1223,8 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info outMode.peakRefreshRate = peakFps.getValue(); outMode.vsyncRate = mode->getVsyncRate().getValue(); - const auto vsyncConfigSet = mScheduler->getVsyncConfiguration().getConfigsForRefreshRate( - Fps::fromValue(outMode.peakRefreshRate)); + const auto vsyncConfigSet = + mScheduler->getVsyncConfigsForRefreshRate(Fps::fromValue(outMode.peakRefreshRate)); outMode.appVsyncOffset = vsyncConfigSet.late.appOffset; outMode.sfVsyncOffset = vsyncConfigSet.late.sfOffset; outMode.group = mode->getGroup(); @@ -2212,7 +2203,6 @@ status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken } hdrInfoReporter->addListener(listener); - mAddingHDRLayerInfoListener = true; return OK; } @@ -2273,13 +2263,13 @@ sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( const auto cycle = [&] { if (FlagManager::getInstance().deprecate_vsync_sf()) { ALOGW_IF(vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger, - "requested unsupported config eVsyncSourceSurfaceFlinger"); + "requested unsupported config eVsyncSourceSurfaceFlinger"); return scheduler::Cycle::Render; } return vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger - ? scheduler::Cycle::LastComposite - : scheduler::Cycle::Render; + ? scheduler::Cycle::LastComposite + : scheduler::Cycle::Render; }(); return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle); } @@ -2308,19 +2298,6 @@ void SurfaceFlinger::scheduleSample() { void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { - if (timestamp < 0 && vsyncPeriod.has_value()) { - if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) { - const int32_t value = static_cast<int32_t>(-timestamp); - // one byte is good enough to encode android.hardware.drm.HdcpLevel - const int32_t maxLevel = (value >> 8) & 0xFF; - const int32_t connectedLevel = value & 0xFF; - ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64, - __func__, connectedLevel, maxLevel, hwcDisplayId); - updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel); - return; - } - } - SFTRACE_NAME(vsyncPeriod ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str() : ftl::Concat(__func__, ' ', hwcDisplayId).c_str()); @@ -2809,9 +2786,10 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, // setTransactionFlags which will schedule another SF frame. This was if the tracker // needs to adjust the vsync timeline, it will be done before the next frame. if (FlagManager::getInstance().vrr_config() && mustComposite) { - mScheduler->getVsyncSchedule()->getTracker().onFrameBegin( - pacesetterFrameTarget.expectedPresentTime(), - pacesetterFrameTarget.lastSignaledFrameTime()); + mScheduler->getVsyncSchedule() + ->getTracker() + .onFrameBegin(pacesetterFrameTarget.expectedPresentTime(), + pacesetterFrameTarget.lastSignaledFrameTime()); } if (transactionFlushNeeded()) { setTransactionFlags(eTransactionFlushNeeded); @@ -2980,7 +2958,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( for (const auto& [token, display] : FTL_FAKE_GUARD(mStateLock, mDisplays)) { auto compositionDisplay = display->getCompositionDisplay(); if (!compositionDisplay->getState().isEnabled) continue; - for (auto outputLayer : compositionDisplay->getOutputLayersOrderedByZ()) { + for (const auto* outputLayer : compositionDisplay->getOutputLayersOrderedByZ()) { if (outputLayer->getLayerFE().getCompositionState() == nullptr) { // This is unexpected but instead of crashing, capture traces to disk // and recover gracefully by forcing CE to rebuild layer stack. @@ -3060,7 +3038,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( // This should be interpreted to mean that there are 2 cached sets. // So there are only 2 non skipped layers -- b and s. // The layers rrc and cc are flattened into layers b and s respectively. - const LayerFE::HwcLayerDebugState &hwcState = layerFE->getLastHwcState(); + const LayerFE::HwcLayerDebugState& hwcState = layerFE->getLastHwcState(); if (hwcState.overrideBufferId != prevOverrideBufferId) { // End the existing run. if (prevOverrideBufferId) { @@ -3072,11 +3050,10 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( } } - compositionSummary.push_back( - layerFE->mSnapshot->classifyCompositionForDebug(hwcState)); + compositionSummary.push_back(layerFE->mSnapshot->classifyCompositionForDebug(hwcState)); if (hwcState.overrideBufferId && !hwcState.wasSkipped) { - compositionSummary.push_back(':'); + compositionSummary.push_back(':'); } prevOverrideBufferId = hwcState.overrideBufferId; @@ -3107,11 +3084,23 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( .c_str()); mPowerAdvisor->setCompositedWorkload(compositedWorkload); - moveSnapshotsFromCompositionArgs(refreshArgs, layers); SFTRACE_ASYNC_FOR_TRACK_END(WorkloadTracer::TRACK_NAME, WorkloadTracer::COMPOSITION_TRACE_COOKIE); SFTRACE_NAME_FOR_TRACK(WorkloadTracer::TRACK_NAME, "Post Composition"); SFTRACE_NAME("postComposition"); + + if (mDisplayModeController.supportsHdcp()) { + for (const auto& [id, _] : frameTargeters) { + ftl::FakeGuard guard(mStateLock); + if (const auto display = getCompositionDisplayLocked(id)) { + if (!display->isSecure() && display->hasSecureLayers()) { + mDisplayModeController.startHdcpNegotiation(id); + } + } + } + } + + moveSnapshotsFromCompositionArgs(refreshArgs, layers); mTimeStats->recordFrameDuration(pacesetterTarget.frameBeginTime().ns(), systemTime()); // Send a power hint after presentation is finished. @@ -3315,40 +3304,12 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const TimePoint presentTime = TimePoint::now(); - // The Uids of layer owners that are in buffer stuffing mode, and their elevated - // buffer counts. Messages to start recovery are sent exclusively to these Uids. - BufferStuffingMap bufferStuffedUids; - // Set presentation information before calling Layer::releasePendingBuffer, such that jank // information from previous' frame classification is already available when sending jank info // to clients, so they get jank classification as early as possible. mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime, pacesetterGpuCompositionDoneFenceTime); - // Find and register any layers that are in buffer stuffing mode - const auto& presentFrames = mFrameTimeline->getPresentFrames(); - - for (const auto& frame : presentFrames) { - const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId()); - if (!layer) continue; - uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0; - int32_t jankType = frame->getJankType().value_or(JankType::None); - if (jankType & JankType::BufferStuffing && - layer->flags & layer_state_t::eRecoverableFromBufferStuffing) { - auto [it, wasEmplaced] = - bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers); - // Update with maximum number of queued buffers, allows clients drawing - // multiple windows to account for the most severely stuffed window - if (!wasEmplaced && it->second < numberQueuedBuffers) { - it->second = numberQueuedBuffers; - } - } - } - - if (!bufferStuffedUids.empty()) { - mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids)); - } - // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might // be sampled a little later than when we started doing work for this frame, // but that should be okay since CompositorTiming has snapping logic. @@ -3361,8 +3322,7 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const auto schedule = mScheduler->getVsyncSchedule(); const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime); const Fps renderRate = pacesetterDisplay->refreshRateSelector().getActiveMode().fps; - const nsecs_t vsyncPhase = - mScheduler->getVsyncConfiguration().getCurrentConfigs().late.sfOffset; + const nsecs_t vsyncPhase = mScheduler->getCurrentVsyncConfigs().late.sfOffset; const CompositorTiming compositorTiming(vsyncDeadline.ns(), renderRate.getPeriodNsecs(), vsyncPhase, presentLatency.ns()); @@ -3845,12 +3805,9 @@ std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDispl .hwcDisplayId = hwcDisplayId, .port = info.port, .activeMode = std::move(activeMode)}; - if (mIsHdcpViaNegVsync) { - state.isSecure = connectionType == ui::DisplayConnectionType::Internal; - } else { - // TODO(b/349703362): Remove this when HDCP aidl API becomes ready - state.isSecure = true; // All physical displays are currently considered secure. - } + // TODO: b/349703362 - Remove first condition when HDCP aidl APIs are enforced + state.isSecure = !mDisplayModeController.supportsHdcp() || + connectionType == ui::DisplayConnectionType::Internal; state.isProtected = true; state.displayName = std::move(info.name); state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId); @@ -4354,37 +4311,35 @@ void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) { mVisibleWindowIds = std::move(visibleWindowIds); } - BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo, - windowInfos = std::move(windowInfos), - displayInfos = std::move(displayInfos), - inputWindowCommands = - std::move(mInputWindowCommands), - inputFlinger = mInputFlinger, this, - visibleWindowsChanged, vsyncId, - frameTime]() mutable { - SFTRACE_NAME("BackgroundExecutor::updateInputFlinger"); - if (updateWindowInfo) { - mWindowInfosListenerInvoker - ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos), - std::move(displayInfos), - ftl::to_underlying(vsyncId), - frameTime.ns()}, - std::move(inputWindowCommands.releaseListeners()), - /* forceImmediateCall= */ visibleWindowsChanged || - !inputWindowCommands.getFocusRequests().empty()); - } else { - // If there are listeners but no changes to input windows, call the listeners - // immediately. - for (const auto& listener : inputWindowCommands.getListeners()) { - if (IInterface::asBinder(listener)->isBinderAlive()) { - listener->onWindowInfosReported(); + BackgroundExecutor::getInstance().sendCallbacks( + {[updateWindowInfo, windowInfos = std::move(windowInfos), + displayInfos = std::move(displayInfos), + inputWindowCommands = std::move(mInputWindowCommands), inputFlinger = mInputFlinger, + this, visibleWindowsChanged, vsyncId, frameTime]() mutable { + SFTRACE_NAME("BackgroundExecutor::updateInputFlinger"); + if (updateWindowInfo) { + mWindowInfosListenerInvoker + ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos), + std::move(displayInfos), + ftl::to_underlying(vsyncId), + frameTime.ns()}, + std::move(inputWindowCommands.releaseListeners()), + /* forceImmediateCall= */ visibleWindowsChanged || + !inputWindowCommands.getFocusRequests() + .empty()); + } else { + // If there are listeners but no changes to input windows, call the listeners + // immediately. + for (const auto& listener : inputWindowCommands.getListeners()) { + if (IInterface::asBinder(listener)->isBinderAlive()) { + listener->onWindowInfosReported(); + } + } } - } - } - for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) { - inputFlinger->setFocusedWindow(focusRequest); - } - }}); + for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) { + inputFlinger->setFocusedWindow(focusRequest); + } + }}); mInputWindowCommands.clear(); } @@ -4694,7 +4649,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { /*applyImmediately*/ true); } - const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs(); + const auto configs = mScheduler->getCurrentVsyncConfigs(); mScheduler->createEventThread(scheduler::Cycle::Render, mFrameTimeline->getTokenManager(), /* workDuration */ configs.late.appWorkDuration, @@ -5091,8 +5046,8 @@ status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() && resolvedState.state.surface) { sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface); - std::string layerName = (layer) ? - layer->getDebugName() : std::to_string(resolvedState.state.layerId); + std::string layerName = + (layer) ? layer->getDebugName() : std::to_string(resolvedState.state.layerId); resolvedState.externalTexture = getExternalTextureFromBufferData(*resolvedState.state.bufferData, layerName.c_str(), transactionState.getId()); @@ -5142,6 +5097,9 @@ status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState }(state.flags); const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone; + // Copy fields of |state| needed after it is moved into queueTransaction + VsyncId vsyncId{state.frameTimelineInfo.vsyncId}; + auto applyToken = state.applyToken; { // Transactions are added via a lockless queue and does not need to be added from the main // thread. @@ -5151,10 +5109,10 @@ status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState for (const auto& [displayId, data] : mNotifyExpectedPresentMap) { if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) { - scheduleNotifyExpectedPresentHint(displayId, VsyncId{state.frameTimelineInfo.vsyncId}); + scheduleNotifyExpectedPresentHint(displayId, vsyncId); } } - setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint); + setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint); return NO_ERROR; } @@ -5719,13 +5677,7 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa incRefreshableDisplays(); } - if (displayId == mActiveDisplayId && - FlagManager::getInstance().correct_virtual_display_power_state()) { - applyOptimizationPolicy(__func__); - } - const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr; - using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy; if (currentMode == hal::PowerMode::OFF) { // Turn on the display @@ -5740,10 +5692,12 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa onActiveDisplayChangedLocked(activeDisplay.get(), *display); } - if (displayId == mActiveDisplayId && - !FlagManager::getInstance().correct_virtual_display_power_state()) { - optimizeThreadScheduling("setPhysicalDisplayPowerMode(ON/DOZE)", - OptimizationPolicy::optimizeForPerformance); + if (displayId == mActiveDisplayId) { + if (FlagManager::getInstance().correct_virtual_display_power_state()) { + applyOptimizationPolicy("setPhysicalDisplayPowerMode(ON)"); + } else { + disablePowerOptimizations("setPhysicalDisplayPowerMode(ON)"); + } } getHwComposer().setPowerMode(displayId, mode); @@ -5752,8 +5706,7 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState(); requestHardwareVsync(displayId, enable); - if (displayId == mActiveDisplayId && - !FlagManager::getInstance().correct_virtual_display_power_state()) { + if (displayId == mActiveDisplayId) { mScheduler->enableSyntheticVsync(false); } @@ -5770,13 +5723,13 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa if (const auto display = getActivatableDisplay()) { onActiveDisplayChangedLocked(activeDisplay.get(), *display); } else { - if (!FlagManager::getInstance().correct_virtual_display_power_state()) { - optimizeThreadScheduling("setPhysicalDisplayPowerMode(OFF)", - OptimizationPolicy::optimizeForPower); + if (FlagManager::getInstance().correct_virtual_display_power_state()) { + applyOptimizationPolicy("setPhysicalDisplayPowerMode(OFF)"); + } else { + enablePowerOptimizations("setPhysicalDisplayPowerMode(OFF)"); } - if (currentModeNotDozeSuspend && - !FlagManager::getInstance().correct_virtual_display_power_state()) { + if (currentModeNotDozeSuspend) { mScheduler->enableSyntheticVsync(); } } @@ -5804,9 +5757,7 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON."); mVisibleRegionsDirty = true; scheduleRepaint(); - if (!FlagManager::getInstance().correct_virtual_display_power_state()) { - mScheduler->enableSyntheticVsync(false); - } + mScheduler->enableSyntheticVsync(false); } constexpr bool kAllowToEnable = true; mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get()); @@ -5816,8 +5767,7 @@ void SurfaceFlinger::setPhysicalDisplayPowerMode(const sp<DisplayDevice>& displa constexpr bool kDisallow = true; mScheduler->disableHardwareVsync(displayId, kDisallow); - if (displayId == mActiveDisplayId && - !FlagManager::getInstance().correct_virtual_display_power_state()) { + if (displayId == mActiveDisplayId) { mScheduler->enableSyntheticVsync(); } getHwComposer().setPowerMode(displayId, mode); @@ -5856,44 +5806,43 @@ void SurfaceFlinger::setVirtualDisplayPowerMode(const sp<DisplayDevice>& display to_string(displayId).c_str()); } -void SurfaceFlinger::optimizeThreadScheduling( - const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy) { - ALOGD("%s: Optimizing thread scheduling: %s", whence, to_string(optimizationPolicy)); +bool SurfaceFlinger::shouldOptimizeForPerformance() { + for (const auto& [_, display] : mDisplays) { + // Displays that are optimized for power are always powered on and should not influence + // whether there is an active display for the purpose of power optimization, etc. If these + // displays are being shown somewhere, a different (physical or virtual) display that is + // optimized for performance will be powered on in addition. Displays optimized for + // performance will change power mode, so if they are off then they are not active. + if (display->isPoweredOn() && + display->getOptimizationPolicy() == + gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance) { + return true; + } + } + return false; +} + +void SurfaceFlinger::enablePowerOptimizations(const char* whence) { + ALOGD("%s: Enabling power optimizations", whence); + + setSchedAttr(false, whence); + setSchedFifo(false, whence); +} + +void SurfaceFlinger::disablePowerOptimizations(const char* whence) { + ALOGD("%s: Disabling power optimizations", whence); - const bool optimizeForPerformance = - optimizationPolicy == gui::ISurfaceComposer::OptimizationPolicy::optimizeForPerformance; // TODO: b/281692563 - Merge the syscalls. For now, keep uclamp in a separate syscall // and set it before SCHED_FIFO due to b/190237315. - setSchedAttr(optimizeForPerformance, whence); - setSchedFifo(optimizeForPerformance, whence); + setSchedAttr(true, whence); + setSchedFifo(true, whence); } void SurfaceFlinger::applyOptimizationPolicy(const char* whence) { - using OptimizationPolicy = gui::ISurfaceComposer::OptimizationPolicy; - - const bool optimizeForPerformance = - std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) { - const auto& display = pair.second; - return display->isPoweredOn() && - display->getOptimizationPolicy() == - OptimizationPolicy::optimizeForPerformance; - }); - - optimizeThreadScheduling(whence, - optimizeForPerformance ? OptimizationPolicy::optimizeForPerformance - : OptimizationPolicy::optimizeForPower); - - if (mScheduler) { - const bool disableSyntheticVsync = - std::any_of(mDisplays.begin(), mDisplays.end(), [](const auto& pair) { - const auto& display = pair.second; - const hal::PowerMode powerMode = display->getPowerMode(); - return powerMode != hal::PowerMode::OFF && - powerMode != hal::PowerMode::DOZE_SUSPEND && - display->getOptimizationPolicy() == - OptimizationPolicy::optimizeForPerformance; - }); - mScheduler->enableSyntheticVsync(!disableSyntheticVsync); + if (shouldOptimizeForPerformance()) { + disablePowerOptimizations(whence); + } else { + enablePowerOptimizations(whence); } } @@ -5937,8 +5886,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - if ((uid != AID_SHELL) && - !PermissionCache::checkPermission(sDump, pid, uid)) { + if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid); write(fd, result.c_str(), result.size()); @@ -6123,9 +6071,8 @@ void SurfaceFlinger::dumpDisplays(std::string& result) const { display->dump(dumper); std::lock_guard lock(mVirtualDisplaysMutex); - const auto virtualSnapshotIt = mVirtualDisplays.find(virtualId); - if (virtualSnapshotIt != mVirtualDisplays.end()) { - virtualSnapshotIt->second.dump(dumper); + if (const auto snapshotOpt = mVirtualDisplays.get(virtualId)) { + snapshotOpt->get().dump(dumper); } } } @@ -6137,6 +6084,7 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { if (!displayId) { continue; } + const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId); if (!hwcDisplayId) { continue; @@ -6145,6 +6093,7 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { StringAppendF(&result, "Display %s (HWC display %" PRIu64 "): ", to_string(*displayId).c_str(), *hwcDisplayId); + uint8_t port; DisplayIdentificationData data; if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) { @@ -6172,6 +6121,19 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { result.append(edid->displayName.data(), edid->displayName.length()); result.append("\"\n"); } + + for (const auto& [token, display] : mDisplays) { + const auto virtualDisplayId = asVirtualDisplayId(display->getDisplayIdVariant()); + if (virtualDisplayId) { + StringAppendF(&result, "Display %s (Virtual display): displayName=\"%s\"", + to_string(*virtualDisplayId).c_str(), display->getDisplayName().c_str()); + std::lock_guard lock(mVirtualDisplaysMutex); + if (const auto snapshotOpt = mVirtualDisplays.get(*virtualDisplayId)) { + StringAppendF(&result, " uniqueId=\"%s\"", snapshotOpt->get().uniqueId().c_str()); + } + result.append("\n"); + } + } } void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args, @@ -6382,7 +6344,7 @@ void SurfaceFlinger::dumpAll(const DumpArgs& args, const std::string& compositio // figure out if we're stuck somewhere const nsecs_t now = systemTime(); const nsecs_t inTransaction(mDebugInTransaction); - nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0; + nsecs_t inTransactionDuration = (inTransaction) ? now - inTransaction : 0; /* * Dump library configuration. @@ -6562,7 +6524,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) { IPCThreadState* ipc = IPCThreadState::self(); ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d", - ipc->getCallingPid(), ipc->getCallingUid()); + ipc->getCallingPid(), ipc->getCallingUid()); return PERMISSION_DENIED; } return OK; @@ -6656,9 +6618,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { code == IBinder::SYSPROPS_TRANSACTION) { return OK; } - // Numbers from 1000 to 1045 are currently used for backdoors. The code + // Numbers from 1000 to 1047 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 <= 1046) { + if (code >= 1000 && code <= 1047) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -6678,11 +6640,12 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r CHECK_INTERFACE(ISurfaceComposer, data, reply); IPCThreadState* ipc = IPCThreadState::self(); const int uid = ipc->getCallingUid(); - if (CC_UNLIKELY(uid != AID_SYSTEM - && !PermissionCache::checkCallingPermission(sHardwareTest))) { + if (CC_UNLIKELY(uid != AID_SYSTEM && + !PermissionCache::checkCallingPermission(sHardwareTest))) { const int pid = ipc->getCallingPid(); ALOGE("Permission Denial: " - "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + "can't access SurfaceFlinger pid=%d, uid=%d", + pid, uid); return PERMISSION_DENIED; } int n; @@ -6753,7 +6716,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r n = data.readInt32(); if (n) { // color matrix is sent as a column-major mat4 matrix - for (size_t i = 0 ; i < 4; i++) { + for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { mClientColorMatrix[i][j] = data.readFloat(); } @@ -7200,6 +7163,34 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r mScheduler->setDebugPresentDelay(TimePoint::fromNs(ms2ns(jankDelayMs))); return NO_ERROR; } + // Update WorkDuration + // parameters: + // - (required) i64 minSfNs, used as the late.sf WorkDuration. + // - (required) i64 maxSfNs, used as the early.sf and earlyGl.sf WorkDuration. + // - (required) i64 appDurationNs, used as the late.app, early.app and earlyGl.app + // WorkDuration. + // Usage: + // adb shell service call SurfaceFlinger 1047 i64 12333333 i64 16666666 i64 16666666 + case 1047: { + if (!property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) { + ALOGE("Not supported when work duration is not enabled"); + return INVALID_OPERATION; + } + int64_t minSfNs = 0; + int64_t maxSfNs = 0; + int64_t appDurationNs = 0; + if (data.readInt64(&minSfNs) != NO_ERROR || data.readInt64(&maxSfNs) != NO_ERROR || + data.readInt64(&appDurationNs) != NO_ERROR) { + return BAD_VALUE; + } + mScheduler->reloadPhaseConfiguration(mDisplayModeController + .getActiveMode(mActiveDisplayId) + .fps, + Duration::fromNs(minSfNs), + Duration::fromNs(maxSfNs), + Duration::fromNs(appDurationNs)); + return NO_ERROR; + } } } return err; @@ -7273,9 +7264,7 @@ auto SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) class WindowDisconnector { public: WindowDisconnector(ANativeWindow* window, int api) : mWindow(window), mApi(api) {} - ~WindowDisconnector() { - native_window_api_disconnect(mWindow, mApi); - } + ~WindowDisconnector() { native_window_api_disconnect(mWindow, mApi); } private: ANativeWindow* mWindow; @@ -8386,8 +8375,7 @@ uint32_t SurfaceFlinger::getMaxAcquiredBufferCountForCurrentRefreshRate(uid_t ui } int SurfaceFlinger::getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const { - const auto vsyncConfig = - mScheduler->getVsyncConfiguration().getConfigsForRefreshRate(refreshRate).late; + const auto vsyncConfig = mScheduler->getVsyncConfigsForRefreshRate(refreshRate).late; const auto presentLatency = vsyncConfig.appWorkDuration + vsyncConfig.sfWorkDuration; return calculateMaxAcquiredBufferCount(refreshRate, presentLatency); } @@ -8493,10 +8481,12 @@ void SurfaceFlinger::updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t con } static_cast<void>(mScheduler->schedule([this, displayId = *idOpt, connectedLevel, maxLevel]() { + const bool secure = connectedLevel >= 2 /* HDCP_V1 */; if (const auto display = FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(displayId))) { Mutex::Autolock lock(mStateLock); - display->setSecure(connectedLevel >= 2 /* HDCP_V1 */); + display->setSecure(secure); } + FTL_FAKE_GUARD(kMainThreadContext, mDisplayModeController.setSecure(displayId, secure)); mScheduler->onHdcpLevelsChanged(scheduler::Cycle::Render, displayId, connectedLevel, maxLevel); })); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index f61214cc65..f0298079a0 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -733,14 +733,19 @@ private: void setVirtualDisplayPowerMode(const sp<DisplayDevice>& display, hal::PowerMode mode) REQUIRES(mStateLock, kMainThreadContext); - // Adjusts thread scheduling according to the optimization policy - static void optimizeThreadScheduling( - const char* whence, gui::ISurfaceComposer::OptimizationPolicy optimizationPolicy); + // Returns whether to optimize globally for performance instead of power. + bool shouldOptimizeForPerformance() REQUIRES(mStateLock); + + // Turns on power optimizations, for example when there are no displays to be optimized for + // performance. + static void enablePowerOptimizations(const char* whence); + + // Turns off power optimizations. + static void disablePowerOptimizations(const char* whence); // Enables or disables power optimizations depending on whether there are displays that should // be optimized for performance. - void applyOptimizationPolicy(const char* whence) REQUIRES(kMainThreadContext) - REQUIRES(mStateLock); + void applyOptimizationPolicy(const char* whence) REQUIRES(mStateLock); // Returns the preferred mode for PhysicalDisplayId if the Scheduler has selected one for that // display. Falls back to the display's defaultModeId otherwise. @@ -1334,8 +1339,6 @@ private: HWComposer::HotplugEvent event; }; - bool mIsHdcpViaNegVsync = false; - std::mutex mHotplugMutex; std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex); @@ -1453,8 +1456,6 @@ private: // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; - // Tracks the number of maximum queued buffers by layer owner Uid. - using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>; BufferCountTracker mBufferCountTracker; std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index bf1035149b..ebf451501d 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -161,6 +161,7 @@ void FlagManager::dump(std::string& result) const { DUMP_ACONFIG_FLAG(game_default_frame_rate); DUMP_ACONFIG_FLAG(graphite_renderengine); DUMP_ACONFIG_FLAG(hdcp_level_hal); + DUMP_ACONFIG_FLAG(hdcp_negotiation); DUMP_ACONFIG_FLAG(idle_screen_refresh_rate_timeout); DUMP_ACONFIG_FLAG(latch_unsignaled_with_auto_refresh_changed); DUMP_ACONFIG_FLAG(local_tonemap_screenshots); @@ -259,6 +260,7 @@ FLAG_MANAGER_ACONFIG_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category FLAG_MANAGER_ACONFIG_FLAG(misc1, "") FLAG_MANAGER_ACONFIG_FLAG(vrr_config, "debug.sf.enable_vrr_config") FLAG_MANAGER_ACONFIG_FLAG(hdcp_level_hal, "") +FLAG_MANAGER_ACONFIG_FLAG(hdcp_negotiation, "debug.sf.hdcp_negotiation"); FLAG_MANAGER_ACONFIG_FLAG(add_sf_skipped_frames_to_trace, "") FLAG_MANAGER_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency, "") FLAG_MANAGER_ACONFIG_FLAG(cache_when_source_crop_layer_only_moved, diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 8f361ac610..72b3bc302a 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -95,6 +95,7 @@ public: bool game_default_frame_rate() const; bool graphite_renderengine() const; bool hdcp_level_hal() const; + bool hdcp_negotiation() const; bool idle_screen_refresh_rate_timeout() const; bool latch_unsignaled_with_auto_refresh_changed() const; bool local_tonemap_screenshots() const; diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index d412a19f3c..e8b75cfa6d 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -190,6 +190,16 @@ flag { } # graphite_renderengine_preview_rollout flag { + name: "hdcp_negotiation" + namespace: "core_graphics" + description: "detect secure layers to start HDCP negotiation" + bug: "375340594" + metadata { + purpose: PURPOSE_BUGFIX + } +} # hdcp_negotiation + +flag { name: "increase_missed_frame_jank_threshold" namespace: "core_graphics" description: "Increase the jank threshold to 4 milliseconds" diff --git a/services/surfaceflinger/tests/end2end/.clang-format b/services/surfaceflinger/tests/end2end/.clang-format new file mode 120000 index 0000000000..5e8e20be26 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/.clang-format @@ -0,0 +1 @@ +../../../../../../build/soong/scripts/system-clang-format
\ No newline at end of file diff --git a/services/surfaceflinger/tests/end2end/.clang-tidy b/services/surfaceflinger/tests/end2end/.clang-tidy new file mode 100644 index 0000000000..29f3b4721c --- /dev/null +++ b/services/surfaceflinger/tests/end2end/.clang-tidy @@ -0,0 +1,380 @@ +# Copyright 2025 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FormatStyle: file +InheritParentConfig: true + +# Please add checks explicitly rather than using wildcards like "modernize-*". +# These check names are current as of LLVM 20.0.0, as reported by "clang-tidy --list-checks --checks=*" +# For more information on each check, see https://clang.llvm.org/extra/clang-tidy/checks/list.html. +Checks: + # from android-* + - android-cloexec-accept + - android-cloexec-accept4 + - android-cloexec-creat + - android-cloexec-dup + - android-cloexec-epoll-create + - android-cloexec-epoll-create1 + - android-cloexec-fopen + - android-cloexec-inotify-init + - android-cloexec-inotify-init1 + - android-cloexec-memfd-create + - android-cloexec-open + - android-cloexec-pipe + - android-cloexec-pipe2 + - android-cloexec-socket + - android-comparison-in-temp-failure-retry + + # from bugprone-* + - bugprone-argument-comment + - bugprone-assert-side-effect + - bugprone-assignment-in-if-condition + - bugprone-bad-signal-to-kill-thread + - bugprone-bool-pointer-implicit-conversion + - bugprone-branch-clone + - bugprone-casting-through-void + - bugprone-chained-comparison + - bugprone-compare-pointer-to-member-virtual-function + - bugprone-copy-constructor-init + - bugprone-crtp-constructor-accessibility + - bugprone-dangling-handle + - bugprone-dynamic-static-initializers + - bugprone-easily-swappable-parameters + - bugprone-empty-catch + - bugprone-exception-escape + - bugprone-fold-init-type + - bugprone-forward-declaration-namespace + - bugprone-forwarding-reference-overload + - bugprone-implicit-widening-of-multiplication-result + - bugprone-inaccurate-erase + - bugprone-inc-dec-in-conditions + - bugprone-incorrect-enable-if + - bugprone-incorrect-roundings + - bugprone-infinite-loop + - bugprone-integer-division + - bugprone-lambda-function-name + - bugprone-macro-parentheses + - bugprone-macro-repeated-side-effects + - bugprone-misplaced-operator-in-strlen-in-alloc + - bugprone-misplaced-pointer-arithmetic-in-alloc + - bugprone-misplaced-widening-cast + - bugprone-move-forwarding-reference + - bugprone-multi-level-implicit-pointer-conversion + - bugprone-multiple-new-in-one-expression + - bugprone-multiple-statement-macro + - bugprone-narrowing-conversions + - bugprone-no-escape + - bugprone-non-zero-enum-to-bool-conversion + - bugprone-not-null-terminated-result + - bugprone-optional-value-conversion + - bugprone-parent-virtual-call + - bugprone-pointer-arithmetic-on-polymorphic-object + - bugprone-posix-return + - bugprone-redundant-branch-condition + - bugprone-reserved-identifier + - bugprone-return-const-ref-from-parameter + - bugprone-shared-ptr-array-mismatch + - bugprone-signal-handler + - bugprone-signed-char-misuse + - bugprone-sizeof-container + - bugprone-sizeof-expression + - bugprone-spuriously-wake-up-functions + - bugprone-standalone-empty + - bugprone-string-constructor + - bugprone-string-integer-assignment + - bugprone-string-literal-with-embedded-nul + - bugprone-stringview-nullptr + - bugprone-suspicious-enum-usage + - bugprone-suspicious-include + - bugprone-suspicious-memory-comparison + - bugprone-suspicious-memset-usage + - bugprone-suspicious-missing-comma + - bugprone-suspicious-realloc-usage + - bugprone-suspicious-semicolon + - bugprone-suspicious-string-compare + - bugprone-suspicious-stringview-data-usage + - bugprone-swapped-arguments + - bugprone-switch-missing-default-case + - bugprone-terminating-continue + - bugprone-throw-keyword-missing + - bugprone-too-small-loop-variable + - bugprone-unchecked-optional-access + - bugprone-undefined-memory-manipulation + - bugprone-undelegated-constructor + - bugprone-unhandled-exception-at-new + - bugprone-unhandled-self-assignment + - bugprone-unique-ptr-array-mismatch + - bugprone-unsafe-functions + - bugprone-unused-local-non-trivial-variable + - bugprone-unused-raii + - bugprone-unused-return-value + - bugprone-use-after-move + - bugprone-virtual-near-miss + + # from cert-* + - cert-con36-c + - cert-con54-cpp + - cert-ctr56-cpp + - cert-dcl03-c + - cert-dcl16-c + - cert-dcl37-c + - cert-dcl50-cpp + - cert-dcl51-cpp + - cert-dcl54-cpp + - cert-dcl58-cpp + - cert-dcl59-cpp + - cert-env33-c + - cert-err09-cpp + - cert-err33-c + - cert-err34-c + - cert-err52-cpp + - cert-err58-cpp + - cert-err60-cpp + - cert-err61-cpp + - cert-exp42-c + - cert-fio38-c + - cert-flp30-c + - cert-flp37-c + - cert-int09-c + - cert-mem57-cpp + - cert-msc24-c + - cert-msc30-c + - cert-msc32-c + - cert-msc33-c + - cert-msc50-cpp + - cert-msc51-cpp + - cert-msc54-cpp + - cert-oop11-cpp + - cert-oop54-cpp + - cert-oop57-cpp + - cert-oop58-cpp + - cert-pos44-c + - cert-pos47-c + - cert-sig30-c + - cert-str34-c + + # from concurrency-* + - concurrency-mt-unsafe + - concurrency-thread-canceltype-asynchronous + + # from cppcoreguidelines-* + - cppcoreguidelines-avoid-c-arrays + - cppcoreguidelines-avoid-capturing-lambda-coroutines + - cppcoreguidelines-avoid-const-or-ref-data-members + - cppcoreguidelines-avoid-do-while + - cppcoreguidelines-avoid-goto + - cppcoreguidelines-avoid-magic-numbers + - cppcoreguidelines-avoid-non-const-global-variables + - cppcoreguidelines-avoid-reference-coroutine-parameters + - cppcoreguidelines-c-copy-assignment-signature + - cppcoreguidelines-explicit-virtual-functions + - cppcoreguidelines-init-variables + - cppcoreguidelines-interfaces-global-init + - cppcoreguidelines-macro-to-enum + - cppcoreguidelines-macro-usage + - cppcoreguidelines-misleading-capture-default-by-value + - cppcoreguidelines-missing-std-forward + - cppcoreguidelines-narrowing-conversions + - cppcoreguidelines-no-malloc + - cppcoreguidelines-no-suspend-with-lock + - cppcoreguidelines-noexcept-destructor + - cppcoreguidelines-noexcept-move-operations + - cppcoreguidelines-noexcept-swap + - cppcoreguidelines-non-private-member-variables-in-classes + - cppcoreguidelines-owning-memory + - cppcoreguidelines-prefer-member-initializer + - cppcoreguidelines-pro-bounds-array-to-pointer-decay + - cppcoreguidelines-pro-bounds-constant-array-index + - cppcoreguidelines-pro-bounds-pointer-arithmetic + - cppcoreguidelines-pro-type-const-cast + - cppcoreguidelines-pro-type-cstyle-cast + - cppcoreguidelines-pro-type-member-init + - cppcoreguidelines-pro-type-reinterpret-cast + - cppcoreguidelines-pro-type-static-cast-downcast + - cppcoreguidelines-pro-type-union-access + - cppcoreguidelines-pro-type-vararg + - cppcoreguidelines-rvalue-reference-param-not-moved + - cppcoreguidelines-slicing + - cppcoreguidelines-special-member-functions + - cppcoreguidelines-use-default-member-init + - cppcoreguidelines-virtual-class-destructor + + # from google-* + - google-build-explicit-make-pair + - google-build-namespaces + - google-build-using-namespace + - google-default-arguments + - google-explicit-constructor + - google-global-names-in-headers + - google-objc-avoid-nsobject-new + - google-objc-avoid-throwing-exception + - google-objc-function-naming + - google-objc-global-variable-declaration + - google-readability-avoid-underscore-in-googletest-name + - google-readability-braces-around-statements + - google-readability-casting + - google-readability-function-size + - google-readability-namespace-comments + - google-readability-todo + - google-runtime-int + - google-runtime-operator + - google-upgrade-googletest-case + + # from misc-* + - misc-confusable-identifiers + - misc-const-correctness + - misc-coroutine-hostile-raii + - misc-definitions-in-headers + - misc-header-include-cycle + - misc-include-cleaner + - misc-misleading-bidirectional + - misc-misleading-identifier + - misc-misplaced-const + - misc-new-delete-overloads + - misc-no-recursion + - misc-non-copyable-objects + - misc-non-private-member-variables-in-classes + - misc-redundant-expression + - misc-static-assert + - misc-throw-by-value-catch-by-reference + - misc-unconventional-assign-operator + - misc-uniqueptr-reset-release + - misc-unused-alias-decls + - misc-unused-parameters + - misc-unused-using-decls + - misc-use-anonymous-namespace + - misc-use-internal-linkage + + # from modernize-* + - modernize-avoid-bind + - modernize-avoid-c-arrays + - modernize-concat-nested-namespaces + - modernize-deprecated-headers + - modernize-deprecated-ios-base-aliases + - modernize-loop-convert + - modernize-macro-to-enum + - modernize-make-shared + - modernize-make-unique + - modernize-min-max-use-initializer-list + - modernize-pass-by-value + - modernize-raw-string-literal + - modernize-redundant-void-arg + - modernize-replace-auto-ptr + - modernize-replace-disallow-copy-and-assign-macro + - modernize-replace-random-shuffle + - modernize-return-braced-init-list + - modernize-shrink-to-fit + - modernize-type-traits + - modernize-unary-static-assert + - modernize-use-auto + - modernize-use-bool-literals + - modernize-use-constraints + - modernize-use-default-member-init + - modernize-use-designated-initializers + - modernize-use-emplace + - modernize-use-equals-default + - modernize-use-equals-delete + - modernize-use-nodiscard + - modernize-use-noexcept + - modernize-use-nullptr + - modernize-use-override + - modernize-use-ranges + - modernize-use-starts-ends-with + - modernize-use-std-format + - modernize-use-std-numbers + - modernize-use-std-print + - modernize-use-trailing-return-type + - modernize-use-transparent-functors + - modernize-use-uncaught-exceptions + - modernize-use-using + + # from performance-* + - performance-avoid-endl + - performance-enum-size + - performance-faster-string-find + - performance-for-range-copy + - performance-implicit-conversion-in-loop + - performance-inefficient-algorithm + - performance-inefficient-string-concatenation + - performance-inefficient-vector-operation + - performance-move-const-arg + - performance-move-constructor-init + - performance-no-automatic-move + - performance-no-int-to-ptr + - performance-noexcept-destructor + - performance-noexcept-move-constructor + - performance-noexcept-swap + - performance-trivially-destructible + - performance-type-promotion-in-math-fn + - performance-unnecessary-copy-initialization + - performance-unnecessary-value-param + + # from portability-* + - portability-restrict-system-includes + - portability-simd-intrinsics + - portability-std-allocator-const + + # from readability-* + - readability-avoid-const-params-in-decls + - readability-avoid-nested-conditional-operator + - readability-avoid-return-with-void-value + - readability-avoid-unconditional-preprocessor-if + - readability-braces-around-statements + - readability-const-return-type + - readability-container-contains + - readability-container-data-pointer + - readability-container-size-empty + - readability-convert-member-functions-to-static + - readability-delete-null-pointer + - readability-duplicate-include + - readability-else-after-return + - readability-enum-initial-value + - readability-function-cognitive-complexity + - readability-function-size + - readability-identifier-length + - readability-identifier-naming + - readability-implicit-bool-conversion + - readability-inconsistent-declaration-parameter-name + - readability-isolate-declaration + - readability-magic-numbers + - readability-make-member-function-const + - readability-math-missing-parentheses + - readability-misleading-indentation + - readability-misplaced-array-index + - readability-named-parameter + - readability-non-const-parameter + - readability-operators-representation + - readability-qualified-auto + - readability-redundant-access-specifiers + - readability-redundant-casting + - readability-redundant-control-flow + - readability-redundant-declaration + - readability-redundant-function-ptr-dereference + - readability-redundant-inline-specifier + - readability-redundant-member-init + - readability-redundant-preprocessor + - readability-redundant-smartptr-get + - readability-redundant-string-cstr + - readability-redundant-string-init + - readability-reference-to-constructed-temporary + - readability-simplify-boolean-expr + - readability-simplify-subscript-expr + - readability-static-accessed-through-instance + - readability-static-definition-in-anonymous-namespace + - readability-string-compare + - readability-suspicious-call-argument + - readability-uniqueptr-delete-release + - readability-uppercase-literal-suffix + - readability-use-anyofallof + - readability-use-std-min-max diff --git a/services/surfaceflinger/tests/end2end/.clangd b/services/surfaceflinger/tests/end2end/.clangd new file mode 100644 index 0000000000..d64d2f8a55 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/.clangd @@ -0,0 +1,20 @@ +# Copyright 2025 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Diagnostics: + UnusedIncludes: Strict + MissingIncludes: Strict + ClangTidy: + FastCheckFilter: None + # See the .clang-tidy files for additional configuration diff --git a/services/surfaceflinger/tests/end2end/Android.bp b/services/surfaceflinger/tests/end2end/Android.bp new file mode 100644 index 0000000000..8810330179 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/Android.bp @@ -0,0 +1,68 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_test { + name: "surfaceflinger_end2end_tests", + test_suites: ["device-tests"], + require_root: true, + + cpp_std: "experimental", + cflags: [ + "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", + "-DNODISCARD_EXPECTED", + "-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS", + "-Wall", + "-Wconversion", + "-Werror", + "-Wextra", + "-Wformat", + "-Wno-non-virtual-dtor", + "-Wno-sign-compare", + "-Wno-sign-conversion", + "-Wshadow", + "-Wthread-safety", + "-Wunreachable-code", + "-Wunused", + ], + srcs: [ + "main.cpp", + "test_framework/core/TestService.cpp", + "test_framework/fake_hwc3/Hwc3Composer.cpp", + "test_framework/fake_hwc3/Hwc3Controller.cpp", + "test_framework/surfaceflinger/SFController.cpp", + "tests/Placeholder_test.cpp", + ], + tidy: true, + tidy_flags: [ + "--config=", // Use the .clang-tidy closest to each source file for the configuration + ], + tidy_checks_as_errors: [ + "*", + ], + include_dirs: [ + "frameworks/native/include", + ], + local_include_dirs: ["."], + shared_libs: [ + "libbase", + "libbinder", + "libbinder_ndk", + "libcutils", + "libgui", + "libsync", + "libui", + "libutils", + ], + static_libs: [ + "android.hardware.common-V2-ndk", + "android.hardware.graphics.common-V6-ndk", + "android.hardware.graphics.composer3-V3-ndk", + "libgtest", + ], +} diff --git a/services/surfaceflinger/tests/end2end/AndroidTest.xml b/services/surfaceflinger/tests/end2end/AndroidTest.xml new file mode 100644 index 0000000000..99cb7b3fee --- /dev/null +++ b/services/surfaceflinger/tests/end2end/AndroidTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2025 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Configuration for surfaceflinger_end2end_tests"> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" /> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <!-- Stop everything to run SurfaceFlinger in isolation, with relaxed SELinux permissions --> + <option name="teardown-command" value="stop" /> + <option name="teardown-command" value="setprop debug.sf.nobootanimation 1" /> + + <!-- Restart everything with normal settings after the test finishes. --> + <option name="teardown-command" value="stop" /> + <option name="teardown-command" value="setprop debug.sf.nobootanimation 0" /> + <option name="teardown-command" value="setprop debug.sf.hwc_service_name default" /> + <option name="teardown-command" value="start" /> + </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="surfaceflinger_end2end_tests->/data/local/tests/surfaceflinger_end2end_tests" /> + </target_preparer> + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tests" /> + <option name="module-name" value="surfaceflinger_end2end_tests" /> + <option name="native-test-timeout" value="15m"/> + </test> +</configuration> diff --git a/services/surfaceflinger/tests/end2end/OWNERS b/services/surfaceflinger/tests/end2end/OWNERS new file mode 100644 index 0000000000..aa5b5957a2 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/OWNERS @@ -0,0 +1,7 @@ +lpique@google.com # primary POC +bwidawsk@google.com +ddavenport@google.com +markyacoub@google.com +sukoo@google.com + +include platform/frameworks/native:/services/surfaceflinger/OWNERS diff --git a/services/surfaceflinger/tests/end2end/README.md b/services/surfaceflinger/tests/end2end/README.md new file mode 100644 index 0000000000..2f58cec5c7 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/README.md @@ -0,0 +1,75 @@ +# `surfaceflinger_end2end_tests` + +Tests to cover end to end testing of SurfaceFlinger. + +In particular the test framework allows you to simulate various display +configurations, so the test can confirm displays are handled correctly. + +## Quick Useful info + +### Running the tests + +At present the tests should run on any target, though the typical target would +be a [Cuttlefish](https://source.android.com/docs/devices/cuttlefish) VM +target such as `aosp_cf_x86_64_phone`. + +At some future time the test may be rewritten to require +[`vkms`](https://dri.freedesktop.org/docs/drm/gpu/vkms.html) and +[`drm_hwcomposer`](https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer) + +``` +atest surfaceflinger_end2end_tests +``` + +You can also run the google test binary directly. However you will also need +to run a few other set-up and tear-down commands that are part of the +AndroidTest.xml configuration, so that SurfaceFlinger can be used run isolation +from the rest of the system. + +``` +# Set-up +adb root +adb shell stop +adb shell setenforce 0 +adb shell setprop debug.sf.nobootanimation 1 + +# Sync and run the test +adb sync data +adb shell data/nativetest64/surfaceflinger_end2end_tests/surfaceflinger_end2end_tests + +# Tear-down +adb shell stop +adb shell setenforce 1 +adb shell setprop debug.sf.nobootanimation 0 +adb shell setprop debug.sf.hwc_service_name default +``` + +### Manual clang-tidy checks via Soong + +At present Android does not run the clang-tidy checks as part of its +presubmit checks. + +You can run them through the build system by using phony target that are +automatically created for each source subdirectory. + +For the code under `frameworks/native/services/surfaceflinger/tests/end2end`, +you would build: + +``` +m tidy-frameworks-native-services-surfaceflinger-tests-end2end +``` + +For more information see the build documentation: + +* <https://android.googlesource.com/platform/build/soong/+/main/docs/tidy.md#the-tidy_directory-targets> + +### Seeing clang-tidy checks in your editor + +If your editor supports using [`clangd`](https://clangd.llvm.org/) as a +C++ language server, you can build and export a compilation database using +Soong. With the local `.clangd` configuration file, you should see the same +checks in editor, along with all the other checks `clangd` runs. + +See the build documentation for the compilation database instructions: + +https://android.googlesource.com/platform/build/soong/+/main/docs/compdb.md diff --git a/services/surfaceflinger/tests/end2end/main.cpp b/services/surfaceflinger/tests/end2end/main.cpp new file mode 100644 index 0000000000..ddf690021d --- /dev/null +++ b/services/surfaceflinger/tests/end2end/main.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <cstdlib> +#include <string_view> + +#include <android-base/logging.h> +#include <android/binder_process.h> +#include <gtest/gtest.h> + +namespace { + +void init(int argc, char** argv) { + using namespace std::string_view_literals; + + ::testing::InitGoogleTest(&argc, argv); + ::android::base::InitLogging(argv, android::base::StderrLogger); + + auto minimumSeverity = android::base::INFO; + for (int i = 1; i < argc; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const std::string_view arg = argv[i]; + + if (arg == "-v"sv) { + minimumSeverity = android::base::DEBUG; + } else if (arg == "-vv"sv) { + minimumSeverity = android::base::VERBOSE; + } + } + ::android::base::SetMinimumLogSeverity(minimumSeverity); +} + +} // namespace + +auto main(int argc, char** argv) -> int { + init(argc, argv); + + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + return RUN_ALL_TESTS(); +}
\ No newline at end of file diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h new file mode 100644 index 0000000000..c3a535e10f --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h @@ -0,0 +1,29 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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> + +namespace android::surfaceflinger::tests::end2end::test_framework::core { + +struct DisplayConfiguration final { + using Id = int64_t; + + Id id{}; +}; + +} // namespace android::surfaceflinger::tests::end2end::test_framework::core diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp new file mode 100644 index 0000000000..0531f18527 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <memory> +#include <span> +#include <string> +#include <utility> +#include <vector> + +#include <android-base/expected.h> +#include <ftl/ignore.h> + +#include "test_framework/core/DisplayConfiguration.h" +#include "test_framework/core/TestService.h" +#include "test_framework/fake_hwc3/Hwc3Controller.h" +#include "test_framework/surfaceflinger/SFController.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::core { + +struct TestService::Passkey final {}; + +auto TestService::startWithDisplays(const std::vector<DisplayConfiguration>& displays) + -> base::expected<std::unique_ptr<TestService>, std::string> { + using namespace std::string_literals; + + auto service = std::make_unique<TestService>(TestService::Passkey{}); + if (service == nullptr) { + return base::unexpected("Failed to construct the TestService instance."s); + } + + if (auto result = service->init(displays); !result) { + return base::unexpected("Failed to init the TestService instance: "s + result.error()); + } + + return service; +} + +TestService::TestService(Passkey passkey) { + ftl::ignore(passkey); +} + +auto TestService::init(std::span<const DisplayConfiguration> displays) + -> base::expected<void, std::string> { + using namespace std::string_literals; + + auto hwcResult = fake_hwc3::Hwc3Controller::make(displays); + if (!hwcResult) { + return base::unexpected(std::move(hwcResult).error()); + } + auto hwc = *std::move(hwcResult); + + auto flingerResult = surfaceflinger::SFController::make(); + if (!flingerResult) { + return base::unexpected(std::move(flingerResult).error()); + } + auto flinger = *std::move(flingerResult); + + surfaceflinger::SFController::useHwcService(fake_hwc3::Hwc3Controller::getServiceName()); + + if (auto result = flinger->startAndConnect(); !result) { + return base::unexpected(std::move(result).error()); + } + + mHwc = std::move(hwc); + mFlinger = std::move(flinger); + return {}; +} + +} // namespace android::surfaceflinger::tests::end2end::test_framework::core diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h new file mode 100644 index 0000000000..21e6426406 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h @@ -0,0 +1,76 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <span> +#include <string> +#include <vector> + +#include <android-base/expected.h> +#include <android-base/logging.h> + +#include "test_framework/core/DisplayConfiguration.h" + +namespace android::surfaceflinger::tests::end2end::test_framework { + +namespace surfaceflinger { + +class SFController; + +} // namespace surfaceflinger + +namespace fake_hwc3 { + +class Hwc3Controller; + +} // namespace fake_hwc3 + +namespace core { + +class TestService final { + struct Passkey; // Uses the passkey idiom to restrict construction. + + public: + // Constructs the test service, and starts it with the given displays as connected at boot. + [[nodiscard]] static auto startWithDisplays(const std::vector<DisplayConfiguration>& displays) + -> base::expected<std::unique_ptr<TestService>, std::string>; + + explicit TestService(Passkey passkey); + + // Obtains the HWC3 back-end controller + [[nodiscard]] auto hwc() -> fake_hwc3::Hwc3Controller& { + CHECK(mHwc); + return *mHwc; + } + + // Obtains the SurfaceFlinger front-end controller + [[nodiscard]] auto flinger() -> surfaceflinger::SFController& { + CHECK(mFlinger); + return *mFlinger; + } + + private: + [[nodiscard]] auto init(std::span<const DisplayConfiguration> displays) + -> base::expected<void, std::string>; + + std::shared_ptr<fake_hwc3::Hwc3Controller> mHwc; + std::shared_ptr<surfaceflinger::SFController> mFlinger; +}; + +} // namespace core +} // namespace android::surfaceflinger::tests::end2end::test_framework diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp new file mode 100644 index 0000000000..5349ef0b58 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <cstdint> +#include <memory> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +#include <aidl/android/hardware/graphics/composer3/BnComposer.h> +#include <aidl/android/hardware/graphics/composer3/Capability.h> +#include <aidl/android/hardware/graphics/composer3/IComposer.h> +#include <aidl/android/hardware/graphics/composer3/PowerMode.h> + +#include <android-base/expected.h> +#include <android-base/logging.h> +#include <android/binder_auto_utils.h> +#include <android/binder_interface_utils.h> +#include <android/binder_status.h> +#include <ftl/ignore.h> + +#include "test_framework/core/DisplayConfiguration.h" +#include "test_framework/fake_hwc3/Hwc3Composer.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 { + +class Hwc3Composer::Hwc3ComposerImpl final + : public aidl::android::hardware::graphics::composer3::BnComposer { + using Capability = aidl::android::hardware::graphics::composer3::Capability; + using IComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient; + using Hwc3PowerMode = aidl::android::hardware::graphics::composer3::PowerMode; + + // begin IComposer overrides + + auto dump(int dumpFd, const char** args, uint32_t num_args) -> binder_status_t override { + UNIMPLEMENTED(WARNING); + ftl::ignore(dumpFd, args, num_args); + return static_cast<binder_status_t>(STATUS_NO_MEMORY); + } + + auto createClient(std::shared_ptr<IComposerClient>* out_client) -> ndk::ScopedAStatus override { + UNIMPLEMENTED(WARNING); + ftl::ignore(out_client); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + IComposer::EX_NO_RESOURCES, "Client failed to initialize"); + } + + auto getCapabilities(std::vector<Capability>* out_capabilities) -> ndk::ScopedAStatus override { + UNIMPLEMENTED(WARNING); + ftl::ignore(out_capabilities); + return ndk::ScopedAStatus::ok(); + } + + // end IComposer overrides +}; + +struct Hwc3Composer::Passkey final {}; + +auto Hwc3Composer::getServiceName(std::string_view baseServiceName) -> std::string { + return Hwc3ComposerImpl::makeServiceName(baseServiceName); +} + +auto Hwc3Composer::make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string> { + using namespace std::string_literals; + + auto composer = std::make_shared<Hwc3Composer>(Passkey{}); + if (composer == nullptr) { + return base::unexpected("Failed to construct the Hwc3Composer instance."s); + } + + if (auto result = composer->init(); !result) { + return base::unexpected("Failed to init the Hwc3Composer instance: "s + result.error()); + } + + return composer; +} + +Hwc3Composer::Hwc3Composer(Hwc3Composer::Passkey passkey) { + ftl::ignore(passkey); +} + +auto Hwc3Composer::init() -> base::expected<void, std::string> { + using namespace std::string_literals; + + auto impl = ndk::SharedRefBase::make<Hwc3ComposerImpl>(); + if (!impl) { + return base::unexpected("Failed to construct the Hwc3ComposerImpl instance."s); + } + + mImpl = std::move(impl); + + return {}; +} + +auto Hwc3Composer::getComposer() -> std::shared_ptr<Hwc3IComposer> { + return mImpl; +} + +void Hwc3Composer::addDisplay(const core::DisplayConfiguration& display) { + UNIMPLEMENTED(WARNING); + ftl::ignore(display, mImpl); +} + +void Hwc3Composer::removeDisplay(core::DisplayConfiguration::Id displayId) { + UNIMPLEMENTED(WARNING); + ftl::ignore(displayId, mImpl); +} + +} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h new file mode 100644 index 0000000000..6d6b7374c6 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h @@ -0,0 +1,61 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <string> +#include <string_view> + +#include <aidl/android/hardware/graphics/composer3/IComposer.h> +#include <android-base/expected.h> + +#include "test_framework/core/DisplayConfiguration.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 { + +class Hwc3Composer final { + struct Passkey; // Uses the passkey idiom to restrict construction. + + class Hwc3ComposerImpl; // An internal class implements the AIDL interface. + + public: + using Hwc3IComposer = aidl::android::hardware::graphics::composer3::IComposer; + + // Gets the full qualified service name given a base name for the service. + [[nodiscard]] static auto getServiceName(std::string_view baseServiceName) -> std::string; + + // Constructs a Hwc3Composer instance. + [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string>; + + explicit Hwc3Composer(Passkey passkey); + + // Obtains the AIDL composer3::IComposer interface for the internal instance. + [[nodiscard]] auto getComposer() -> std::shared_ptr<Hwc3IComposer>; + + // Adds a display to the composer. This will sent a hotplug connect event. + void addDisplay(const core::DisplayConfiguration& display); + + // Removes a display from the composer. This will sent a hotplug disconnect event. + void removeDisplay(core::DisplayConfiguration::Id displayId); + + private: + [[nodiscard]] auto init() -> base::expected<void, std::string>; + + std::shared_ptr<Hwc3ComposerImpl> mImpl; +}; + +} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp new file mode 100644 index 0000000000..ea985c09b4 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <memory> +#include <span> +#include <string> +#include <utility> + +#include <android-base/expected.h> +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_stability.h> +#include <android/binder_status.h> +#include <fmt/format.h> +#include <ftl/ignore.h> + +#include "test_framework/core/DisplayConfiguration.h" +#include "test_framework/fake_hwc3/Hwc3Composer.h" +#include "test_framework/fake_hwc3/Hwc3Controller.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 { + +struct Hwc3Controller::Passkey final {}; + +auto Hwc3Controller::make(std::span<const core::DisplayConfiguration> displays) + -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string> { + using namespace std::string_literals; + + auto controller = std::make_unique<Hwc3Controller>(Passkey{}); + if (controller == nullptr) { + return base::unexpected("Failed to construct the Hwc3Controller instance"s); + } + + if (auto result = controller->init(displays); !result) { + return base::unexpected("Failed to construct the Hwc3Controller instance: "s + + result.error()); + } + + return controller; +} + +Hwc3Controller::Hwc3Controller(Passkey passkey) { + ftl::ignore(passkey); +} + +auto Hwc3Controller::init(const std::span<const core::DisplayConfiguration> displays) + -> base::expected<void, std::string> { + using namespace std::string_literals; + + auto qualifiedServiceName = Hwc3Composer::getServiceName(baseServiceName); + + auto composerResult = Hwc3Composer::make(); + if (!composerResult) { + return base::unexpected(std::move(composerResult).error()); + } + auto composer = *std::move(composerResult); + + for (const auto& display : displays) { + composer->addDisplay(display); + } + + auto binder = composer->getComposer()->asBinder(); + + // This downgrade allows us to use the fake service name without it being defined in the + // VINTF manifest. + AIBinder_forceDowngradeToLocalStability(binder.get()); + + auto status = AServiceManager_addService(binder.get(), qualifiedServiceName.c_str()); + if (status != STATUS_OK) { + return base::unexpected(fmt::format("Failed to register service {}. Error {}.", + qualifiedServiceName, status)); + } + LOG(INFO) << "Registered service " << qualifiedServiceName << ". Error: " << status; + + mComposer = std::move(composer); + return {}; +} + +auto Hwc3Controller::getServiceName() -> std::string { + return Hwc3Composer::getServiceName(baseServiceName); +} + +void Hwc3Controller::addDisplay(const core::DisplayConfiguration& config) { + CHECK(mComposer); + mComposer->addDisplay(config); +} + +void Hwc3Controller::removeDisplay(core::DisplayConfiguration::Id displayId) { + CHECK(mComposer); + mComposer->removeDisplay(displayId); +} + +} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h new file mode 100644 index 0000000000..e53d2cfc48 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h @@ -0,0 +1,59 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <span> +#include <string> + +#include <android-base/expected.h> + +#include "test_framework/core/DisplayConfiguration.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 { + +class Hwc3Composer; + +class Hwc3Controller final { + struct Passkey; // Uses the passkey idiom to restrict construction. + + public: + // Gets the service name for the HWC3 instance that will be created and registered + [[nodiscard]] static auto getServiceName() -> std::string; + + // Makes the HWC3 controller instance. + [[nodiscard]] static auto make(std::span<const core::DisplayConfiguration> displays) + -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string>; + + explicit Hwc3Controller(Passkey passkey); + + // Adds a new display to the HWC3, which will become a hotplug connect event. + void addDisplay(const core::DisplayConfiguration& config); + + // Removes a new display from the HWC3, which will become a hotplug disconnect event. + void removeDisplay(core::DisplayConfiguration::Id displayId); + + private: + static constexpr std::string baseServiceName = "fake"; + + [[nodiscard]] auto init(std::span<const core::DisplayConfiguration> displays) + -> base::expected<void, std::string>; + + std::shared_ptr<Hwc3Composer> mComposer; +}; + +} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp new file mode 100644 index 0000000000..1cf49c5750 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <cstdlib> +#include <memory> +#include <string> +#include <string_view> +#include <thread> +#include <utility> + +#include <android-base/expected.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/gui/ISurfaceComposer.h> +#include <binder/IBinder.h> +#include <binder/IInterface.h> +#include <binder/IServiceManager.h> +#include <binder/Status.h> +#include <ftl/finalizer.h> +#include <ftl/ignore.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <utils/String16.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + +#include "test_framework/surfaceflinger/SFController.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger { + +namespace { + +auto waitForSurfaceFlingerAIDL() -> sp<gui::ISurfaceComposer> { + constexpr auto kTimeout = std::chrono::seconds(30); + constexpr auto kSurfaceFlingerServiceName = "SurfaceFlingerAIDL"; + const sp<android::IServiceManager> serviceManager(android::defaultServiceManager()); + const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout; + + LOG(INFO) << "Waiting " << kTimeout << " for service manager registration...."; + sp<android::IBinder> flingerService; + while (flingerService == nullptr) { + if (std::chrono::steady_clock::now() > kTimeoutAfter) { + LOG(INFO) << "... Timeout!"; + return nullptr; + } + + constexpr auto sleepTime = std::chrono::milliseconds(10); + std::this_thread::sleep_for(sleepTime); + flingerService = serviceManager->checkService(String16(kSurfaceFlingerServiceName)); + } + LOG(INFO) << "Obtained surfaceflinger interface from service manager."; + + return interface_cast<gui::ISurfaceComposer>(flingerService); +} + +} // namespace + +struct SFController::Passkey final {}; + +void SFController::useHwcService(std::string_view fqn) { + base::SetProperty("debug.sf.hwc_service_name", std::string(fqn)); +} + +auto SFController::make() -> base::expected<std::shared_ptr<SFController>, std::string> { + using namespace std::string_literals; + + auto controller = std::make_unique<SFController>(Passkey{}); + if (controller == nullptr) { + return base::unexpected("Failed to construct the SFController instance."s); + } + + if (auto result = controller->init(); !result) { + return base::unexpected("Failed to init the SFController instance: "s + result.error()); + } + + return controller; +} + +SFController::SFController(Passkey passkey) { + ftl::ignore(passkey); +} + +auto SFController::init() -> base::expected<void, std::string> { + LOG(INFO) << "Stopping everything to prepare for tests"; + // NOLINTBEGIN(cert-env33-c) + system("stop"); + // NOLINTEND(cert-env33-c) + + mCleanup = ftl::Finalizer([this]() { stop(); }); + + return {}; +} + +auto SFController::startAndConnect() -> base::expected<void, std::string> { + using namespace std::string_literals; + + start(); + + LOG(VERBOSE) << "Getting ISurfaceComposer...."; + auto surfaceComposerAidl = waitForSurfaceFlingerAIDL(); + if (surfaceComposerAidl == nullptr) { + return base::unexpected("Failed to obtain the surfaceComposerAidl interface."s); + } + LOG(VERBOSE) << "Getting ISurfaceComposerClient...."; + sp<gui::ISurfaceComposerClient> surfaceComposerClientAidl; + if (!surfaceComposerAidl->createConnection(&surfaceComposerClientAidl).isOk()) { + return base::unexpected("Failed to obtain the surfaceComposerClientAidl interface."s); + } + if (surfaceComposerClientAidl == nullptr) { + return base::unexpected("Failed to obtain a valid surfaceComposerClientAidl interface."s); + } + auto surfaceComposerClient = sp<SurfaceComposerClient>::make(surfaceComposerClientAidl); + if (surfaceComposerClient == nullptr) { + return base::unexpected( + "Failed to construct a surfaceComposerClient around the aidl interface."s); + } + + mSurfaceComposerAidl = std::move(surfaceComposerAidl); + mSurfaceComposerClientAidl = std::move(surfaceComposerClientAidl); + mSurfaceComposerClient = std::move(surfaceComposerClient); + + LOG(INFO) << "Connected to surfaceflinger"; + return {}; +} + +void SFController::start() { + LOG(INFO) << "Starting surfaceflinger"; + // NOLINTBEGIN(cert-env33-c) + system("start surfaceflinger"); + // NOLINTEND(cert-env33-c) +} + +void SFController::stop() { + LOG(INFO) << "Stopping surfaceflinger"; + // NOLINTBEGIN(cert-env33-c) + system("stop surfaceflinger"); + // NOLINTEND(cert-env33-c) + + if (mSurfaceComposerAidl != nullptr) { + LOG(INFO) << "Waiting for SF AIDL interface to die"; + + constexpr auto kTimeout = std::chrono::seconds(30); + const auto binder = android::gui::ISurfaceComposer::asBinder(mSurfaceComposerAidl); + const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout; + + while (binder->isBinderAlive()) { + if (std::chrono::steady_clock::now() > kTimeoutAfter) { + LOG(INFO) << "... Timeout!"; + break; + } + + ftl::ignore = binder->pingBinder(); + + constexpr auto kPollInterval = std::chrono::milliseconds(10); + std::this_thread::sleep_for(kPollInterval); + } + + constexpr auto kShutdownWait = std::chrono::milliseconds(500); + std::this_thread::sleep_for(kShutdownWait); + } + + mSurfaceComposerClient = nullptr; + mSurfaceComposerClientAidl = nullptr; + mSurfaceComposerAidl = nullptr; +} + +} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h new file mode 100644 index 0000000000..58bac9197f --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h @@ -0,0 +1,70 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <string> +#include <string_view> + +#include <android-base/expected.h> +#include <ftl/finalizer.h> +#include <utils/StrongPointer.h> + +namespace android::gui { + +class ISurfaceComposer; +class ISurfaceComposerClient; + +} // namespace android::gui + +namespace android { + +class SurfaceComposerClient; + +} // namespace android + +namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger { + +class SFController final { + struct Passkey; // Uses the passkey idiom to restrict construction. + + public: + // Sets a property so that SurfaceFlinger uses the named HWC service. + static void useHwcService(std::string_view fqn); + + // Makes an instance of the SFController. + [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<SFController>, std::string>; + + explicit SFController(Passkey pass); + + // Starts SurfaceFlinger and establishes the AIDL interface connections. + [[nodiscard]] auto startAndConnect() -> base::expected<void, std::string>; + + private: + [[nodiscard]] auto init() -> base::expected<void, std::string>; + static void start(); + void stop(); + + sp<gui::ISurfaceComposer> mSurfaceComposerAidl; + sp<gui::ISurfaceComposerClient> mSurfaceComposerClientAidl; + sp<SurfaceComposerClient> mSurfaceComposerClient; + + // Finalizers should be last so their destructors are invoked first. + ftl::FinalizerFtl mCleanup; +}; + +} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger diff --git a/services/surfaceflinger/tests/end2end/tests/.clang-tidy b/services/surfaceflinger/tests/end2end/tests/.clang-tidy new file mode 100644 index 0000000000..4924c466c0 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/tests/.clang-tidy @@ -0,0 +1,32 @@ +# Copyright 2025 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FormatStyle: file +InheritParentConfig: true + +# Note: For tests, we are actually turning off certain checks enabled for the +# non-test code in the parent .clang-tidy file. +Checks: + - -cppcoreguidelines-avoid-magic-numbers # Allow tests to use magic numbers. + - -cppcoreguidelines-avoid-goto # Google Test macros use goto. + - -cppcoreguidelines-avoid-non-const-global-variables # Google Test macros define global variables. + - -cppcoreguidelines-macro-usage # Google Benchmark defines function-like macros. + - -cppcoreguidelines-owning-memory # Google Test macros use operator new directly. + - -google-runtime-int # Tests might intentionally use the base "short"/"long" types and not want to use "int16"/"int64". + - -misc-use-anonymous-namespace # Google Test macros declare some static global variables to not export them. + - -modernize-use-trailing-return-type # Google Test macros use non-trailing return types. + - -performance-move-const-arg # Tests might std::move() a trivially copyable value as part of testing that moving works. + - -readability-function-cognitive-complexity # Assertions turn into extra branches, increasing apparent complexity. + - -readability-magic-numbers # Allow tests to use magic numbers + diff --git a/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp new file mode 100644 index 0000000000..3c4277f8a0 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <android-base/logging.h> + +#include "test_framework/core/TestService.h" + +namespace android::surfaceflinger::tests::end2end { +namespace { + +struct Placeholder : public ::testing::Test {}; + +TEST_F(Placeholder, Bringup) { + auto serviceResult = test_framework::core::TestService::startWithDisplays({ + {.id = 123}, + }); + if (!serviceResult) { + LOG(WARNING) << "End2End service not available. " << serviceResult.error(); + GTEST_SKIP() << "End2End service not available. " << serviceResult.error(); + } +} + +} // namespace +} // namespace android::surfaceflinger::tests::end2end diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 6f15db8beb..76e01a6e7d 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -23,7 +23,6 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <gui/DisplayEventReceiver.h> #include <log/log.h> #include <scheduler/VsyncConfig.h> #include <utils/Errors.h> @@ -112,8 +111,6 @@ protected: void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime); void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride>); - void expectQueuedBufferCountReceivedByConnection( - ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount); void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime, nsecs_t deadlineTimestamp) { @@ -147,7 +144,6 @@ protected: sp<MockEventThreadConnection> mConnection; sp<MockEventThreadConnection> mThrottledConnection; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; - std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders; std::chrono::nanoseconds mVsyncPeriod; @@ -380,14 +376,6 @@ void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection( EXPECT_EQ(expectedDisplayId, event.header.displayId); } -void EventThreadTest::expectQueuedBufferCountReceivedByConnection( - ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) { - auto args = connectionEventRecorder.waitForCall(); - ASSERT_TRUE(args.has_value()); - const auto& event = std::get<0>(args.value()); - EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers); -} - namespace { using namespace testing; @@ -880,63 +868,6 @@ TEST_F(EventThreadTest, postHcpLevelsChanged) { EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel); } -TEST_F(EventThreadTest, connectionReceivesBufferStuffing) { - setupEventThread(); - - // Create a connection that will experience buffer stuffing. - ConnectionEventRecorder stuffedConnectionEventRecorder{0}; - sp<MockEventThreadConnection> stuffedConnection = - createConnection(stuffedConnectionEventRecorder, - gui::ISurfaceComposer::EventRegistration::modeChanged | - gui::ISurfaceComposer::EventRegistration::frameRateOverride, - 111); - - // Add a connection and buffer count to the list of stuffed Uids that will receive - // data in the next vsync event. - BufferStuffingMap bufferStuffedUids; - bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3); - mThread->addBufferStuffedUids(bufferStuffedUids); - mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder); - - // Signal that we want the next vsync event to be posted to two connections. - mThread->requestNextVsync(mConnection); - mThread->requestNextVsync(stuffedConnection); - onVSyncEvent(123, 456, 789); - - // Vsync event data contains number of queued buffers. - expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0); - expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3); -} - -TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) { - setupEventThread(); - - // Create a connection with the same Uid as another connection. - ConnectionEventRecorder secondConnectionEventRecorder{0}; - sp<MockEventThreadConnection> secondConnection = - createConnection(secondConnectionEventRecorder, - gui::ISurfaceComposer::EventRegistration::modeChanged | - gui::ISurfaceComposer::EventRegistration::frameRateOverride, - mConnectionUid); - - // Add connection Uid and buffer count to the list of stuffed Uids that will receive - // data in the next vsync event. - BufferStuffingMap bufferStuffedUids; - bufferStuffedUids.try_emplace(mConnectionUid, 3); - mThread->addBufferStuffedUids(bufferStuffedUids); - mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder); - mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder); - - // Signal that we want the next vsync event to be posted to two connections. - mThread->requestNextVsync(mConnection); - mThread->requestNextVsync(secondConnection); - onVSyncEvent(123, 456, 789); - - // Vsync event data contains number of queued buffers. - expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3); - expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3); -} - } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp index 2332bf62da..d5c22a9601 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPhysicalDisplayPowerModeTest.cpp @@ -17,7 +17,6 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" -#include <android_companion_virtualdevice_flags.h> #include <com_android_graphics_surfaceflinger_flags.h> #include <common/test/FlagUtils.h> #include "DisplayTransactionTestHelpers.h" @@ -79,19 +78,11 @@ struct EventThreadBaseSupportedVariant { struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant { static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1); - setupDisableSyntheticVsyncCallExpectations(test); - } - - static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0); } static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1); - setupEnableSyntheticVsyncCallExpectations(test); - } - - static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0); } }; @@ -100,20 +91,12 @@ struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) { // Expect to enable hardware VSYNC and disable synthetic VSYNC. EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1); - setupDisableSyntheticVsyncCallExpectations(test); - } - - static void setupDisableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1); } static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) { // Expect to disable hardware VSYNC and enable synthetic VSYNC. EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1); - setupEnableSyntheticVsyncCallExpectations(test); - } - - static void setupEnableSyntheticVsyncCallExpectations(DisplayTransactionTest* test) { EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1); } }; @@ -168,7 +151,7 @@ struct TransitionOffToDozeSuspendVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); - Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupRepaintEverythingCallExpectations(test); } @@ -193,7 +176,7 @@ struct TransitionDozeSuspendToOffVariant : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupEnableSyntheticVsyncCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); } @@ -205,7 +188,7 @@ struct TransitionDozeSuspendToOffVariant struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); } }; @@ -223,7 +206,7 @@ struct TransitionDozeSuspendToDozeVariant struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); } }; @@ -251,7 +234,7 @@ struct TransitionOnToUnknownVariant : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupDisableSyntheticVsyncCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupNoComposerPowerModeCallExpectations(test); } }; @@ -352,9 +335,6 @@ void SetPhysicalDisplayPowerModeTest::transitionDisplayCommon() { // -------------------------------------------------------------------- // Preconditions - SET_FLAG_FOR_TEST(android::companion::virtualdevice::flags::correct_virtual_display_power_state, - true); - Case::Doze::setupComposerCallExpectations(this); auto display = Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE); diff --git a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp index 21ee071f1b..3589553be0 100644 --- a/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp +++ b/services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp @@ -22,6 +22,8 @@ #include <chrono> #include <thread> +#include <scheduler/Time.h> + #include "Scheduler/VsyncConfiguration.h" using namespace testing; @@ -39,6 +41,10 @@ public: : impl::WorkDuration(currentFps, sfDuration, appDuration, sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration, appEarlyGlDuration, hwcMinWorkDuration) {} + + TestableWorkDuration(Fps currentFps, Duration minSfDuration, Duration maxSfDuration, + Duration appDuration) + : impl::WorkDuration(currentFps, minSfDuration, maxSfDuration, appDuration) {} }; class WorkDurationTest : public testing::Test { @@ -168,6 +174,35 @@ TEST_F(WorkDurationTest, minHwcWorkDuration) { EXPECT_EQ(mWorkDuration.getCurrentConfigs().hwcMinWorkDuration, 1234ns); } +TEST_F(WorkDurationTest, workDurationIsARange) { + const Duration minSfDuration = Duration::fromNs(10'500'000); + const Duration maxSfDuration = Duration::fromNs(20'500'000); + const Duration appDuration = Duration::fromNs(16'000'000); + const TestableWorkDuration workDuration{60_Hz, minSfDuration, maxSfDuration, appDuration}; + + auto currentOffsets = workDuration.getCurrentConfigs(); + auto offsets = workDuration.getConfigsForRefreshRate(60_Hz); + + EXPECT_EQ(currentOffsets, offsets); + EXPECT_EQ(offsets.late.sfOffset, 6'166'667); + EXPECT_EQ(offsets.late.appOffset, 6'833'334); + + EXPECT_EQ(offsets.late.sfWorkDuration, 10'500'000ns); + EXPECT_EQ(offsets.late.appWorkDuration, 16'000'000ns); + + EXPECT_EQ(offsets.early.sfOffset, -3'833'333); + EXPECT_EQ(offsets.early.appOffset, 13'500'001); + + EXPECT_EQ(offsets.early.sfWorkDuration, 20'500'000ns); + EXPECT_EQ(offsets.early.appWorkDuration, 16'000'000ns); + + EXPECT_EQ(offsets.earlyGpu.sfOffset, -3'833'333); + EXPECT_EQ(offsets.earlyGpu.appOffset, 13'500'001); + + EXPECT_EQ(offsets.earlyGpu.sfWorkDuration, 20'500'000ns); + EXPECT_EQ(offsets.earlyGpu.appWorkDuration, 16'000'000ns); +} + class TestablePhaseOffsets : public impl::PhaseOffsets { public: TestablePhaseOffsets(nsecs_t vsyncPhaseOffsetNs, nsecs_t sfVSyncPhaseOffsetNs, diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 7319f1ee22..00e4cc6818 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -190,6 +190,8 @@ public: MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*)); MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id)); MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id)); + MOCK_METHOD(Error, startHdcpNegotiation, + (Display, const aidl::android::hardware::drm::HdcpLevels& levels)); MOCK_METHOD(Error, getLuts, (Display, const std::vector<sp<GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*)); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 4ca6fe073b..a20b9e1f0d 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -116,6 +116,8 @@ public: MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override)); MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&), (override)); + MOCK_METHOD(hal::Error, startHdcpNegotiation, + (const aidl::android::hardware::drm::HdcpLevels& levels), (override)); MOCK_METHOD(hal::Error, getLuts, (const std::vector<android::sp<android::GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*), diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h index 01d078bbf1..449c45bc0a 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h @@ -152,6 +152,8 @@ public: MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId)); MOCK_METHOD(status_t, setDisplayPictureProfileHandle, (PhysicalDisplayId, const PictureProfileHandle&)); + MOCK_METHOD(status_t, startHdcpNegotiation, + (PhysicalDisplayId, const aidl::android::hardware::drm::HdcpLevels&)); MOCK_METHOD(status_t, getLuts, (PhysicalDisplayId, const std::vector<sp<GraphicBuffer>>&, std::vector<aidl::android::hardware::graphics::composer3::Luts>*)); diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 3036fec456..cce4d2aba8 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -55,7 +55,6 @@ public: MOCK_METHOD(void, onHdcpLevelsChanged, (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel), (override)); - MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override)); }; } // namespace android::mock diff --git a/services/vibratorservice/Android.bp b/services/vibratorservice/Android.bp index ed03cfc3f5..4b12cc20a4 100644 --- a/services/vibratorservice/Android.bp +++ b/services/vibratorservice/Android.bp @@ -26,6 +26,7 @@ cc_library_shared { srcs: [ "VibratorCallbackScheduler.cpp", + "VibratorController.cpp", "VibratorHalController.cpp", "VibratorHalWrapper.cpp", "VibratorManagerHalController.cpp", @@ -41,17 +42,17 @@ cc_library_shared { }, shared_libs: [ + "android.hardware.vibrator-V3-ndk", "libbinder_ndk", "liblog", "libutils", - "android.hardware.vibrator-V3-ndk", ], cflags: [ "-Wall", "-Werror", - "-Wunused", "-Wunreachable-code", + "-Wunused", ], local_include_dirs: ["include"], diff --git a/services/vibratorservice/VibratorController.cpp b/services/vibratorservice/VibratorController.cpp new file mode 100644 index 0000000000..21924e9f13 --- /dev/null +++ b/services/vibratorservice/VibratorController.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "VibratorController" + +#ifndef qDoWithRetries +#define qDoWithRetries(op) doWithRetries(op, __FUNCTION__) +#endif + +#include <aidl/android/hardware/vibrator/IVibrator.h> +#include <android/binder_manager.h> +#include <binder/IServiceManager.h> + +#include <utils/Log.h> + +#include <vibratorservice/VibratorController.h> + +using ::aidl::android::hardware::vibrator::Effect; +using ::aidl::android::hardware::vibrator::EffectStrength; +using ::aidl::android::hardware::vibrator::IVibrator; + +using Status = ::ndk::ScopedAStatus; + +using namespace std::placeholders; + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +inline bool isStatusUnsupported(const Status& status) { + // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported. + return status.getStatus() == STATUS_UNKNOWN_TRANSACTION || + status.getExceptionCode() == EX_UNSUPPORTED_OPERATION; +} + +inline bool isStatusTransactionFailed(const Status& status) { + // STATUS_UNKNOWN_TRANSACTION means the HAL is an older version, so operation is unsupported. + return status.getStatus() != STATUS_UNKNOWN_TRANSACTION && + status.getExceptionCode() == EX_TRANSACTION_FAILED; +} + +// ------------------------------------------------------------------------------------------------- + +bool VibratorProvider::isDeclared() { + std::lock_guard<std::mutex> lock(mMutex); + if (mIsDeclared.has_value()) { + return *mIsDeclared; + } + + bool isDeclared = AServiceManager_isDeclared(mServiceName.c_str()); + if (!isDeclared) { + ALOGV("Vibrator HAL service not declared."); + } + + mIsDeclared.emplace(isDeclared); + return isDeclared; +} + +std::shared_ptr<IVibrator> VibratorProvider::waitForVibrator() { + if (!isDeclared()) { + return nullptr; + } + + auto vibrator = IVibrator::fromBinder( + ndk::SpAIBinder(AServiceManager_waitForService(mServiceName.c_str()))); + if (vibrator) { + ALOGV("Successfully connected to Vibrator HAL service."); + } else { + ALOGE("Error connecting to declared Vibrator HAL service."); + } + + return vibrator; +} + +std::shared_ptr<IVibrator> VibratorProvider::checkForVibrator() { + if (!isDeclared()) { + return nullptr; + } + + auto vibrator = IVibrator::fromBinder( + ndk::SpAIBinder(AServiceManager_checkService(mServiceName.c_str()))); + if (vibrator) { + ALOGV("Successfully reconnected to Vibrator HAL service."); + } else { + ALOGE("Error reconnecting to declared Vibrator HAL service."); + } + + return vibrator; +} + +// ------------------------------------------------------------------------------------------------- + +bool VibratorController::init() { + if (!mVibratorProvider->isDeclared()) { + return false; + } + std::lock_guard<std::mutex> lock(mMutex); + if (mVibrator == nullptr) { + mVibrator = mVibratorProvider->waitForVibrator(); + } + return mVibratorProvider->isDeclared(); +} + +Status VibratorController::off() { + return qDoWithRetries(std::bind(&IVibrator::off, _1)); +} + +Status VibratorController::setAmplitude(float amplitude) { + return qDoWithRetries(std::bind(&IVibrator::setAmplitude, _1, amplitude)); +} + +Status VibratorController::setExternalControl(bool enabled) { + return qDoWithRetries(std::bind(&IVibrator::setExternalControl, _1, enabled)); +} + +Status VibratorController::alwaysOnEnable(int32_t id, const Effect& effect, + const EffectStrength& strength) { + return qDoWithRetries(std::bind(&IVibrator::alwaysOnEnable, _1, id, effect, strength)); +} + +Status VibratorController::alwaysOnDisable(int32_t id) { + return qDoWithRetries(std::bind(&IVibrator::alwaysOnDisable, _1, id)); +} + +// ------------------------------------------------------------------------------------------------- + +std::shared_ptr<IVibrator> VibratorController::reconnectToVibrator() { + std::lock_guard<std::mutex> lock(mMutex); + mVibrator = mVibratorProvider->checkForVibrator(); + return mVibrator; +} + +Status VibratorController::doWithRetries(const VibratorController::VibratorOp& op, + const char* logLabel) { + if (!init()) { + ALOGV("Skipped %s because Vibrator HAL is not declared", logLabel); + return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, "IVibrator not declared"); + } + std::shared_ptr<IVibrator> vibrator; + { + std::lock_guard<std::mutex> lock(mMutex); + vibrator = mVibrator; + } + + if (!vibrator) { + ALOGE("Skipped %s because Vibrator HAL is declared but failed to load", logLabel); + return Status::fromExceptionCodeWithMessage(EX_ILLEGAL_STATE, + "IVibrator declared but failed to load"); + } + + auto status = doOnce(vibrator.get(), op, logLabel); + for (int i = 1; i < MAX_ATTEMPTS && isStatusTransactionFailed(status); i++) { + vibrator = reconnectToVibrator(); + if (!vibrator) { + // Failed to reconnect to vibrator HAL after a transaction failed, skip retries. + break; + } + status = doOnce(vibrator.get(), op, logLabel); + } + + return status; +} + +Status VibratorController::doOnce(IVibrator* vibrator, const VibratorController::VibratorOp& op, + const char* logLabel) { + auto status = op(vibrator); + if (!status.isOk()) { + if (isStatusUnsupported(status)) { + ALOGV("Vibrator HAL %s is unsupported: %s", logLabel, status.getMessage()); + } else { + ALOGE("Vibrator HAL %s failed: %s", logLabel, status.getMessage()); + } + } + return status; +} + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android diff --git a/services/vibratorservice/include/vibratorservice/VibratorController.h b/services/vibratorservice/include/vibratorservice/VibratorController.h new file mode 100644 index 0000000000..691c8ae518 --- /dev/null +++ b/services/vibratorservice/include/vibratorservice/VibratorController.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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_CONTROLLER_H +#define ANDROID_OS_VIBRATOR_CONTROLLER_H + +#include <aidl/android/hardware/vibrator/IVibrator.h> + +#include <android-base/thread_annotations.h> + +namespace android { + +namespace vibrator { + +// ------------------------------------------------------------------------------------------------- + +/* Provider for IVibrator HAL service instances. */ +class VibratorProvider { +public: + using IVibrator = ::aidl::android::hardware::vibrator::IVibrator; + + VibratorProvider() : mServiceName(std::string(IVibrator::descriptor) + "/default") {} + virtual ~VibratorProvider() = default; + + /* Returns true if vibrator HAL service is declared in the device, false otherwise. */ + virtual bool isDeclared(); + + /* Connects to vibrator HAL, possibly waiting for the declared service to become available. */ + virtual std::shared_ptr<IVibrator> waitForVibrator(); + + /* Connects to vibrator HAL if declared and available, without waiting. */ + virtual std::shared_ptr<IVibrator> checkForVibrator(); + +private: + std::mutex mMutex; + const std::string mServiceName; + std::optional<bool> mIsDeclared GUARDED_BY(mMutex); +}; + +// ------------------------------------------------------------------------------------------------- + +/* Controller for Vibrator HAL handle. + * This relies on VibratorProvider to connect to the underlying Vibrator HAL service and reconnects + * after each transaction failed call. This also ensures connecting to the service is thread-safe. + */ +class VibratorController { +public: + using Effect = ::aidl::android::hardware::vibrator::Effect; + using EffectStrength = ::aidl::android::hardware::vibrator::EffectStrength; + using IVibrator = ::aidl::android::hardware::vibrator::IVibrator; + using Status = ::ndk::ScopedAStatus; + using VibratorOp = std::function<Status(IVibrator*)>; + + VibratorController() : VibratorController(std::make_shared<VibratorProvider>()) {} + VibratorController(std::shared_ptr<VibratorProvider> vibratorProvider) + : mVibratorProvider(std::move(vibratorProvider)), mVibrator(nullptr) {} + virtual ~VibratorController() = default; + + /* Connects HAL service, possibly waiting for the declared service to become available. + * This will automatically be called at the first API usage if it was not manually called + * beforehand. Call this manually during the setup phase to avoid slowing the first API call. + * Returns true if HAL service is declared, false otherwise. + */ + bool init(); + + /* Turn vibrator off. */ + Status off(); + + /* Set vibration amplitude in [0,1]. */ + Status setAmplitude(float amplitude); + + /* Enable/disable external control. */ + Status setExternalControl(bool enabled); + + /* Enable always-on for given id, with given effect and strength. */ + Status alwaysOnEnable(int32_t id, const Effect& effect, const EffectStrength& strength); + + /* Disable always-on for given id. */ + Status alwaysOnDisable(int32_t id); + +private: + /* Max number of attempts to perform an operation when it fails with transaction error. */ + static constexpr int MAX_ATTEMPTS = 2; + + std::mutex mMutex; + std::shared_ptr<VibratorProvider> mVibratorProvider; + std::shared_ptr<IVibrator> mVibrator GUARDED_BY(mMutex); + + /* Reconnects HAL service without waiting for the service to become available. */ + std::shared_ptr<IVibrator> reconnectToVibrator(); + + /* Perform given operation on HAL with retries on transaction failures. */ + Status doWithRetries(const VibratorOp& op, const char* logLabel); + + /* Perform given operation on HAL with logs for error/unsupported results. */ + static Status doOnce(IVibrator* vibrator, const VibratorOp& op, const char* logLabel); +}; + +// ------------------------------------------------------------------------------------------------- + +}; // namespace vibrator + +}; // namespace android + +#endif // ANDROID_OS_VIBRATOR_CONTROLLER_H diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalController.h b/services/vibratorservice/include/vibratorservice/VibratorHalController.h index a1cb3fad35..9b3202bc60 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalController.h @@ -14,6 +14,8 @@ * limitations under the License. */ +// TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed + #ifndef ANDROID_OS_VIBRATORHALCONTROLLER_H #define ANDROID_OS_VIBRATORHALCONTROLLER_H diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h index 065227861d..68568d4163 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h @@ -14,6 +14,8 @@ * limitations under the License. */ +// TODO(b/308452413): remove this file once android.os.vibrator.remove_hidl_support is removed + #ifndef ANDROID_OS_VIBRATORHALWRAPPER_H #define ANDROID_OS_VIBRATORHALWRAPPER_H diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp index 038248e636..92c6286513 100644 --- a/services/vibratorservice/test/Android.bp +++ b/services/vibratorservice/test/Android.bp @@ -27,6 +27,7 @@ cc_test { test_suites: ["device-tests"], srcs: [ "VibratorCallbackSchedulerTest.cpp", + "VibratorControllerTest.cpp", "VibratorHalControllerTest.cpp", "VibratorHalWrapperAidlTest.cpp", "VibratorManagerHalControllerTest.cpp", @@ -39,12 +40,12 @@ cc_test { "-Wextra", ], shared_libs: [ + "android.hardware.vibrator-V3-ndk", "libbase", "libbinder_ndk", "liblog", - "libvibratorservice", "libutils", - "android.hardware.vibrator-V3-ndk", + "libvibratorservice", ], static_libs: [ "libgmock", diff --git a/services/vibratorservice/test/VibratorControllerTest.cpp b/services/vibratorservice/test/VibratorControllerTest.cpp new file mode 100644 index 0000000000..11ec75bc36 --- /dev/null +++ b/services/vibratorservice/test/VibratorControllerTest.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "VibratorControllerTest" + +#include <aidl/android/hardware/vibrator/IVibrator.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <utils/Log.h> +#include <thread> + +#include <vibratorservice/VibratorController.h> + +#include "test_mocks.h" +#include "test_utils.h" + +using ::aidl::android::hardware::vibrator::Effect; +using ::aidl::android::hardware::vibrator::EffectStrength; +using ::aidl::android::hardware::vibrator::IVibrator; + +using namespace android; +using namespace testing; + +const auto kReturnOk = []() { return ndk::ScopedAStatus::ok(); }; +const auto kReturnUnsupported = []() { + return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION); +}; +const auto kReturnTransactionFailed = []() { + return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED); +}; +const auto kReturnUnknownTransaction = []() { + return ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION); +}; +const auto kReturnIllegalArgument = []() { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); +}; + +// ------------------------------------------------------------------------------------------------- + +/* Provides mock IVibrator instance for testing. */ +class FakeVibratorProvider : public vibrator::VibratorProvider { +public: + FakeVibratorProvider() + : mIsDeclared(true), + mMockVibrator(ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>()), + mConnectCount(0), + mReconnectCount(0) {} + virtual ~FakeVibratorProvider() = default; + + bool isDeclared() override { return mIsDeclared; } + + std::shared_ptr<IVibrator> waitForVibrator() override { + mConnectCount++; + return mIsDeclared ? mMockVibrator : nullptr; + } + + std::shared_ptr<IVibrator> checkForVibrator() override { + mReconnectCount++; + return mIsDeclared ? mMockVibrator : nullptr; + } + + void setDeclared(bool isDeclared) { mIsDeclared = isDeclared; } + + int32_t getConnectCount() { return mConnectCount; } + + int32_t getReconnectCount() { return mReconnectCount; } + + std::shared_ptr<StrictMock<vibrator::MockIVibrator>> getMockVibrator() { return mMockVibrator; } + +private: + bool mIsDeclared; + std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator; + int32_t mConnectCount; + int32_t mReconnectCount; +}; + +// ------------------------------------------------------------------------------------------------- + +class VibratorControllerTest : public Test { +public: + void SetUp() override { + mProvider = std::make_shared<FakeVibratorProvider>(); + mController = std::make_unique<vibrator::VibratorController>(mProvider); + ASSERT_NE(mController, nullptr); + } + +protected: + std::shared_ptr<FakeVibratorProvider> mProvider = nullptr; + std::unique_ptr<vibrator::VibratorController> mController = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +TEST_F(VibratorControllerTest, TestInitServiceDeclared) { + ASSERT_TRUE(mController->init()); + ASSERT_EQ(1, mProvider->getConnectCount()); + ASSERT_EQ(0, mProvider->getReconnectCount()); + + // Noop when wrapper was already initialized. + ASSERT_TRUE(mController->init()); + ASSERT_EQ(1, mProvider->getConnectCount()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestInitServiceNotDeclared) { + mProvider->setDeclared(false); + + ASSERT_FALSE(mController->init()); + ASSERT_EQ(0, mProvider->getConnectCount()); + ASSERT_EQ(0, mProvider->getReconnectCount()); + + ASSERT_FALSE(mController->init()); + ASSERT_EQ(0, mProvider->getConnectCount()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestFirstCallTriggersInit) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + + auto status = mController->off(); + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(1, mProvider->getConnectCount()); +} + +TEST_F(VibratorControllerTest, TestSuccessfulResultDoesNotRetry) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + + auto status = mController->off(); + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestUnsupportedOperationResultDoesNotRetry) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnUnsupported); + + auto status = mController->off(); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestUnknownTransactionResultDoesNotRetry) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnUnknownTransaction); + + auto status = mController->off(); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestOperationFailedDoesNotRetry) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnIllegalArgument); + + auto status = mController->off(); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(0, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestTransactionFailedRetriesOnlyOnce) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(2)) + .WillRepeatedly(kReturnTransactionFailed); + + auto status = mController->off(); + ASSERT_FALSE(status.isOk()); + ASSERT_EQ(1, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestTransactionFailedThenSucceedsReturnsSuccessAfterRetries) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(2)) + .WillOnce(kReturnTransactionFailed) + .WillRepeatedly(kReturnOk); + + auto status = mController->off(); + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(1, mProvider->getReconnectCount()); +} + +TEST_F(VibratorControllerTest, TestOff) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), off()) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + + auto status = mController->off(); + ASSERT_TRUE(status.isOk()); +} + +TEST_F(VibratorControllerTest, TestSetAmplitude) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), setAmplitude(Eq(0.1f))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + EXPECT_CALL(*mProvider->getMockVibrator().get(), setAmplitude(Eq(0.2f))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnIllegalArgument); + + ASSERT_TRUE(mController->setAmplitude(0.1f).isOk()); + ASSERT_FALSE(mController->setAmplitude(0.2f).isOk()); +} + +TEST_F(VibratorControllerTest, TestSetExternalControl) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), setExternalControl(Eq(false))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + EXPECT_CALL(*mProvider->getMockVibrator().get(), setExternalControl(Eq(true))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnIllegalArgument); + + ASSERT_TRUE(mController->setExternalControl(false).isOk()); + ASSERT_FALSE(mController->setExternalControl(true).isOk()); +} + +TEST_F(VibratorControllerTest, TestAlwaysOnEnable) { + EXPECT_CALL(*mProvider->getMockVibrator().get(), + alwaysOnEnable(Eq(1), Eq(Effect::CLICK), Eq(EffectStrength::LIGHT))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnOk); + EXPECT_CALL(*mProvider->getMockVibrator().get(), + alwaysOnEnable(Eq(2), Eq(Effect::TICK), Eq(EffectStrength::MEDIUM))) + .Times(Exactly(1)) + .WillRepeatedly(kReturnIllegalArgument); + + ASSERT_TRUE(mController->alwaysOnEnable(1, Effect::CLICK, EffectStrength::LIGHT).isOk()); + ASSERT_FALSE(mController->alwaysOnEnable(2, Effect::TICK, EffectStrength::MEDIUM).isOk()); +} diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index be8fb3ea1d..d75058abe3 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -48,14 +48,9 @@ cc_aconfig_library { aconfig_declarations: "libvulkan_flags", } -cc_library_shared { - name: "libvulkan", - llndk: { - symbol_file: "libvulkan.map.txt", - export_llndk_headers: [ - "vulkan_headers", - ], - }, +cc_defaults { + name: "libvulkan_defaults", + sanitize: { misc_undefined: ["integer"], }, @@ -88,6 +83,34 @@ cc_library_shared { "-Wno-global-constructors", "-Wno-zero-length-array", ], +} + +cc_library { + name: "libvulkanallocator", + defaults: ["libvulkan_defaults"], + cflags: [ + // This code uses malloc_usable_size(), + // and thus can't be built with _FORTIFY_SOURCE=3. + "-U_FORTIFY_SOURCE", + "-D_FORTIFY_SOURCE=2", + ], + srcs: [ + "allocator.cpp", + ], + header_libs: [ + "vulkan_headers", + ], +} + +cc_library_shared { + name: "libvulkan", + defaults: ["libvulkan_defaults"], + llndk: { + symbol_file: "libvulkan.map.txt", + export_llndk_headers: [ + "vulkan_headers", + ], + }, srcs: [ "api.cpp", @@ -131,6 +154,7 @@ cc_library_shared { ], static_libs: [ "libgrallocusage", + "libvulkanallocator", "libvulkanflags", ], } diff --git a/vulkan/libvulkan/allocator.cpp b/vulkan/libvulkan/allocator.cpp new file mode 100644 index 0000000000..2ca0586a14 --- /dev/null +++ b/vulkan/libvulkan/allocator.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "allocator.h" + +#include <stdlib.h> + +#include <algorithm> + +#include <log/log.h> + +// #define ENABLE_ALLOC_CALLSTACKS 1 +#if ENABLE_ALLOC_CALLSTACKS +#include <utils/CallStack.h> +#define ALOGD_CALLSTACK(...) \ + do { \ + ALOGD(__VA_ARGS__); \ + android::CallStack callstack; \ + callstack.update(); \ + callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, " "); \ + } while (false) +#else +#define ALOGD_CALLSTACK(...) \ + do { \ + } while (false) +#endif + +namespace vulkan { +namespace driver { + +namespace { + +VKAPI_ATTR void* DefaultAllocate(void*, + size_t size, + size_t alignment, + VkSystemAllocationScope) { + void* ptr = nullptr; + // Vulkan requires 'alignment' to be a power of two, but posix_memalign + // additionally requires that it be at least sizeof(void*). + int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size); + ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment, + ret, ptr); + return ret == 0 ? ptr : nullptr; +} + +// This function is marked `noinline` so that LLVM can't infer an object size +// for FORTIFY through it, given that it's abusing malloc_usable_size(). +__attribute__((__noinline__)) +VKAPI_ATTR void* DefaultReallocate(void*, + void* ptr, + size_t size, + size_t alignment, + VkSystemAllocationScope) { + if (size == 0) { + free(ptr); + return nullptr; + } + + // TODO(b/143295633): Right now we never shrink allocations; if the new + // request is smaller than the existing chunk, we just continue using it. + // Right now the loader never reallocs, so this doesn't matter. If that + // changes, or if this code is copied into some other project, this should + // probably have a heuristic to allocate-copy-free when doing so will save + // "enough" space. + size_t old_size = ptr ? malloc_usable_size(ptr) : 0; + if (size <= old_size) + return ptr; + + void* new_ptr = nullptr; + if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0) + return nullptr; + if (ptr) { + memcpy(new_ptr, ptr, std::min(old_size, size)); + free(ptr); + } + return new_ptr; +} + +VKAPI_ATTR void DefaultFree(void*, void* ptr) { + ALOGD_CALLSTACK("Free: %p", ptr); + free(ptr); +} + +} // anonymous namespace + +const VkAllocationCallbacks& GetDefaultAllocator() { + static const VkAllocationCallbacks kDefaultAllocCallbacks = { + .pUserData = nullptr, + .pfnAllocation = DefaultAllocate, + .pfnReallocation = DefaultReallocate, + .pfnFree = DefaultFree, + }; + + return kDefaultAllocCallbacks; +} + +} // namespace driver +} // namespace vulkan diff --git a/vulkan/libvulkan/allocator.h b/vulkan/libvulkan/allocator.h new file mode 100644 index 0000000000..9095e921c5 --- /dev/null +++ b/vulkan/libvulkan/allocator.h @@ -0,0 +1,25 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <vulkan/vulkan.h> + +namespace vulkan { +namespace driver { + +const VkAllocationCallbacks& GetDefaultAllocator(); + +} // namespace driver +} // namespace vulkan diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 7d0f545774..28c1b5f663 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -50,22 +50,6 @@ using namespace com::android::graphics::libvulkan; extern "C" android_namespace_t* android_get_exported_namespace(const char*); -// #define ENABLE_ALLOC_CALLSTACKS 1 -#if ENABLE_ALLOC_CALLSTACKS -#include <utils/CallStack.h> -#define ALOGD_CALLSTACK(...) \ - do { \ - ALOGD(__VA_ARGS__); \ - android::CallStack callstack; \ - callstack.update(); \ - callstack.log(LOG_TAG, ANDROID_LOG_DEBUG, " "); \ - } while (false) -#else -#define ALOGD_CALLSTACK(...) \ - do { \ - } while (false) -#endif - namespace vulkan { namespace driver { @@ -829,54 +813,6 @@ void CreateInfoWrapper::FilterExtension(const char* name) { } } -VKAPI_ATTR void* DefaultAllocate(void*, - size_t size, - size_t alignment, - VkSystemAllocationScope) { - void* ptr = nullptr; - // Vulkan requires 'alignment' to be a power of two, but posix_memalign - // additionally requires that it be at least sizeof(void*). - int ret = posix_memalign(&ptr, std::max(alignment, sizeof(void*)), size); - ALOGD_CALLSTACK("Allocate: size=%zu align=%zu => (%d) %p", size, alignment, - ret, ptr); - return ret == 0 ? ptr : nullptr; -} - -VKAPI_ATTR void* DefaultReallocate(void*, - void* ptr, - size_t size, - size_t alignment, - VkSystemAllocationScope) { - if (size == 0) { - free(ptr); - return nullptr; - } - - // TODO(b/143295633): Right now we never shrink allocations; if the new - // request is smaller than the existing chunk, we just continue using it. - // Right now the loader never reallocs, so this doesn't matter. If that - // changes, or if this code is copied into some other project, this should - // probably have a heuristic to allocate-copy-free when doing so will save - // "enough" space. - size_t old_size = ptr ? malloc_usable_size(ptr) : 0; - if (size <= old_size) - return ptr; - - void* new_ptr = nullptr; - if (posix_memalign(&new_ptr, std::max(alignment, sizeof(void*)), size) != 0) - return nullptr; - if (ptr) { - memcpy(new_ptr, ptr, std::min(old_size, size)); - free(ptr); - } - return new_ptr; -} - -VKAPI_ATTR void DefaultFree(void*, void* ptr) { - ALOGD_CALLSTACK("Free: %p", ptr); - free(ptr); -} - InstanceData* AllocateInstanceData(const VkAllocationCallbacks& allocator) { void* data_mem = allocator.pfnAllocation( allocator.pUserData, sizeof(InstanceData), alignof(InstanceData), @@ -916,17 +852,6 @@ bool OpenHAL() { return Hal::Open(); } -const VkAllocationCallbacks& GetDefaultAllocator() { - static const VkAllocationCallbacks kDefaultAllocCallbacks = { - .pUserData = nullptr, - .pfnAllocation = DefaultAllocate, - .pfnReallocation = DefaultReallocate, - .pfnFree = DefaultFree, - }; - - return kDefaultAllocCallbacks; -} - PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName) { const ProcHook* hook = GetProcHook(pName); if (!hook) diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h index 4b855e5999..fa85dd5b55 100644 --- a/vulkan/libvulkan/driver.h +++ b/vulkan/libvulkan/driver.h @@ -27,6 +27,7 @@ #include <vulkan/vulkan.h> #include <hardware/hwvulkan.h> +#include "allocator.h" #include "api_gen.h" #include "driver_gen.h" #include "debug_report.h" @@ -102,7 +103,6 @@ struct DeviceData { }; bool OpenHAL(); -const VkAllocationCallbacks& GetDefaultAllocator(); void QueryPresentationProperties( VkPhysicalDevice physicalDevice, diff --git a/vulkan/scripts/vkjson_generator.py b/vulkan/scripts/vkjson_generator.py index 7dc55d9703..6f621a1542 100644 --- a/vulkan/scripts/vkjson_generator.py +++ b/vulkan/scripts/vkjson_generator.py @@ -184,12 +184,22 @@ def generate_vk_core_struct_definition(f): vkJson_core_entries.append(f"{struct_name} {version.lower()}") f.write(f"struct {struct_name} {{\n") + f.write(f" {struct_name}() {{\n") # Start of constructor + for item in items: + for struct_type, _ in item.items(): + field_name = "properties" if "Properties" in struct_type else "features" + f.write(f" memset(&{field_name}, 0, sizeof({struct_type}));\n") + f.write(" }\n") # End of constructor for item in items: for struct_type, _ in item.items(): field_name = "properties" if "Properties" in struct_type else "features" f.write(f" {struct_type} {field_name};\n") + if version == "Core14": + f.write(f"std::vector<VkImageLayout> copy_src_layouts;\n") + f.write(f"std::vector<VkImageLayout> copy_dst_layouts;\n") + f.write("};\n\n") return vkJson_core_entries @@ -212,11 +222,6 @@ def generate_memset_statements(f): f.write(f"memset(&{variable_name}, 0, sizeof({class_name}));\n") entries.append(f"{class_name} {variable_name}") - # Process vulkan core structs - for version in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"]: - struct_name = f"VkJson{version}" - f.write(f"memset(&{version.lower()}, 0, sizeof({struct_name}));\n") - return entries @@ -1757,6 +1762,21 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { if (device.properties.apiVersion >= VK_API_VERSION_1_4) {\n""") f.write(cc_code_properties_14) f.write(f"vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n") + + f.write("""\ +if (device.core14.properties.copySrcLayoutCount > 0 || device.core14.properties.copyDstLayoutCount > 0 ) { + if (device.core14.properties.copySrcLayoutCount > 0) { + device.core14.copy_src_layouts.resize(device.core14.properties.copySrcLayoutCount); + device.core14.properties.pCopySrcLayouts = device.core14.copy_src_layouts.data(); + } + if (device.core14.properties.copyDstLayoutCount > 0) { + device.core14.copy_dst_layouts.resize(device.core14.properties.copyDstLayoutCount); + device.core14.properties.pCopyDstLayouts = device.core14.copy_dst_layouts.data(); + } + vkGetPhysicalDeviceProperties2(physical_device, &properties); +} + \n""") + f.write(cc_code_features_14) f.write(f"vkGetPhysicalDeviceFeatures2(physical_device, &features);\n\n") f.write("""\ diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 531a623e90..517e62d1db 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -36,6 +36,9 @@ #include <type_traits> #include <utility> +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ namespace { /* diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h index d74644ca8e..cfba8c58d9 100644 --- a/vulkan/vkjson/vkjson.h +++ b/vulkan/vkjson/vkjson.h @@ -33,6 +33,9 @@ #undef max #endif +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ struct VkJsonLayer { VkLayerProperties properties; std::vector<VkExtensionProperties> extensions; @@ -263,23 +266,41 @@ struct VkJsonKHRDriverProperties { }; struct VkJsonCore11 { + VkJsonCore11() { + memset(&properties, 0, sizeof(VkPhysicalDeviceVulkan11Properties)); + memset(&features, 0, sizeof(VkPhysicalDeviceVulkan11Features)); + } VkPhysicalDeviceVulkan11Properties properties; VkPhysicalDeviceVulkan11Features features; }; struct VkJsonCore12 { + VkJsonCore12() { + memset(&properties, 0, sizeof(VkPhysicalDeviceVulkan12Properties)); + memset(&features, 0, sizeof(VkPhysicalDeviceVulkan12Features)); + } VkPhysicalDeviceVulkan12Properties properties; VkPhysicalDeviceVulkan12Features features; }; struct VkJsonCore13 { + VkJsonCore13() { + memset(&properties, 0, sizeof(VkPhysicalDeviceVulkan13Properties)); + memset(&features, 0, sizeof(VkPhysicalDeviceVulkan13Features)); + } VkPhysicalDeviceVulkan13Properties properties; VkPhysicalDeviceVulkan13Features features; }; struct VkJsonCore14 { + VkJsonCore14() { + memset(&properties, 0, sizeof(VkPhysicalDeviceVulkan14Properties)); + memset(&features, 0, sizeof(VkPhysicalDeviceVulkan14Features)); + } VkPhysicalDeviceVulkan14Properties properties; VkPhysicalDeviceVulkan14Features features; + std::vector<VkImageLayout> copy_src_layouts; + std::vector<VkImageLayout> copy_dst_layouts; }; struct VkJsonDevice { @@ -306,10 +327,6 @@ struct VkJsonDevice { sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures)); memset(&shader_draw_parameter_features, 0, sizeof(VkPhysicalDeviceShaderDrawParameterFeatures)); - memset(&core11, 0, sizeof(VkJsonCore11)); - memset(&core12, 0, sizeof(VkJsonCore12)); - memset(&core13, 0, sizeof(VkJsonCore13)); - memset(&core14, 0, sizeof(VkJsonCore14)); } VkJsonKHRVariablePointers khr_variable_pointers; VkJsonKHRShaderFloat16Int8 khr_shader_float16_int8; diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc index 22b3204e61..636c11933a 100644 --- a/vulkan/vkjson/vkjson_instance.cc +++ b/vulkan/vkjson/vkjson_instance.cc @@ -27,6 +27,9 @@ #include <algorithm> #include <utility> +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ namespace { bool EnumerateExtensions(const char* layer_name, @@ -512,6 +515,23 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { vkGetPhysicalDeviceProperties2(physical_device, &properties); + if (device.core14.properties.copySrcLayoutCount > 0 || + device.core14.properties.copyDstLayoutCount > 0) { + if (device.core14.properties.copySrcLayoutCount > 0) { + device.core14.copy_src_layouts.resize( + device.core14.properties.copySrcLayoutCount); + device.core14.properties.pCopySrcLayouts = + device.core14.copy_src_layouts.data(); + } + if (device.core14.properties.copyDstLayoutCount > 0) { + device.core14.copy_dst_layouts.resize( + device.core14.properties.copyDstLayoutCount); + device.core14.properties.pCopyDstLayouts = + device.core14.copy_dst_layouts.data(); + } + vkGetPhysicalDeviceProperties2(physical_device, &properties); + } + device.core14.features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES; device.core14.features.pNext = features.pNext; |