summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/atrace/atrace.rc91
-rw-r--r--cmds/flatland/GLHelper.cpp2
-rw-r--r--include/android/configuration.h1
-rw-r--r--include/android/thermal.h20
-rw-r--r--include/powermanager/PowerHalController.h3
-rw-r--r--include/powermanager/PowerHalWrapper.h70
-rw-r--r--include/powermanager/PowerHintSessionWrapper.h2
-rw-r--r--libs/binder/Binder.cpp3
-rw-r--r--libs/binder/rust/rpcbinder/src/server/trusty.rs4
-rw-r--r--libs/binder/tests/binderRpcTest.cpp8
-rw-r--r--libs/binder/trusty/include/binder/RpcServerTrusty.h9
-rw-r--r--libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp9
-rw-r--r--libs/dumputils/dump_utils.cpp1
-rw-r--r--libs/gui/Android.bp5
-rw-r--r--libs/gui/ISurfaceComposer.cpp121
-rw-r--r--libs/gui/Surface.cpp34
-rw-r--r--libs/gui/SurfaceComposerClient.cpp330
-rw-r--r--libs/gui/SurfaceControl.cpp4
-rw-r--r--libs/gui/include/gui/ISurfaceComposer.h9
-rw-r--r--libs/gui/include/gui/Surface.h3
-rw-r--r--libs/gui/include/gui/SurfaceComposerClient.h55
-rw-r--r--libs/gui/include/gui/SurfaceControl.h6
-rw-r--r--libs/gui/tests/Android.bp6
-rw-r--r--libs/gui/tests/BLASTBufferQueue_test.cpp2
-rw-r--r--libs/gui/tests/BufferQueue_test.cpp15
-rw-r--r--libs/gui/tests/DisplayedContentSampling_test.cpp2
-rw-r--r--libs/gui/tests/EndToEndNativeInputTest.cpp2
-rw-r--r--libs/gui/tests/GLTest.cpp2
-rw-r--r--libs/gui/tests/RegionSampling_test.cpp2
-rw-r--r--libs/gui/tests/SamplingDemo.cpp2
-rw-r--r--libs/gui/tests/Surface_test.cpp66
-rw-r--r--libs/input/input_flags.aconfig10
-rw-r--r--libs/renderengine/skia/filters/LutShader.cpp7
-rw-r--r--libs/ui/include/ui/DisplayIdentification.h17
-rw-r--r--opengl/tests/lib/WindowSurface.cpp2
-rw-r--r--services/automotive/display/AutomotiveDisplayProxyService.cpp2
-rw-r--r--services/inputflinger/dispatcher/Entry.h7
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp117
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h9
-rw-r--r--services/inputflinger/dispatcher/LatencyTracker.cpp9
-rw-r--r--services/inputflinger/dispatcher/LatencyTracker.h9
-rw-r--r--services/inputflinger/dispatcher/TouchState.cpp4
-rw-r--r--services/inputflinger/dispatcher/TouchState.h2
-rw-r--r--services/inputflinger/dispatcher/TouchedWindow.cpp4
-rw-r--r--services/inputflinger/dispatcher/TouchedWindow.h7
-rw-r--r--services/inputflinger/dispatcher/include/InputDispatcherInterface.h2
-rw-r--r--services/inputflinger/reader/mapper/TouchpadInputMapper.cpp23
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp239
-rw-r--r--services/inputflinger/tests/LatencyTracker_test.cpp19
-rw-r--r--services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp5
-rw-r--r--services/powermanager/Android.bp1
-rw-r--r--services/powermanager/PowerHalController.cpp15
-rw-r--r--services/powermanager/PowerHalWrapper.cpp18
-rw-r--r--services/powermanager/tests/PowerHalWrapperAidlTest.cpp49
-rw-r--r--services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp5
-rw-r--r--services/surfaceflinger/Display/VirtualDisplaySnapshot.h1
-rw-r--r--services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp4
-rw-r--r--services/surfaceflinger/DisplayHardware/ComposerHal.h5
-rw-r--r--services/surfaceflinger/DisplayHardware/HWComposer.cpp6
-rw-r--r--services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp17
-rw-r--r--services/surfaceflinger/FrameTimeline/FrameTimeline.cpp13
-rw-r--r--services/surfaceflinger/FrameTimeline/FrameTimeline.h10
-rw-r--r--services/surfaceflinger/LayerVector.h3
-rw-r--r--services/surfaceflinger/PowerAdvisor/SessionManager.h5
-rw-r--r--services/surfaceflinger/QueuedTransactionState.h42
-rw-r--r--services/surfaceflinger/Scheduler/EventThread.cpp25
-rw-r--r--services/surfaceflinger/Scheduler/EventThread.h11
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.cpp39
-rw-r--r--services/surfaceflinger/Scheduler/Scheduler.h28
-rw-r--r--services/surfaceflinger/Scheduler/VsyncConfiguration.cpp11
-rw-r--r--services/surfaceflinger/Scheduler/VsyncConfiguration.h2
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp306
-rw-r--r--services/surfaceflinger/SurfaceFlinger.h12
-rw-r--r--services/surfaceflinger/Tracing/TransactionProtoParser.cpp2
l---------services/surfaceflinger/tests/end2end/.clang-format1
-rw-r--r--services/surfaceflinger/tests/end2end/.clang-tidy380
-rw-r--r--services/surfaceflinger/tests/end2end/.clangd20
-rw-r--r--services/surfaceflinger/tests/end2end/Android.bp68
-rw-r--r--services/surfaceflinger/tests/end2end/AndroidTest.xml39
-rw-r--r--services/surfaceflinger/tests/end2end/README.md75
-rw-r--r--services/surfaceflinger/tests/end2end/main.cpp55
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h29
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp82
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/core/TestService.h76
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp123
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h61
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp106
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h59
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp181
-rw-r--r--services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h70
-rw-r--r--services/surfaceflinger/tests/end2end/tests/.clang-tidy32
-rw-r--r--services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp39
-rw-r--r--services/surfaceflinger/tests/unittests/EventThreadTest.cpp69
-rw-r--r--services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp29
-rw-r--r--services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h14
-rw-r--r--services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp112
-rw-r--r--services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp2
-rw-r--r--services/surfaceflinger/tests/unittests/VsyncConfigurationTest.cpp35
-rw-r--r--services/surfaceflinger/tests/unittests/mock/MockEventThread.h1
-rw-r--r--services/vibratorservice/Android.bp5
-rw-r--r--services/vibratorservice/VibratorController.cpp196
-rw-r--r--services/vibratorservice/include/vibratorservice/VibratorController.h118
-rw-r--r--services/vibratorservice/include/vibratorservice/VibratorHalController.h2
-rw-r--r--services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h2
-rw-r--r--services/vibratorservice/test/Android.bp5
-rw-r--r--services/vibratorservice/test/VibratorControllerTest.cpp248
-rw-r--r--vulkan/libvulkan/Android.bp40
-rw-r--r--vulkan/libvulkan/allocator.cpp113
-rw-r--r--vulkan/libvulkan/allocator.h25
-rw-r--r--vulkan/libvulkan/driver.cpp75
-rw-r--r--vulkan/libvulkan/driver.h2
-rw-r--r--vulkan/scripts/vkjson_generator.py30
-rw-r--r--vulkan/vkjson/vkjson.cc3
-rw-r--r--vulkan/vkjson/vkjson.h25
-rw-r--r--vulkan/vkjson/vkjson_instance.cc20
115 files changed, 3895 insertions, 785 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/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/rust/rpcbinder/src/server/trusty.rs b/libs/binder/rust/rpcbinder/src/server/trusty.rs
index fe45decf25..54d82d5bd0 100644
--- a/libs/binder/rust/rpcbinder/src/server/trusty.rs
+++ b/libs/binder/rust/rpcbinder/src/server/trusty.rs
@@ -106,6 +106,10 @@ pub struct RpcServerConnection {
ctx: *mut c_void,
}
+// SAFETY: The opaque handle: `ctx` points into a dynamic allocation,
+// and not tied to anything specific to the current thread.
+unsafe impl Send for RpcServerConnection {}
+
impl Drop for RpcServerConnection {
fn drop(&mut self) {
// We do not need to close handle_fd since we do not own it.
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/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ae4b74e03b..269936858a 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -26,7 +26,6 @@
#include <gui/ISurfaceComposer.h>
#include <gui/LayerState.h>
#include <gui/SchedulingPolicy.h>
-#include <gui/TransactionState.h>
#include <private/gui/ParcelUtils.h>
#include <stdint.h>
#include <sys/types.h>
@@ -61,12 +60,54 @@ public:
virtual ~BpSurfaceComposer();
- status_t setTransactionState(TransactionState&& state) override {
+ status_t setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
+ const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
+ const std::vector<uint64_t>& mergedTransactionIds) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- SAFE_PARCEL(state.writeToParcel, &data);
- if (state.mFlags & ISurfaceComposer::eOneWay) {
+ frameTimelineInfo.writeToParcel(&data);
+
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(state.size()));
+ for (const auto& s : state) {
+ SAFE_PARCEL(s.write, data);
+ }
+
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(displays.size()));
+ for (const auto& d : displays) {
+ SAFE_PARCEL(d.write, data);
+ }
+
+ SAFE_PARCEL(data.writeUint32, flags);
+ SAFE_PARCEL(data.writeStrongBinder, applyToken);
+ SAFE_PARCEL(commands.write, data);
+ SAFE_PARCEL(data.writeInt64, desiredPresentTime);
+ SAFE_PARCEL(data.writeBool, isAutoTimestamp);
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(uncacheBuffers.size()));
+ for (const client_cache_t& uncacheBuffer : uncacheBuffers) {
+ SAFE_PARCEL(data.writeStrongBinder, uncacheBuffer.token.promote());
+ SAFE_PARCEL(data.writeUint64, uncacheBuffer.id);
+ }
+ SAFE_PARCEL(data.writeBool, hasListenerCallbacks);
+
+ SAFE_PARCEL(data.writeVectorSize, listenerCallbacks);
+ for (const auto& [listener, callbackIds] : listenerCallbacks) {
+ SAFE_PARCEL(data.writeStrongBinder, listener);
+ SAFE_PARCEL(data.writeParcelableVector, callbackIds);
+ }
+
+ SAFE_PARCEL(data.writeUint64, transactionId);
+
+ SAFE_PARCEL(data.writeUint32, static_cast<uint32_t>(mergedTransactionIds.size()));
+ for (auto mergedTransactionId : mergedTransactionIds) {
+ SAFE_PARCEL(data.writeUint64, mergedTransactionId);
+ }
+
+ if (flags & ISurfaceComposer::eOneWay) {
return remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE,
data, &reply, IBinder::FLAG_ONEWAY);
} else {
@@ -91,9 +132,75 @@ status_t BnSurfaceComposer::onTransact(
case SET_TRANSACTION_STATE: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
- TransactionState state;
- SAFE_PARCEL(state.readFromParcel, &data);
- return setTransactionState(std::move(state));
+ FrameTimelineInfo frameTimelineInfo;
+ frameTimelineInfo.readFromParcel(&data);
+
+ uint32_t count = 0;
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
+ Vector<ComposerState> state;
+ state.setCapacity(count);
+ for (size_t i = 0; i < count; i++) {
+ ComposerState s;
+ SAFE_PARCEL(s.read, data);
+ state.add(s);
+ }
+
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
+ DisplayState d;
+ Vector<DisplayState> displays;
+ displays.setCapacity(count);
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(d.read, data);
+ displays.add(d);
+ }
+
+ uint32_t stateFlags = 0;
+ SAFE_PARCEL(data.readUint32, &stateFlags);
+ sp<IBinder> applyToken;
+ SAFE_PARCEL(data.readStrongBinder, &applyToken);
+ InputWindowCommands inputWindowCommands;
+ SAFE_PARCEL(inputWindowCommands.read, data);
+
+ int64_t desiredPresentTime = 0;
+ bool isAutoTimestamp = true;
+ SAFE_PARCEL(data.readInt64, &desiredPresentTime);
+ SAFE_PARCEL(data.readBool, &isAutoTimestamp);
+
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
+ std::vector<client_cache_t> uncacheBuffers(count);
+ sp<IBinder> tmpBinder;
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(data.readNullableStrongBinder, &tmpBinder);
+ uncacheBuffers[i].token = tmpBinder;
+ SAFE_PARCEL(data.readUint64, &uncacheBuffers[i].id);
+ }
+
+ bool hasListenerCallbacks = false;
+ SAFE_PARCEL(data.readBool, &hasListenerCallbacks);
+
+ std::vector<ListenerCallbacks> listenerCallbacks;
+ int32_t listenersSize = 0;
+ SAFE_PARCEL_READ_SIZE(data.readInt32, &listenersSize, data.dataSize());
+ for (int32_t i = 0; i < listenersSize; i++) {
+ SAFE_PARCEL(data.readStrongBinder, &tmpBinder);
+ std::vector<CallbackId> callbackIds;
+ SAFE_PARCEL(data.readParcelableVector, &callbackIds);
+ listenerCallbacks.emplace_back(tmpBinder, callbackIds);
+ }
+
+ uint64_t transactionId = -1;
+ SAFE_PARCEL(data.readUint64, &transactionId);
+
+ SAFE_PARCEL_READ_SIZE(data.readUint32, &count, data.dataSize());
+ std::vector<uint64_t> mergedTransactions(count);
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(data.readUint64, &mergedTransactions[i]);
+ }
+
+ return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
+ std::move(inputWindowCommands), desiredPresentTime,
+ isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
+ listenerCallbacks, transactionId, mergedTransactions);
}
case GET_SCHEDULING_POLICY: {
gui::SchedulingPolicy policy;
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/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b0650d56ff..9854274cb1 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -824,25 +824,34 @@ void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId) {
// ---------------------------------------------------------------------------
SurfaceComposerClient::Transaction::Transaction() {
- mState.mId = generateId();
+ mId = generateId();
mTransactionCompletedListener = TransactionCompletedListener::getInstance();
}
-SurfaceComposerClient::Transaction::Transaction(const Transaction& other) {
- mState = other.mState;
+SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
+ : mId(other.mId),
+ mFlags(other.mFlags),
+ mMayContainBuffer(other.mMayContainBuffer),
+ mDesiredPresentTime(other.mDesiredPresentTime),
+ mIsAutoTimestamp(other.mIsAutoTimestamp),
+ mFrameTimelineInfo(other.mFrameTimelineInfo),
+ mApplyToken(other.mApplyToken) {
+ mDisplayStates = other.mDisplayStates;
+ mComposerStates = other.mComposerStates;
+ mInputWindowCommands = other.mInputWindowCommands;
mListenerCallbacks = other.mListenerCallbacks;
mTransactionCompletedListener = TransactionCompletedListener::getInstance();
}
void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) {
uint32_t permissions = LayerStatePermissions::getTransactionPermissions(pid, uid);
- for (auto& composerState : mState.mComposerStates) {
+ for (auto& composerState : mComposerStates) {
composerState.state.sanitize(permissions);
}
- if (!mState.mInputWindowCommands.empty() &&
+ if (!mInputWindowCommands.empty() &&
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
ALOGE("Only privileged callers are allowed to send input commands.");
- mState.mInputWindowCommands.clear();
+ mInputWindowCommands.clear();
}
}
@@ -857,13 +866,34 @@ SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) {
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
- TransactionState tmpState;
- SAFE_PARCEL(tmpState.readFromParcel, parcel);
-
+ const uint64_t transactionId = parcel->readUint64();
+ const uint32_t flags = parcel->readUint32();
+ const int64_t desiredPresentTime = parcel->readInt64();
+ const bool isAutoTimestamp = parcel->readBool();
+ const bool logCallPoints = parcel->readBool();
+ FrameTimelineInfo frameTimelineInfo;
+ frameTimelineInfo.readFromParcel(parcel);
+
+ sp<IBinder> applyToken;
+ parcel->readNullableStrongBinder(&applyToken);
size_t count = static_cast<size_t>(parcel->readUint32());
if (count > parcel->dataSize()) {
return BAD_VALUE;
}
+ Vector<DisplayState> displayStates;
+ displayStates.setCapacity(count);
+ for (size_t i = 0; i < count; i++) {
+ DisplayState displayState;
+ if (displayState.read(*parcel) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+ displayStates.add(displayState);
+ }
+
+ count = static_cast<size_t>(parcel->readUint32());
+ if (count > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> listenerCallbacks;
listenerCallbacks.reserve(count);
for (size_t i = 0; i < count; i++) {
@@ -889,8 +919,57 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel
}
}
- mState = std::move(tmpState);
- mListenerCallbacks = std::move(listenerCallbacks);
+ count = static_cast<size_t>(parcel->readUint32());
+ if (count > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ Vector<ComposerState> composerStates;
+ composerStates.setCapacity(count);
+ for (size_t i = 0; i < count; i++) {
+ ComposerState composerState;
+ if (composerState.read(*parcel) == BAD_VALUE) {
+ return BAD_VALUE;
+ }
+ composerStates.add(composerState);
+ }
+
+ InputWindowCommands inputWindowCommands;
+ inputWindowCommands.read(*parcel);
+
+ count = static_cast<size_t>(parcel->readUint32());
+ if (count > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ std::vector<client_cache_t> uncacheBuffers(count);
+ for (size_t i = 0; i < count; i++) {
+ sp<IBinder> tmpBinder;
+ SAFE_PARCEL(parcel->readStrongBinder, &tmpBinder);
+ uncacheBuffers[i].token = tmpBinder;
+ SAFE_PARCEL(parcel->readUint64, &uncacheBuffers[i].id);
+ }
+
+ count = static_cast<size_t>(parcel->readUint32());
+ if (count > parcel->dataSize()) {
+ return BAD_VALUE;
+ }
+ std::vector<uint64_t> mergedTransactionIds(count);
+ for (size_t i = 0; i < count; i++) {
+ SAFE_PARCEL(parcel->readUint64, &mergedTransactionIds[i]);
+ }
+
+ // Parsing was successful. Update the object.
+ mId = transactionId;
+ mFlags = flags;
+ mDesiredPresentTime = desiredPresentTime;
+ mIsAutoTimestamp = isAutoTimestamp;
+ mFrameTimelineInfo = frameTimelineInfo;
+ mDisplayStates = std::move(displayStates);
+ mListenerCallbacks = listenerCallbacks;
+ mComposerStates = std::move(composerStates);
+ mInputWindowCommands = inputWindowCommands;
+ mApplyToken = applyToken;
+ mUncacheBuffers = std::move(uncacheBuffers);
+ mMergedTransactionIds = std::move(mergedTransactionIds);
return NO_ERROR;
}
@@ -908,7 +987,17 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
const_cast<SurfaceComposerClient::Transaction*>(this)->cacheBuffers();
- SAFE_PARCEL(mState.writeToParcel, parcel);
+ parcel->writeUint64(mId);
+ parcel->writeUint32(mFlags);
+ parcel->writeInt64(mDesiredPresentTime);
+ parcel->writeBool(mIsAutoTimestamp);
+ parcel->writeBool(mLogCallPoints);
+ mFrameTimelineInfo.writeToParcel(parcel);
+ parcel->writeStrongBinder(mApplyToken);
+ parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size()));
+ for (auto const& displayState : mDisplayStates) {
+ displayState.write(*parcel);
+ }
parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size()));
for (auto const& [listener, callbackInfo] : mListenerCallbacks) {
@@ -923,6 +1012,24 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const
}
}
+ parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size()));
+ for (auto const& composerState : mComposerStates) {
+ composerState.write(*parcel);
+ }
+
+ mInputWindowCommands.write(*parcel);
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mUncacheBuffers.size()));
+ for (const client_cache_t& uncacheBuffer : mUncacheBuffers) {
+ SAFE_PARCEL(parcel->writeStrongBinder, uncacheBuffer.token.promote());
+ SAFE_PARCEL(parcel->writeUint64, uncacheBuffer.id);
+ }
+
+ SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(mMergedTransactionIds.size()));
+ for (auto mergedTransactionId : mMergedTransactionIds) {
+ SAFE_PARCEL(parcel->writeUint64, mergedTransactionId);
+ }
+
return NO_ERROR;
}
@@ -947,8 +1054,50 @@ void SurfaceComposerClient::Transaction::releaseBufferIfOverwriting(const layer_
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) {
- mState.merge(std::move(other.mState),
- std::bind(&Transaction::releaseBufferIfOverwriting, this, std::placeholders::_1));
+ while (mMergedTransactionIds.size() + other.mMergedTransactionIds.size() >
+ MAX_MERGE_HISTORY_LENGTH - 1 &&
+ mMergedTransactionIds.size() > 0) {
+ mMergedTransactionIds.pop_back();
+ }
+ if (other.mMergedTransactionIds.size() == MAX_MERGE_HISTORY_LENGTH) {
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.end() - 1);
+ } else if (other.mMergedTransactionIds.size() > 0u) {
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.begin(),
+ other.mMergedTransactionIds.end());
+ }
+ mMergedTransactionIds.insert(mMergedTransactionIds.begin(), other.mId);
+
+ for (auto const& otherState : other.mComposerStates) {
+ if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+ [&otherState](const auto& composerState) {
+ return composerState.state.surface ==
+ otherState.state.surface;
+ });
+ it != mComposerStates.end()) {
+ if (otherState.state.what & layer_state_t::eBufferChanged) {
+ releaseBufferIfOverwriting(it->state);
+ }
+ it->state.merge(otherState.state);
+ } else {
+ mComposerStates.add(otherState);
+ }
+ }
+
+ for (auto const& state : other.mDisplayStates) {
+ if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+ [&state](const auto& displayState) {
+ return displayState.token == state.token;
+ });
+ it != mDisplayStates.end()) {
+ it->merge(state);
+ } else {
+ mDisplayStates.add(state);
+ }
+ }
+
for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator(
@@ -972,21 +1121,50 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr
}
}
+ for (const auto& cacheId : other.mUncacheBuffers) {
+ mUncacheBuffers.push_back(cacheId);
+ }
+
+ mInputWindowCommands.merge(other.mInputWindowCommands);
+
+ mMayContainBuffer |= other.mMayContainBuffer;
+ mFlags |= other.mFlags;
+ mApplyToken = other.mApplyToken;
+
+ mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo);
+
+ mLogCallPoints |= other.mLogCallPoints;
+ if (mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY,
+ "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId);
+ }
+
other.clear();
return *this;
}
void SurfaceComposerClient::Transaction::clear() {
- mState.clear();
+ mComposerStates.clear();
+ mDisplayStates.clear();
mListenerCallbacks.clear();
+ mInputWindowCommands.clear();
+ mUncacheBuffers.clear();
+ mMayContainBuffer = false;
+ mDesiredPresentTime = 0;
+ mIsAutoTimestamp = true;
+ mFrameTimelineInfo = {};
+ mApplyToken = nullptr;
+ mMergedTransactionIds.clear();
+ mLogCallPoints = false;
+ mFlags = 0;
}
-uint64_t SurfaceComposerClient::Transaction::getId() const {
- return mState.mId;
+uint64_t SurfaceComposerClient::Transaction::getId() {
+ return mId;
}
std::vector<uint64_t> SurfaceComposerClient::Transaction::getMergedTransactionIds() {
- return mState.mMergedTransactionIds;
+ return mMergedTransactionIds;
}
void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
@@ -995,13 +1173,12 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
client_cache_t uncacheBuffer;
uncacheBuffer.token = BufferCache::getInstance().getToken();
uncacheBuffer.id = cacheId;
- TransactionState state;
- state.mId = generateId();
- state.mApplyToken = Transaction::getDefaultApplyToken();
- state.mUncacheBuffers.emplace_back(std::move(uncacheBuffer));
- state.mFlags = ISurfaceComposer::eOneWay;
- state.mDesiredPresentTime = systemTime();
- status_t status = sf->setTransactionState(std::move(state));
+ Vector<ComposerState> composerStates;
+ Vector<DisplayState> displayStates;
+ status_t status = sf->setTransactionState(FrameTimelineInfo{}, composerStates, displayStates,
+ ISurfaceComposer::eOneWay,
+ Transaction::getDefaultApplyToken(), {}, systemTime(),
+ true, {uncacheBuffer}, false, {}, generateId(), {});
if (status != NO_ERROR) {
ALOGE_AND_TRACE("SurfaceComposerClient::doUncacheBufferTransaction - %s",
strerror(-status));
@@ -1009,12 +1186,12 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) {
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
- if (!mState.mMayContainBuffer) {
+ if (!mMayContainBuffer) {
return;
}
size_t count = 0;
- for (auto& cs : mState.mComposerStates) {
+ for (auto& cs : mComposerStates) {
layer_state_t* s = &cs.state;
if (!(s->what & layer_state_t::eBufferChanged)) {
continue;
@@ -1042,7 +1219,7 @@ void SurfaceComposerClient::Transaction::cacheBuffers() {
std::optional<client_cache_t> uncacheBuffer;
cacheId = BufferCache::getInstance().cache(s->bufferData->buffer, uncacheBuffer);
if (uncacheBuffer) {
- mState.mUncacheBuffers.emplace_back(*uncacheBuffer);
+ mUncacheBuffers.push_back(*uncacheBuffer);
}
}
s->bufferData->flags |= BufferData::BufferDataChange::cachedBufferChanged;
@@ -1111,7 +1288,8 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
/*callbackContext=*/nullptr);
}
- mState.mHasListenerCallbacks = !mListenerCallbacks.empty();
+ bool hasListenerCallbacks = !mListenerCallbacks.empty();
+ std::vector<ListenerCallbacks> listenerCallbacks;
// For every listener with registered callbacks
for (const auto& [listener, callbackInfo] : mListenerCallbacks) {
auto& [callbackIds, surfaceControls] = callbackInfo;
@@ -1120,8 +1298,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
}
if (surfaceControls.empty()) {
- mState.mListenerCallbacks.emplace_back(IInterface::asBinder(listener),
- std::move(callbackIds));
+ listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds));
} else {
// If the listener has any SurfaceControls set on this Transaction update the surface
// state
@@ -1133,7 +1310,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
}
std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end());
s->what |= layer_state_t::eHasListenerCallbacksChanged;
- s->listeners.emplace_back(IInterface::asBinder(listener), std::move(callbacks));
+ s->listeners.emplace_back(IInterface::asBinder(listener), callbacks);
}
}
}
@@ -1145,21 +1322,25 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
ALOGE("Transaction attempted to set synchronous and one way at the same time"
" this is an invalid request. Synchronous will win for safety");
} else {
- mState.mFlags |= ISurfaceComposer::eOneWay;
+ mFlags |= ISurfaceComposer::eOneWay;
}
}
// If both ISurfaceComposer::eEarlyWakeupStart and ISurfaceComposer::eEarlyWakeupEnd are set
// it is equivalent for none
uint32_t wakeupFlags = ISurfaceComposer::eEarlyWakeupStart | ISurfaceComposer::eEarlyWakeupEnd;
- if ((mState.mFlags & wakeupFlags) == wakeupFlags) {
- mState.mFlags &= ~(wakeupFlags);
+ if ((mFlags & wakeupFlags) == wakeupFlags) {
+ mFlags &= ~(wakeupFlags);
}
- if (!mState.mApplyToken) mState.mApplyToken = getDefaultApplyToken();
+ sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken();
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- status_t binderStatus = sf->setTransactionState(std::move(mState));
- mState.mId = generateId();
+ status_t binderStatus =
+ sf->setTransactionState(mFrameTimelineInfo, mComposerStates, mDisplayStates, mFlags,
+ applyToken, mInputWindowCommands, mDesiredPresentTime,
+ mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks,
+ listenerCallbacks, mId, mMergedTransactionIds);
+ mId = generateId();
// Clear the current states and flags
clear();
@@ -1168,8 +1349,8 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay
syncCallback->wait();
}
- if (mState.mLogCallPoints) {
- ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", getId());
+ if (mLogCallPoints) {
+ ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId);
}
mStatus = NO_ERROR;
@@ -1204,7 +1385,7 @@ status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction
}
void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() {
- mState.mLogCallPoints = true;
+ mLogCallPoints = true;
}
// ---------------------------------------------------------------------------
@@ -1262,19 +1443,34 @@ std::optional<gui::StalledTransactionInfo> SurfaceComposerClient::getStalledTran
}
void SurfaceComposerClient::Transaction::setAnimationTransaction() {
- mState.mFlags |= ISurfaceComposer::eAnimation;
+ mFlags |= ISurfaceComposer::eAnimation;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupStart() {
- mState.mFlags |= ISurfaceComposer::eEarlyWakeupStart;
+ mFlags |= ISurfaceComposer::eEarlyWakeupStart;
}
void SurfaceComposerClient::Transaction::setEarlyWakeupEnd() {
- mState.mFlags |= ISurfaceComposer::eEarlyWakeupEnd;
+ mFlags |= ISurfaceComposer::eEarlyWakeupEnd;
}
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
- return mState.getLayerState(sc);
+ auto handle = sc->getLayerStateHandle();
+ if (auto it = std::find_if(mComposerStates.begin(), mComposerStates.end(),
+ [&handle](const auto& composerState) {
+ return composerState.state.surface == handle;
+ });
+ it != mComposerStates.end()) {
+ return &it->state;
+ }
+
+ // we don't have it, add an initialized layer_state to our list
+ ComposerState s;
+ s.state.surface = handle;
+ s.state.layerId = sc->getLayerId();
+ mComposerStates.add(s);
+
+ return &mComposerStates.editItemAt(mComposerStates.size() - 1).state;
}
void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback(
@@ -1650,8 +1846,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe
setReleaseBufferCallback(bufferData.get(), callback);
}
- if (mState.mIsAutoTimestamp) {
- mState.mDesiredPresentTime = systemTime();
+ if (mIsAutoTimestamp) {
+ mDesiredPresentTime = systemTime();
}
s->what |= layer_state_t::eBufferChanged;
s->bufferData = std::move(bufferData);
@@ -1669,7 +1865,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe
const std::vector<SurfaceControlStats>&) {},
nullptr);
- mState.mMayContainBuffer = true;
+ mMayContainBuffer = true;
return *this;
}
@@ -1845,8 +2041,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setSideb
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesiredPresentTime(
nsecs_t desiredPresentTime) {
- mState.mDesiredPresentTime = desiredPresentTime;
- mState.mIsAutoTimestamp = false;
+ mDesiredPresentTime = desiredPresentTime;
+ mIsAutoTimestamp = false;
return *this;
}
@@ -1935,14 +2131,14 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInput
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
const FocusRequest& request) {
- mState.mInputWindowCommands.addFocusRequest(request);
+ mInputWindowCommands.addFocusRequest(request);
return *this;
}
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addWindowInfosReportedListener(
sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) {
- mState.mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener);
+ mInputWindowCommands.addWindowInfosReportedListener(windowInfosReportedListener);
return *this;
}
@@ -2106,7 +2302,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFixed
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameTimelineInfo(
const FrameTimelineInfo& frameTimelineInfo) {
- mState.mergeFrameTimelineInfo(frameTimelineInfo);
+ mergeFrameTimelineInfo(mFrameTimelineInfo, frameTimelineInfo);
return *this;
}
@@ -2145,7 +2341,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrust
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken(
const sp<IBinder>& applyToken) {
- mState.mApplyToken = applyToken;
+ mApplyToken = applyToken;
return *this;
}
@@ -2273,7 +2469,17 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setConte
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
- return mState.getDisplayState(token);
+ if (auto it = std::find_if(mDisplayStates.begin(), mDisplayStates.end(),
+ [token](const auto& display) { return display.token == token; });
+ it != mDisplayStates.end()) {
+ return *it;
+ }
+
+ // If display state doesn't exist, add a new one.
+ DisplayState s;
+ s.token = token;
+ mDisplayStates.add(s);
+ return mDisplayStates.editItemAt(mDisplayStates.size() - 1);
}
status_t SurfaceComposerClient::Transaction::setDisplaySurface(const sp<IBinder>& token,
@@ -2326,6 +2532,20 @@ void SurfaceComposerClient::Transaction::setDisplaySize(const sp<IBinder>& token
s.what |= DisplayState::eDisplaySizeChanged;
}
+// copied from FrameTimelineInfo::merge()
+void SurfaceComposerClient::Transaction::mergeFrameTimelineInfo(FrameTimelineInfo& t,
+ const FrameTimelineInfo& other) {
+ // When merging vsync Ids we take the oldest valid one
+ if (t.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID &&
+ other.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
+ if (other.vsyncId > t.vsyncId) {
+ t = other;
+ }
+ } else if (t.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
+ t = other;
+ }
+}
+
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::setTrustedPresentationCallback(
const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
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/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index de553aef72..9a422fd808 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -65,7 +65,6 @@ struct DisplayState;
struct InputWindowCommands;
class HdrCapabilities;
class Rect;
-class TransactionState;
using gui::FrameTimelineInfo;
using gui::IDisplayEventConnection;
@@ -106,7 +105,13 @@ public:
};
/* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
- virtual status_t setTransactionState(TransactionState&& state) = 0;
+ virtual status_t setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffer,
+ bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) = 0;
};
// ----------------------------------------------------------------------------
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/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 15e3341ca2..4fda8deb9c 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -52,7 +52,6 @@
#include <gui/ITransactionCompletedListener.h>
#include <gui/LayerState.h>
#include <gui/SurfaceControl.h>
-#include <gui/TransactionState.h>
#include <gui/WindowInfosListenerReporter.h>
#include <math/vec3.h>
@@ -448,11 +447,56 @@ public:
static sp<IBinder> sApplyToken;
static std::mutex sApplyTokenMutex;
void releaseBufferIfOverwriting(const layer_state_t& state);
+ static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
// Tracks registered callbacks
sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr;
+ // Prints debug logs when enabled.
+ bool mLogCallPoints = false;
- TransactionState mState;
+ protected:
+ Vector<ComposerState> mComposerStates;
+ Vector<DisplayState> mDisplayStates;
+ std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
+ mListenerCallbacks;
+ std::vector<client_cache_t> mUncacheBuffers;
+
+ // We keep track of the last MAX_MERGE_HISTORY_LENGTH merged transaction ids.
+ // Ordered most recently merged to least recently merged.
+ static const size_t MAX_MERGE_HISTORY_LENGTH = 10u;
+ std::vector<uint64_t> mMergedTransactionIds;
+
+ uint64_t mId;
+ uint32_t mFlags = 0;
+
+ // Indicates that the Transaction may contain buffers that should be cached. The reason this
+ // is only a guess is that buffers can be removed before cache is called. This is only a
+ // hint that at some point a buffer was added to this transaction before apply was called.
+ bool mMayContainBuffer = false;
+
+ // mDesiredPresentTime is the time in nanoseconds that the client would like the transaction
+ // to be presented. When it is not possible to present at exactly that time, it will be
+ // presented after the time has passed.
+ //
+ // If the client didn't pass a desired presentation time, mDesiredPresentTime will be
+ // populated to the time setBuffer was called, and mIsAutoTimestamp will be set to true.
+ //
+ // Desired present times that are more than 1 second in the future may be ignored.
+ // When a desired present time has already passed, the transaction will be presented as soon
+ // as possible.
+ //
+ // Transactions from the same process are presented in the same order that they are applied.
+ // The desired present time does not affect this ordering.
+ int64_t mDesiredPresentTime = 0;
+ bool mIsAutoTimestamp = true;
+
+ // The vsync id provided by Choreographer.getVsyncId and the input event id
+ FrameTimelineInfo mFrameTimelineInfo;
+
+ // If not null, transactions will be queued up using this token otherwise a common token
+ // per process will be used.
+ sp<IBinder> mApplyToken = nullptr;
+ InputWindowCommands mInputWindowCommands;
int mStatus = NO_ERROR;
layer_state_t* getLayerState(const sp<SurfaceControl>& sc);
@@ -462,11 +506,6 @@ public:
void registerSurfaceControlForCallback(const sp<SurfaceControl>& sc);
void setReleaseBufferCallback(BufferData*, ReleaseBufferCallback);
- protected:
- // Accessed in tests.
- std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash>
- mListenerCallbacks;
-
public:
Transaction();
virtual ~Transaction() = default;
@@ -483,7 +522,7 @@ public:
// Returns the current id of the transaction.
// The id is updated every time the transaction is applied.
- uint64_t getId() const;
+ uint64_t getId();
std::vector<uint64_t> getMergedTransactionIds();
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/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..45cde7f10c 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
@@ -648,7 +648,16 @@ public:
mSupportsPresent = supportsPresent;
}
- status_t setTransactionState(TransactionState&&) override { return NO_ERROR; }
+ status_t setTransactionState(
+ const FrameTimelineInfo& /*frameTimelineInfo*/, Vector<ComposerState>& /*state*/,
+ Vector<DisplayState>& /*displays*/, uint32_t /*flags*/,
+ const sp<IBinder>& /*applyToken*/, InputWindowCommands /*inputWindowCommands*/,
+ int64_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
+ const std::vector<client_cache_t>& /*cachedBuffer*/, bool /*hasListenerCallbacks*/,
+ const std::vector<ListenerCallbacks>& /*listenerCallbacks*/, uint64_t /*transactionId*/,
+ const std::vector<uint64_t>& /*mergedTransactionIds*/) override {
+ return NO_ERROR;
+ }
protected:
IBinder* onAsBinder() override { return nullptr; }
@@ -2690,4 +2699,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/input_flags.aconfig b/libs/input/input_flags.aconfig
index 983bbdee6e..9e69b60f93 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/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp
index f262158f2c..6a577ff41f 100644
--- a/libs/renderengine/skia/filters/LutShader.cpp
+++ b/libs/renderengine/skia/filters/LutShader.cpp
@@ -24,7 +24,6 @@
#include <ui/ColorSpace.h>
#include "include/core/SkColorSpace.h"
-#include "src/core/SkColorFilterPriv.h"
using aidl::android::hardware::graphics::composer3::LutProperties;
@@ -116,7 +115,7 @@ static const SkString kShader = SkString(R"(
linear = mix(c0, c1, linear.b);
}
}
- return float4(linear, rgba.a);
+ return float4(fromLinearSrgb(linear), rgba.a);
})");
// same as shader::toColorSpace function
@@ -289,9 +288,7 @@ sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input,
lutProperties[i].samplingKey, srcDataspace);
}
- auto colorXformLutToDst =
- SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace);
- input = input->makeWithColorFilter(colorXformLutToDst);
+ input = input->makeWithWorkingColorSpace(outColorSpace);
}
return input;
}
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 5491ab78b4..2908c61182 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -934,6 +934,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
mPendingEvent(nullptr),
mLastDropReason(DropReason::NOT_DROPPED),
mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
+ mWindowInfosVsyncId(-1),
mMinTimeBetweenUserActivityPokes(DEFAULT_USER_ACTIVITY_POKE_INTERVAL),
mConnectionManager(mLooper),
mTouchStates(mWindowInfos, mConnectionManager),
@@ -951,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);
@@ -1960,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
@@ -2446,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) {
@@ -2486,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();
@@ -2513,7 +2596,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets(
tempTouchState.addOrUpdateWindow(wallpaper,
InputTarget::DispatchMode::AS_IS,
wallpaperFlags, entry.deviceId, {pointer},
- entry.eventTime);
+ entry.eventTime,
+ /*forwardingWindowToken=*/nullptr);
}
}
}
@@ -2612,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,
@@ -4345,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) {
@@ -5769,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;
@@ -5783,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;
}
@@ -5827,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()) {
@@ -5870,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);
@@ -5953,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) {
@@ -7131,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);
}
}
@@ -7172,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/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/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/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/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/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/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 48d02423f8..34c09db6f8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -62,10 +62,15 @@ struct CompositionEngineTest : public testing::Test {
void SetUp() override {
EXPECT_CALL(*mOutput1, getDisplayId)
.WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId1)));
+ EXPECT_CALL(*mOutput1, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId1));
+
EXPECT_CALL(*mOutput2, getDisplayId)
.WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId2)));
+ EXPECT_CALL(*mOutput2, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId2));
+
EXPECT_CALL(*mOutput3, getDisplayId)
.WillRepeatedly(Return(std::make_optional<DisplayId>(kDisplayId3)));
+ EXPECT_CALL(*mOutput3, getDisplayIdVariant).WillRepeatedly(Return(kDisplayId3));
// Most tests will depend on the outputs being enabled.
for (auto& state : mOutputStates) {
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/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index d547af98ea..27ae18fac9 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
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 018ee6e461..ab086e4b55 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;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 14088a6428..787a64b089 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;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index a010353423..3321f51026 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);
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/LayerVector.h b/services/surfaceflinger/LayerVector.h
index 38dc11d3bc..81155fd697 100644
--- a/services/surfaceflinger/LayerVector.h
+++ b/services/surfaceflinger/LayerVector.h
@@ -49,7 +49,8 @@ public:
using Visitor = std::function<void(Layer*)>;
private:
- const StateSet mStateSet;
+ // FIXME: This is set but not used anywhere.
+ [[maybe_unused]] const StateSet mStateSet;
};
}
diff --git a/services/surfaceflinger/PowerAdvisor/SessionManager.h b/services/surfaceflinger/PowerAdvisor/SessionManager.h
index 93a80b55ab..afa52eb260 100644
--- a/services/surfaceflinger/PowerAdvisor/SessionManager.h
+++ b/services/surfaceflinger/PowerAdvisor/SessionManager.h
@@ -68,7 +68,8 @@ private:
bool isLayerRelevant(int32_t layerId);
// The UID of whoever created our ISessionManager connection
- const uid_t mUid;
+ // FIXME: This is set but is not used anywhere.
+ [[maybe_unused]] const uid_t mUid;
// State owned by the main thread
@@ -99,4 +100,4 @@ private:
};
} // namespace adpf
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/QueuedTransactionState.h b/services/surfaceflinger/QueuedTransactionState.h
index 6a17a0d0cb..86683da26c 100644
--- a/services/surfaceflinger/QueuedTransactionState.h
+++ b/services/surfaceflinger/QueuedTransactionState.h
@@ -25,7 +25,6 @@
#include <common/FlagManager.h>
#include <ftl/flags.h>
#include <gui/LayerState.h>
-#include <gui/TransactionState.h>
#include <system/window.h>
namespace android {
@@ -51,26 +50,33 @@ public:
struct QueuedTransactionState {
QueuedTransactionState() = default;
- QueuedTransactionState(TransactionState&& transactionState,
- std::vector<ResolvedComposerState>&& composerStates,
- std::vector<uint64_t>&& uncacheBufferIds, int64_t postTime,
- int originPid, int originUid)
- : frameTimelineInfo(std::move(transactionState.mFrameTimelineInfo)),
- states(composerStates),
- displays(std::move(transactionState.mDisplayStates)),
- flags(transactionState.mFlags),
- applyToken(transactionState.mApplyToken),
- inputWindowCommands(std::move(transactionState.mInputWindowCommands)),
- desiredPresentTime(transactionState.mDesiredPresentTime),
- isAutoTimestamp(transactionState.mIsAutoTimestamp),
+ QueuedTransactionState(const FrameTimelineInfo& frameTimelineInfo,
+ std::vector<ResolvedComposerState>& composerStates,
+ const Vector<DisplayState>& displayStates, uint32_t transactionFlags,
+ const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands,
+ int64_t desiredPresentTime, bool isAutoTimestamp,
+ std::vector<uint64_t> uncacheBufferIds, int64_t postTime,
+ bool hasListenerCallbacks,
+ std::vector<ListenerCallbacks> listenerCallbacks, int originPid,
+ int originUid, uint64_t transactionId,
+ std::vector<uint64_t> mergedTransactionIds)
+ : frameTimelineInfo(frameTimelineInfo),
+ states(std::move(composerStates)),
+ displays(displayStates),
+ flags(transactionFlags),
+ applyToken(applyToken),
+ inputWindowCommands(inputWindowCommands),
+ desiredPresentTime(desiredPresentTime),
+ isAutoTimestamp(isAutoTimestamp),
uncacheBufferIds(std::move(uncacheBufferIds)),
postTime(postTime),
- hasListenerCallbacks(transactionState.mHasListenerCallbacks),
- listenerCallbacks(std::move(transactionState.mListenerCallbacks)),
+ hasListenerCallbacks(hasListenerCallbacks),
+ listenerCallbacks(listenerCallbacks),
originPid(originPid),
originUid(originUid),
- id(transactionState.getId()),
- mergedTransactionIds(std::move(transactionState.mMergedTransactionIds)) {}
+ id(transactionId),
+ mergedTransactionIds(std::move(mergedTransactionIds)) {}
// Invokes `void(const layer_state_t&)` visitor for matching layers.
template <typename Visitor>
@@ -129,7 +135,7 @@ struct QueuedTransactionState {
FrameTimelineInfo frameTimelineInfo;
std::vector<ResolvedComposerState> states;
- std::vector<DisplayState> displays;
+ Vector<DisplayState> displays;
uint32_t flags;
sp<IBinder> applyToken;
InputWindowCommands inputWindowCommands;
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 940374b256..2fb3500798 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));
}
}
@@ -596,15 +593,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 +759,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 +778,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 +898,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 +1122,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 +1226,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 +2206,6 @@ status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken
}
hdrInfoReporter->addListener(listener);
-
mAddingHDRLayerInfoListener = true;
return OK;
}
@@ -2273,13 +2266,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);
}
@@ -2809,9 +2802,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);
@@ -3060,7 +3054,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 +3066,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;
@@ -3315,40 +3308,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 +3326,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());
@@ -4354,37 +4318,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 +4656,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,
@@ -5028,7 +4990,13 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const layer_state_t& state, size_t nu
return true;
}
-status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState) {
+status_t SurfaceFlinger::setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ InputWindowCommands inputWindowCommands, int64_t desiredPresentTime, bool isAutoTimestamp,
+ const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
+ const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
+ const std::vector<uint64_t>& mergedTransactionIds) {
SFTRACE_CALL();
IPCThreadState* ipc = IPCThreadState::self();
@@ -5036,7 +5004,7 @@ status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState
const int originUid = ipc->getCallingUid();
uint32_t permissions = LayerStatePermissions::getTransactionPermissions(originPid, originUid);
ftl::Flags<adpf::Workload> queuedWorkload;
- for (auto& composerState : transactionState.mComposerStates) {
+ for (auto& composerState : states) {
composerState.state.sanitize(permissions);
if (composerState.state.what & layer_state_t::COMPOSITION_EFFECTS) {
queuedWorkload |= adpf::Workload::EFFECTS;
@@ -5046,27 +5014,27 @@ status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState
}
}
- for (DisplayState& display : transactionState.mDisplayStates) {
+ for (DisplayState& display : displays) {
display.sanitize(permissions);
}
- if (!transactionState.mInputWindowCommands.empty() &&
+ if (!inputWindowCommands.empty() &&
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) == 0) {
ALOGE("Only privileged callers are allowed to send input commands.");
- transactionState.mInputWindowCommands.clear();
+ inputWindowCommands.clear();
}
- if (transactionState.mFlags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
+ if (flags & (eEarlyWakeupStart | eEarlyWakeupEnd)) {
const bool hasPermission =
(permissions & layer_state_t::Permission::ACCESS_SURFACE_FLINGER) ||
callingThreadHasPermission(sWakeupSurfaceFlinger);
if (!hasPermission) {
ALOGE("Caller needs permission android.permission.WAKEUP_SURFACE_FLINGER to use "
"eEarlyWakeup[Start|End] flags");
- transactionState.mFlags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
+ flags &= ~(eEarlyWakeupStart | eEarlyWakeupEnd);
}
}
- if (transactionState.mFlags & eEarlyWakeupStart) {
+ if (flags & eEarlyWakeupStart) {
queuedWorkload |= adpf::Workload::WAKEUP;
}
mPowerAdvisor->setQueuedWorkload(queuedWorkload);
@@ -5074,8 +5042,8 @@ status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState
const int64_t postTime = systemTime();
std::vector<uint64_t> uncacheBufferIds;
- uncacheBufferIds.reserve(transactionState.mUncacheBuffers.size());
- for (const auto& uncacheBuffer : transactionState.mUncacheBuffers) {
+ uncacheBufferIds.reserve(uncacheBuffers.size());
+ for (const auto& uncacheBuffer : uncacheBuffers) {
sp<GraphicBuffer> buffer = ClientCache::getInstance().erase(uncacheBuffer);
if (buffer != nullptr) {
uncacheBufferIds.push_back(buffer->getId());
@@ -5083,19 +5051,19 @@ status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState
}
std::vector<ResolvedComposerState> resolvedStates;
- resolvedStates.reserve(transactionState.mComposerStates.size());
- for (auto& state : transactionState.mComposerStates) {
+ resolvedStates.reserve(states.size());
+ for (auto& state : states) {
resolvedStates.emplace_back(std::move(state));
auto& resolvedState = resolvedStates.back();
resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
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());
+ layerName.c_str(), transactionId);
if (resolvedState.externalTexture) {
resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer();
if (FlagManager::getInstance().monitor_buffer_fences()) {
@@ -5123,12 +5091,22 @@ status_t SurfaceFlinger::setTransactionState(TransactionState&& transactionState
}
}
- QueuedTransactionState state{std::move(transactionState),
- std::move(resolvedStates),
+ QueuedTransactionState state{frameTimelineInfo,
+ resolvedStates,
+ displays,
+ flags,
+ applyToken,
+ std::move(inputWindowCommands),
+ desiredPresentTime,
+ isAutoTimestamp,
std::move(uncacheBufferIds),
postTime,
+ hasListenerCallbacks,
+ listenerCallbacks,
originPid,
- originUid};
+ originUid,
+ transactionId,
+ mergedTransactionIds};
state.workloadHint = queuedWorkload;
if (mTransactionTracing) {
@@ -5151,16 +5129,16 @@ 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{frameTimelineInfo.vsyncId});
}
}
- setTransactionFlags(eTransactionFlushNeeded, schedule, state.applyToken, frameHint);
+ setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
return NO_ERROR;
}
bool SurfaceFlinger::applyTransactionState(
const FrameTimelineInfo& frameTimelineInfo, std::vector<ResolvedComposerState>& states,
- std::span<DisplayState> displays, uint32_t flags,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime,
bool isAutoTimestamp, const std::vector<uint64_t>& uncacheBufferIds, const int64_t postTime,
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
@@ -5650,8 +5628,7 @@ void SurfaceFlinger::initializeDisplays() {
auto layerStack = ui::DEFAULT_LAYER_STACK.id;
for (const auto& [id, display] : FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays)) {
- state.displays.emplace_back(
- DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++)));
+ state.displays.push(DisplayState(display.token(), ui::LayerStack::fromValue(layerStack++)));
}
std::vector<QueuedTransactionState> transactions;
@@ -5928,8 +5905,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());
@@ -6114,9 +6090,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);
}
}
}
@@ -6128,6 +6103,7 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
if (!displayId) {
continue;
}
+
const auto hwcDisplayId = getHwComposer().fromPhysicalDisplayId(*displayId);
if (!hwcDisplayId) {
continue;
@@ -6136,6 +6112,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)) {
@@ -6163,6 +6140,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,
@@ -6373,7 +6363,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.
@@ -6553,7 +6543,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;
@@ -6647,9 +6637,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;
}
@@ -6669,11 +6659,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;
@@ -6744,7 +6735,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();
}
@@ -7191,6 +7182,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;
@@ -7264,9 +7283,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;
@@ -8377,8 +8394,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);
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9cf0c6aaa0..1a09269545 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -545,7 +545,13 @@ private:
}
sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const;
- status_t setTransactionState(TransactionState&&) override;
+ status_t setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ InputWindowCommands inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) override;
void bootFinished();
status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const;
sp<IDisplayEventConnection> createDisplayEventConnection(
@@ -792,7 +798,7 @@ private:
*/
bool applyTransactionState(const FrameTimelineInfo& info,
std::vector<ResolvedComposerState>& state,
- std::span<DisplayState> displays, uint32_t flags,
+ Vector<DisplayState>& displays, uint32_t flags,
const InputWindowCommands& inputWindowCommands,
const int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<uint64_t>& uncacheBufferIds,
@@ -1458,8 +1464,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/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 6bbc04cf6f..3297c16113 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -321,7 +321,7 @@ QueuedTransactionState TransactionProtoParser::fromProto(
int32_t displayCount = proto.display_changes_size();
t.displays.reserve(static_cast<size_t>(displayCount));
for (int i = 0; i < displayCount; i++) {
- t.displays.emplace_back(fromProto(proto.display_changes(i)));
+ t.displays.add(fromProto(proto.display_changes(i)));
}
return t;
}
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/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_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
index 9c143fdd41..6cc6322bfc 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp
@@ -45,15 +45,28 @@ protected:
void setTransactionState() {
ASSERT_TRUE(mFlinger.getTransactionQueue().isEmpty());
TransactionInfo transaction;
- mFlinger.setTransactionState(std::move(transaction));
+ mFlinger.setTransactionState(FrameTimelineInfo{}, transaction.states, transaction.displays,
+ transaction.flags, transaction.applyToken,
+ transaction.inputWindowCommands,
+ TimePoint::now().ns() + s2ns(1), transaction.isAutoTimestamp,
+ transaction.unCachedBuffers,
+ /*HasListenerCallbacks=*/false, transaction.callbacks,
+ transaction.id, transaction.mergedTransactionIds);
}
- struct TransactionInfo : public TransactionState {
- TransactionInfo() {
- mApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- mIsAutoTimestamp = false;
- mId = static_cast<uint64_t>(-1);
- }
+ struct TransactionInfo {
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags = 0;
+ sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ InputWindowCommands inputWindowCommands;
+ int64_t desiredPresentTime = 0;
+ bool isAutoTimestamp = false;
+ FrameTimelineInfo frameTimelineInfo{};
+ std::vector<client_cache_t> unCachedBuffers;
+ uint64_t id = static_cast<uint64_t>(-1);
+ std::vector<uint64_t> mergedTransactionIds;
+ std::vector<ListenerCallbacks> callbacks;
};
struct Compositor final : ICompositor {
@@ -370,4 +383,4 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) {
}
}
}
-} // namespace android
+} // namespace android \ No newline at end of file
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 13c32bdf08..c5973db109 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -519,8 +519,18 @@ public:
return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
}
- auto setTransactionState(TransactionState&& state) {
- return mFlinger->setTransactionState(std::move(state));
+ auto setTransactionState(
+ const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
+ Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
+ const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const std::vector<client_cache_t>& uncacheBuffers,
+ bool hasListenerCallbacks, std::vector<ListenerCallbacks>& listenerCallbacks,
+ uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) {
+ return mFlinger->setTransactionState(frameTimelineInfo, states, displays, flags, applyToken,
+ inputWindowCommands, desiredPresentTime,
+ isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
+ listenerCallbacks, transactionId,
+ mergedTransactionIds);
}
auto setTransactionStateInternal(QueuedTransactionState& transaction) {
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 6a5ac2a70e..69dfcc4a8f 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -17,8 +17,6 @@
#undef LOG_TAG
#define LOG_TAG "TransactionApplicationTest"
-#include <cstdint>
-
#include <binder/Binder.h>
#include <common/test/FlagUtils.h>
#include <compositionengine/Display.h>
@@ -71,32 +69,38 @@ public:
TestableSurfaceFlinger mFlinger;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
- struct TransactionInfo : public TransactionState {
- TransactionInfo() {
- mApplyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
- mId = static_cast<uint64_t>(-1);
- }
+ struct TransactionInfo {
+ Vector<ComposerState> states;
+ Vector<DisplayState> displays;
+ uint32_t flags = 0;
+ sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ InputWindowCommands inputWindowCommands;
+ int64_t desiredPresentTime = 0;
+ bool isAutoTimestamp = true;
+ FrameTimelineInfo frameTimelineInfo;
+ std::vector<client_cache_t> uncacheBuffers;
+ uint64_t id = static_cast<uint64_t>(-1);
+ std::vector<uint64_t> mergedTransactionIds;
+ static_assert(0xffffffffffffffff == static_cast<uint64_t>(-1));
};
void checkEqual(TransactionInfo info, QueuedTransactionState state) {
- EXPECT_EQ(0u, info.mComposerStates.size());
+ EXPECT_EQ(0u, info.states.size());
EXPECT_EQ(0u, state.states.size());
- EXPECT_EQ(0u, info.mDisplayStates.size());
+ EXPECT_EQ(0u, info.displays.size());
EXPECT_EQ(0u, state.displays.size());
- EXPECT_EQ(info.mFlags, state.flags);
- EXPECT_EQ(info.mDesiredPresentTime, state.desiredPresentTime);
+ EXPECT_EQ(info.flags, state.flags);
+ EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime);
}
void setupSingle(TransactionInfo& transaction, uint32_t flags, int64_t desiredPresentTime,
bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo) {
mTransactionNumber++;
- transaction.mFlags |= flags;
- transaction.mDesiredPresentTime = desiredPresentTime;
- transaction.mIsAutoTimestamp = isAutoTimestamp;
- transaction.mFrameTimelineInfo = frameTimelineInfo;
- transaction.mHasListenerCallbacks = mHasListenerCallbacks;
- transaction.mListenerCallbacks = mCallbacks;
+ transaction.flags |= flags;
+ transaction.desiredPresentTime = desiredPresentTime;
+ transaction.isAutoTimestamp = isAutoTimestamp;
+ transaction.frameTimelineInfo = frameTimelineInfo;
}
void NotPlacedOnTransactionQueue(uint32_t flags) {
@@ -107,7 +111,12 @@ public:
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
FrameTimelineInfo{});
nsecs_t applicationTime = systemTime();
- mFlinger.setTransactionState(std::move(transaction));
+ mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.applyToken, transaction.inputWindowCommands,
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
+ transaction.id, transaction.mergedTransactionIds);
// If transaction is synchronous, SF applyTransactionState should time out (5s) wating for
// SF to commit the transaction. If this is animation, it should not time out waiting.
@@ -129,7 +138,12 @@ public:
setupSingle(transaction, flags, /*desiredPresentTime*/ time + s2ns(1), false,
FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(std::move(transaction));
+ mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.applyToken, transaction.inputWindowCommands,
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
+ transaction.id, transaction.mergedTransactionIds);
nsecs_t returnedTime = systemTime();
EXPECT_LE(returnedTime, applicationSentTime + TRANSACTION_TIMEOUT);
@@ -155,7 +169,12 @@ public:
/*isAutoTimestamp*/ true, FrameTimelineInfo{});
nsecs_t applicationSentTime = systemTime();
- mFlinger.setTransactionState(std::move(transactionA));
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags,
+ transactionA.applyToken, transactionA.inputWindowCommands,
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
+ transactionA.id, transactionA.mergedTransactionIds);
// This thread should not have been blocked by the above transaction
// (5s is the timeout period that applyTransactionState waits for SF to
@@ -165,7 +184,12 @@ public:
mFlinger.flushTransactionQueues();
applicationSentTime = systemTime();
- mFlinger.setTransactionState(std::move(transactionB));
+ mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+ transactionB.displays, transactionB.flags,
+ transactionB.applyToken, transactionB.inputWindowCommands,
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffers, mHasListenerCallbacks, mCallbacks,
+ transactionB.id, transactionB.mergedTransactionIds);
// this thread should have been blocked by the above transaction
// if this is an animation, this thread should be blocked for 5s
@@ -198,7 +222,12 @@ TEST_F(TransactionApplicationTest, AddToPendingQueue) {
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
FrameTimelineInfo{});
- mFlinger.setTransactionState(std::move(transactionA));
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags, transactionA.applyToken,
+ transactionA.inputWindowCommands, transactionA.desiredPresentTime,
+ transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
+ mHasListenerCallbacks, mCallbacks, transactionA.id,
+ transactionA.mergedTransactionIds);
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -214,7 +243,12 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*desiredPresentTime*/ s2ns(1), false,
FrameTimelineInfo{});
- mFlinger.setTransactionState(std::move(transactionA));
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags, transactionA.applyToken,
+ transactionA.inputWindowCommands, transactionA.desiredPresentTime,
+ transactionA.isAutoTimestamp, transactionA.uncacheBuffers,
+ mHasListenerCallbacks, mCallbacks, transactionA.id,
+ transactionA.mergedTransactionIds);
auto& transactionQueue = mFlinger.getTransactionQueue();
ASSERT_FALSE(transactionQueue.isEmpty());
@@ -223,10 +257,12 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
// transaction here (sending a null applyToken to fake it as from a
// different process) to re-query and reset the cached expected present time
TransactionInfo empty;
- empty.mApplyToken = sp<IBinder>();
- empty.mHasListenerCallbacks = mHasListenerCallbacks;
- empty.mListenerCallbacks = mCallbacks;
- mFlinger.setTransactionState(std::move(empty));
+ empty.applyToken = sp<IBinder>();
+ mFlinger.setTransactionState(empty.frameTimelineInfo, empty.states, empty.displays, empty.flags,
+ empty.applyToken, empty.inputWindowCommands,
+ empty.desiredPresentTime, empty.isAutoTimestamp,
+ empty.uncacheBuffers, mHasListenerCallbacks, mCallbacks, empty.id,
+ empty.mergedTransactionIds);
// flush transaction queue should flush as desiredPresentTime has
// passed
@@ -370,9 +406,9 @@ public:
const auto kFrameTimelineInfo = FrameTimelineInfo{};
setupSingle(transaction, kFlags, kDesiredPresentTime, kIsAutoTimestamp, kFrameTimelineInfo);
- transaction.mApplyToken = applyToken;
+ transaction.applyToken = applyToken;
for (const auto& state : states) {
- transaction.mComposerStates.push_back(state);
+ transaction.states.push_back(state);
}
return transaction;
@@ -384,7 +420,7 @@ public:
EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
std::unordered_set<uint32_t> createdLayers;
for (auto transaction : transactions) {
- for (auto& state : transaction.mComposerStates) {
+ for (auto& state : transaction.states) {
auto layerId = static_cast<uint32_t>(state.state.layerId);
if (createdLayers.find(layerId) == createdLayers.end()) {
mFlinger.addLayer(layerId);
@@ -398,8 +434,8 @@ public:
for (auto transaction : transactions) {
std::vector<ResolvedComposerState> resolvedStates;
- resolvedStates.reserve(transaction.mComposerStates.size());
- for (auto& state : transaction.mComposerStates) {
+ resolvedStates.reserve(transaction.states.size());
+ for (auto& state : transaction.states) {
ResolvedComposerState resolvedState;
resolvedState.state = std::move(state.state);
resolvedState.externalTexture =
@@ -410,9 +446,15 @@ public:
resolvedStates.emplace_back(resolvedState);
}
- QueuedTransactionState transactionState(std::move(transaction),
- std::move(resolvedStates), {}, systemTime(),
- getpid(), static_cast<int>(getuid()));
+ QueuedTransactionState transactionState(transaction.frameTimelineInfo, resolvedStates,
+ transaction.displays, transaction.flags,
+ transaction.applyToken,
+ transaction.inputWindowCommands,
+ transaction.desiredPresentTime,
+ transaction.isAutoTimestamp, {}, systemTime(),
+ mHasListenerCallbacks, mCallbacks, getpid(),
+ static_cast<int>(getuid()), transaction.id,
+ transaction.mergedTransactionIds);
mFlinger.setTransactionStateInternal(transactionState);
}
mFlinger.flushTransactionQueues();
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index b36ad213c8..d3eec5c6f3 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -66,7 +66,7 @@ TEST(TransactionProtoParserTest, parse) {
display.token = nullptr;
}
display.width = 85;
- t1.displays.push_back(display);
+ t1.displays.add(display);
}
class TestMapper : public TransactionProtoParser::FlingerDataMapper {
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/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;